KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > luigidragone > net > ntlm > NTLM


1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15  */

16
17 /*
18  * NTLM.java
19  * Copyright (C) 2002 Luigi Dragone
20  *
21  */

22
23 package com.luigidragone.net.ntlm;
24
25 import java.security.*;
26 import java.security.spec.*;
27 import javax.crypto.*;
28 import javax.crypto.spec.*;
29 import java.io.*;
30
31 /**
32  * <p>
33  * This class implements the Microsoft NTLM authentication protocol.
34  * </p>
35  * <p>
36  * NTLM is a Microsoft proprietary network authentication protocol used in many
37  * situations (e.g. by the Microsoft Proxy Server to authenticate a browser).
38  * </p>
39  * <p>
40  * It requires a JCE compatible MD4 hash algorithm implementation and
41  * a DES with no-padding ECB cipher to compute the requested value. <br>
42  * An open source JCE compatible library is Cryptix JCE and it is available
43  * <a HREF="http://www.cryptix.org/">here</a>. We are assuming that the JCE provider
44  * is correctly installed and configured. Notice that the Sun JCE implementation
45  * proviedes the DES cipher but doesn't provide the MD4 hashing.
46  * </p>
47  * To perform an authentication the following information are needed:
48  * <ul>
49  * <li>the host name (with its own domain);</li>
50  * <li>the user name (with its own domain);</li>
51  * <li>the user password.</li>
52  * </ul>
53  * Alternatively, the user password can be replaced with its Lan Manager and
54  * NT hashed versions. On a Windows system these data can be collected in the
55  * registry, otherwise they can be extracted from a SAMBA password file.<br>
56  * Notice that the host and user domain could not be the same.
57  * </p>
58  * <p>
59  * To start an NTLM authentication procedure (e.g. with a proxy server) build a
60  * request message calling {@link #formatRequest(String, String) formatRequest}
61  * and send it to the server.<br>
62  * Once the challenge packet has been correctly received extract from it the nonce
63  * with {@link #getNonce(byte[]) getNonce} function and use it to compute the
64  * reply and build the response message with
65  * {@link #formatResponse(String, String, String, byte[], byte[], byte[]) formatResponse}
66  * method and send it back to the server.<br>
67  * Repeat the previous steps until the server authenticates the client's identity
68  * or a large number of retries has been made. The check of a successful authentication
69  * is protocol specific (e.g. code 200 in HTTP), thus it is not performed by this component.
70  * </p>
71  * <p>
72  * We want to access to the page <code>http://www.server.com/page.html</code>
73  * through an NTLM proxy <code>proxy.domain.com</code> that accepts connection on
74  * port 80.<br>
75  * We access to proxy from host <code>HOSTDOMAIN\\HOST<code> with the user
76  * <code>USERDOMAIN\\user</code> (password <code>"1234567890"</code>).<br>
77  * As first step we open a socket connection to proxy server and set up the
78  * required object. Notice that we use a keep-alive connection, because NTLM
79  * authentication is connection based and the connection must be alive through the
80  * whole process.<br>
81  * <pre>
82  * Socket s = new Socket("proxy.domain.com", 80);
83  * s.setKeepAlive(true);
84  * InputStream is = s.getInputStream();
85  * OutputStream os = s.getOutputStream();
86  * BufferedReader r = new BufferedReader(new InputStreamReader(is));
87  * BufferedWriter w = new BufferedWriter(new OutputStreamWriter(os));
88  *
89  * String host = "HOST";
90  * String hostDomain = "HOSTDOMAIN";
91  * String user = "user";
92  * String userDomain = "USERDOMAIN";
93  * String password = "1234567890";
94  * </pre>
95  * Then, we format a request message and send it in a HTTP compliant GET message.<br>
96  * <pre>
97  * byte[] fstMsg = NTLM.formatRequest(host, hostDomain);
98  * byte[] fstMsg64 = Codecs.base64Encode(fstMsg);
99  * System.out.println("NTLM Request Packet: " + new String(fstMsg64));
100  *
101  * w.write("GET http://www.server.com/page.html HTTP/1.0\n");
102  * w.write("Host: www.server.com\n");
103  * w.write("Proxy-Connection: Keep-Alive\n");
104  * w.write("Proxy-Authorization: NTLM " + new String(fstMsg64) + "\n\n");
105  * w.flush();
106  * System.out.println("First Message Sent");
107  * </pre>
108  * We wait for the server response and we parse it to extract the nonce.<br>
109  * <pre>
110  * String resp = "";
111  * int contentLength = 0;
112  * while((line = r.readLine()) != null)
113  * if(line.length() == 0)
114  * break;
115  * if(line.startsWith("Content-Length"))
116  * contentLength = Integer.parseInt(line.substring(line.indexOf(":") + 1).trim());
117  * else if(line.startsWith("Proxy-Authenticate"))
118  * resp = line.substring(line.indexOf(":") + 1).trim();
119  * r.skip(contentLength);
120  * System.out.println("Second Message Received");
121  * System.out.println("Content Length: " + contentLength);
122  * System.out.println("Proxy-Authenticate: " + resp);
123  * resp = resp.substring(resp.indexOf(" ")).trim();
124  * System.out.println("NTLM Chellange Packet: " + resp);
125  * resp = Codecs.base64Decode(resp);
126  * byte[] sndMsg = resp.getBytes();
127  * byte[] nonce = NTLM.getNonce(sndMsg);
128  * </pre>
129  * With the nonce collected in the previous step we create a response message.<br>
130  * <pre>
131  * byte[] trdMsg = NTLM.formatResponse(host, user, userDomain,
132  * NTLM.computeLMPassword(password), NTLM.computeNTPassword(password),
133  * nonce);
134  * System.out.println(trdMsg.length);
135  * byte[] trdMsg64 = Codecs.base64Encode(trdMsg);
136  * System.out.println("NTLM Response Packet: " + new String(trdMsg64));
137  * </pre>
138  * We sent the message to the server.<br>
139  * <pre>
140  * w.write("GET http://www.server.com/page.html HTTP/1.0\n");
141  * w.write("Proxy-Connection: Keep-Alive\n");
142  * w.write("Host: www.server.com\n");
143  * w.write("Proxy-Authorization: NTLM " + new String(trdMsg64) + "\n\n");
144  * w.flush();
145  * System.out.println("Third Message Sent");
146  * </pre>
147  * Finally we wait the server reply.<br>
148  * <pre>
149  * System.out.println("Server response: " + r.readLine());
150  * </pre>
151  * If the reply is like <code>"HTTP/1.0 200 OK"</code> it has worked fine, else
152  * the server response is containing a new nonce.
153  * </p>
154  * <p>
155  * Notice that despite the computing of hashed passwords and of nonce response is
156  * exactly the same of the SMB authentication protocol, the message format is slightly
157  * different. <br>
158  * Therefore, the methods {@link #computeLMPassword(String) computeLMPassword},
159  * {@link #computeNTPassword(String) computeNTPassword} and
160  * {@link #computeNTLMResponse(byte[], byte[], byte[], byte[], byte[]) computeNTLMResponse}
161  * can be used to perform a SMB authentication too.
162  * </p>
163  * <p>
164  * This implementation is based on:
165  * <ul>
166  * <li>the reverse engineering of the NTLM protocol made by Ronald Tschal&auml;r
167  * and available <a HREF="http://www.innovation.ch/java/ntlm.html">here</a>;</li>
168  * <li>the documentation about NTLM provided with <a HREF="http://www.atstake.com/research/lc3/">
169  * L0phtCrack 1.5</a>;</li>
170  * <li>the &quot;Handbook of Applied Cryptography&quot;, freely available
171  * <a HREF="http://www.cacr.math.uwaterloo.ca/hac/">here</a>;</li>
172  * <li>the C source code of NTLM library in the <a HREF="http://www.samba.org/">
173  * SAMBA Project</a>.</li>
174  * </ul>
175  * Nevertheless, because there isn't any official protocol specification publicly
176  * available there is any warranty that code works correctly and that it is
177  * conforming to Microsoft NTLM protocol.
178  * </p>
179  * <p>
180  * For implementation reasons only the public members perform argument consistency
181  * checks. The public members also catch and hide every exceptions that can be
182  * throwed (even though interfaces specify otherwise).
183  * </p>
184  *
185  * @author Luigi Dragone (<a HREF="mailto:luigi@luigidragone.com">luigi@luigidragone.com</a>)
186  *
187  * @version 1.0.1
188  *
189  * @see <a HREF="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
190  * @see <a HREF="ftp://ftp.samba.org/pub/samba/docs/textdocs/ENCRYPTION.txt">LanMan and NT Password Encryption in Samba 2.x</a>
191  * @see <a HREF="http://www.cacr.math.uwaterloo.ca/hac/">&quot;Handbook of Applied Cryptography&quot;</a>
192  * @see <a HREF="http://java.sun.com/products/jce/">JCE</a>
193  * @see <a HREF="http://www.cryptix.org/">Cryptix</a>
194  */

195 public class NTLM {
196   protected NTLM() {}
197   /**
198    * The magic number used to compute the Lan Manager hashed password.
199    */

200   protected static final byte[] MAGIC = new byte[] {0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
201   /**
202    * <p>
203    * Converts an unsigned byte to an unsigned integer.
204    * </p>
205    * <p>
206    * Notice that Java bytes are always signed, but the cryptographic algorithms
207    * rely on unsigned ones, that can be simulated in this way.<br>
208    * A bit mask is employed to prevent that the signum bit is extended to MSBs.
209    * </p>
210    */

211   protected static int unsignedByteToInt(byte b) {
212     return (int)b & 0xFF;
213   }
214   protected static byte getLoByte(char c) {
215     return (byte)c;
216   }
217   protected static byte getHiByte(char c) {
218     return (byte)((c >>> 8) & 0xFF);
219   }
220   protected static short swapBytes(short s) {
221     return (short)(((s << 8) & 0xFF00) | ((s >>> 8) & 0x00FF));
222   }
223   /**
224    * <p>
225    * Computes an odd DES key from 56 bits represented as a 7-bytes array.
226    * </p>
227    * <p>
228    * Keeps elements from index <code>offset</code> to index <code>offset + 7</code> of
229    * supplied array.
230    * </p>
231    *
232    * @param keyData a byte array containing the 56 bits used to compute the DES key
233    * @param offset the offset of the first element of the 56-bits key data
234    *
235    * @return the odd DES key generated
236    *
237    * @exception InvalidKeyException
238    * @exception NoSuchAlgorithmException
239    * @exception InvalidKeySpecException
240    */

241   protected static Key computeDESKey(byte[] keyData, int offset) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
242     byte[] desKeyData = new byte[8];
243     int[] k = new int[7];
244
245     for(int i = 0; i < 7; i++)
246       k[i] = unsignedByteToInt(keyData[offset + i]);
247
248     desKeyData[0] = (byte)(k[0] >>> 1);
249     desKeyData[1] = (byte)(((k[0] & 0x01) << 6) | (k[1] >>> 2));
250     desKeyData[2] = (byte)(((k[1] & 0x03) << 5) | (k[2] >>> 3));
251     desKeyData[3] = (byte)(((k[2] & 0x07) << 4) | (k[3] >>> 4));
252     desKeyData[4] = (byte)(((k[3] & 0x0F) << 3) | (k[4] >>> 5));
253     desKeyData[5] = (byte)(((k[4] & 0x1F) << 2) | (k[5] >>> 6));
254     desKeyData[6] = (byte)(((k[5] & 0x3F) << 1) | (k[6] >>> 7));
255     desKeyData[7] = (byte)(k[6] & 0x7F);
256
257     for(int i=0; i<8; i++)
258       desKeyData[i] = (byte)(unsignedByteToInt(desKeyData[i]) << 1);
259
260     KeySpec desKeySpec = new DESKeySpec(desKeyData);
261     SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
262     SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
263     return secretKey;
264   }
265   /**
266    * Encrypts the 8-bytes plain text three times with the 3 56-bits DES keys and
267    * puts the result in a 24-bytes array.
268    *
269    * @param keys a 21-bytes array containing 3 56-bits DES keys
270    * @param plaintext a 8-bytes array to be encrypted
271    *
272    * @return a 24-bytes array containing the plaintext DES encrypted with the supplied keys
273    *
274    * @exception InvalidKeyException
275    * @exception NoSuchAlgorithmException
276    * @exception javax.crypto.NoSuchPaddingException
277    * @exception InvalidKeySpecException
278    * @exception BadPaddingException
279    * @exception IllegalBlockSizeException
280    * @exception ShortBufferException
281    */

282   protected static byte[] encrypt(byte[] keys, byte[] plaintext) throws InvalidKeyException, NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException, ShortBufferException {
283     byte[] ciphertext = new byte[24];
284     Cipher c = Cipher.getInstance("DES/ECB/NoPadding");
285     Key k = computeDESKey(keys, 0);
286     c.init(Cipher.ENCRYPT_MODE, k);
287     c.doFinal(plaintext, 0, 8, ciphertext, 0);
288     k = computeDESKey(keys, 7);
289     c.init(Cipher.ENCRYPT_MODE, k);
290     c.doFinal(plaintext, 0, 8, ciphertext, 8);
291     k = computeDESKey(keys, 14);
292     c.init(Cipher.ENCRYPT_MODE, k);
293     c.doFinal(plaintext, 0, 8, ciphertext, 16);
294     return ciphertext;
295   }
296
297   /**
298    * Computes the Lan Manager hashed version of a password.
299    *
300    * @param password the user password
301    *
302    * @return the Lan Manager hashed version of the password in a 16-bytes array
303    *
304    * @exception IllegalArgumentException if the supplied password is null
305    * @exception javax.crypto.NoSuchPaddingException if there isn't any suitable padding method
306    * @exception NoSuchAlgorithmException if there isn't any suitable cipher algorithm
307    */

308   public static byte[] computeLMPassword(String JavaDoc password) throws IllegalArgumentException JavaDoc, NoSuchPaddingException, NoSuchAlgorithmException {
309     if(password == null)
310       throw new IllegalArgumentException JavaDoc("password : null value not allowed");
311     try {
312       //Gets the first 14-bytes of the ASCII upper cased password
313
int len = password.length();
314       if(len > 14)
315         len = 14;
316       Cipher c = Cipher.getInstance("DES/ECB/NoPadding");
317
318       byte[] lm_pw = new byte[14];
319       byte[] bytes = password.toUpperCase().getBytes();
320       int i;
321       for(i = 0; i < len; i++)
322         lm_pw[i] = bytes[i];
323       for(; i < 14; i++)
324         lm_pw[i] = 0;
325
326       byte[] lm_hpw = new byte[16];
327       //Builds a first DES key with its first 7 bytes
328
Key k = computeDESKey(lm_pw, 0);
329       c.init(Cipher.ENCRYPT_MODE, k);
330       //Hashes the MAGIC number with this key into the first 8 bytes of the result
331
c.doFinal(MAGIC, 0, 8, lm_hpw, 0);
332
333       //Repeats the work with the last 7 bytes to gets the last 8 bytes of the result
334
k = computeDESKey(lm_pw, 7);
335       c.init(Cipher.ENCRYPT_MODE, k);
336       c.doFinal(MAGIC, 0, 8, lm_hpw, 8);
337
338       return lm_hpw;
339     } catch(InvalidKeySpecException ex) {
340       return null;
341     } catch(InvalidKeyException ex) {
342       return null;
343     } catch(BadPaddingException ex) {
344       return null;
345     } catch(IllegalBlockSizeException ex) {
346       return null;
347     } catch(ShortBufferException ex) {
348       return null;
349     }
350   }
351
352   /**
353    * Computes the NT hashed version of a password.
354    *
355    * @param password the user password
356    *
357    * @return the NT hashed version of the password in a 16-bytes array
358    *
359    * @exception IllegalArgumentException if the supplied password is null
360    * @exception NoSuchAlgorithmException if there isn't any suitable cipher algorithm
361    */

362   public static byte[] computeNTPassword(String JavaDoc password) throws IllegalArgumentException JavaDoc, NoSuchAlgorithmException {
363     if(password == null)
364       throw new IllegalArgumentException JavaDoc("password : null value not allowed");
365     //Gets the first 14-bytes of the UNICODE password
366
int len = password.length();
367     if(len > 14)
368       len = 14;
369     byte[] nt_pw = new byte[2 * len];
370     for(int i = 0; i < len; i++) {
371         char ch = password.charAt(i);
372         nt_pw[2 * i] = getLoByte(ch);
373         nt_pw[2 * i + 1] = getHiByte(ch);
374     }
375
376     //Return its MD4 digest as the hashed version
377
MessageDigest md = MessageDigest.getInstance("MD4");
378     return md.digest(nt_pw);
379   }
380   /**
381    * <p>
382    * Computes the NTLM response to the nonce based on the supplied hashed passwords.
383    * </p>
384    * <p>
385    * If the hashed password are not available they can be computed from the cleartext password
386    * by the means of {@link #computeLMPassword(String) computeLMPassword} and
387    * {@link #computeNTPassword(String) computeNTPassword} methods.
388    * </p>
389    *
390    * @param lmPassword a 16-bytes array containing the Lan Manager hashed password
391    * @param ntPassword a 16-bytes array containing the Lan Manager hashed password
392    * @param nonce a 8-bytes array representing the server's nonce
393    * @param lmResponse a 24-bytes array that will contain the Lan Manager response after the method invocation
394    * @param ntResponse a 24-bytes array that will contain the NT response after the method invocation
395    *
396    * @exception IllegalArgumentException if a parameter has an illegal size
397    * @exception javax.crypto.NoSuchPaddingException if there isn't any suitable padding method
398    * @exception NoSuchAlgorithmException if there isn't any suitable cipher algorithm
399    */

400   public static void computeNTLMResponse(byte[] lmPassword, byte[] ntPassword, byte[] nonce, byte[] lmResponse, byte[] ntResponse) throws IllegalArgumentException JavaDoc, NoSuchPaddingException, NoSuchAlgorithmException {
401     if(lmPassword.length != 16)
402       throw new IllegalArgumentException JavaDoc("lmPassword : illegal size");
403     if(ntPassword.length != 16)
404       throw new IllegalArgumentException JavaDoc("ntPassword : illegal size");
405     if(nonce.length != 8)
406       throw new IllegalArgumentException JavaDoc("nonce : illegal size");
407     if(lmResponse.length != 24)
408       throw new IllegalArgumentException JavaDoc("lmResponse : illegal size");
409     if(ntResponse.length != 24)
410       throw new IllegalArgumentException JavaDoc("ntResponse : illegal size");
411     try {
412       //Puts the hashed passwords into 21-bytes arrays with trailing 0s
413
byte[] lmHPw = new byte[21];
414       byte[] ntHPw = new byte[21];
415       System.arraycopy(lmPassword, 0, lmHPw, 0, 16);
416       System.arraycopy(ntPassword, 0, ntHPw, 0, 16);
417       for(int i = 16; i < 21; i++) {
418         lmHPw[i] = 0;
419         ntHPw[i] = 0;
420       }
421       //Encrypts the nonce with the padded hashed passwords to compute the responses
422
System.arraycopy(encrypt(lmHPw, nonce), 0, lmResponse, 0, 24);
423       System.arraycopy(encrypt(ntHPw, nonce), 0, ntResponse, 0, 24);
424     } catch(ShortBufferException ex) {
425     } catch(IllegalBlockSizeException ex) {
426     } catch(BadPaddingException ex) {
427     } catch(InvalidKeySpecException ex) {
428     } catch(InvalidKeyException ex) {
429     }
430   }
431
432   /**
433    * <p>
434    * Builds a request message for the host of the specified domain that can be send
435    * to the server to start the NTLM protocol.
436    * </p>
437    * <p>
438    * The returned message should be encoded according to protocol specific rules
439    * (e.g. base 64 encoding).<br>
440    * The message format is discussed <a HREF="http://www.innovation.ch/java/ntlm.html">here</a>.
441    * </p>
442    *
443    * @param host the name of the host that is authenticating
444    * @param hostDomain the name of the domain to which the host belongs
445    *
446    * @return the request message to send to server to open an authentication procedure
447    *
448    * @exception IOException if an error occurs during the message formatting
449    *
450    * @see <a HREF="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
451    *
452    */

453   public static byte[] formatRequest(String JavaDoc host, String JavaDoc hostDomain) throws IOException {
454     hostDomain = hostDomain.toUpperCase();
455     host = host.toUpperCase();
456     short domainLen = (short)hostDomain.length();
457     short hostLen = (short)host.length();
458     short hostOff = 0x20;
459     short domainOff = (short)(hostOff + hostLen);
460     ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
461     DataOutputStream dataOut = new DataOutputStream(os);
462     dataOut.writeBytes("NTLMSSP\0");
463     dataOut.writeByte(0x01);
464     dataOut.writeByte(0x00);
465     dataOut.writeByte(0x00);
466     dataOut.writeByte(0x00);
467     dataOut.writeShort(swapBytes((short)0xb203));
468     dataOut.writeShort(0x0000);
469     dataOut.writeShort(swapBytes(domainLen));
470     dataOut.writeShort(swapBytes(domainLen));
471     dataOut.writeShort(swapBytes(domainOff));
472     dataOut.writeShort(0x0000);
473     dataOut.writeShort(swapBytes(hostLen));
474     dataOut.writeShort(swapBytes(hostLen));
475     dataOut.writeShort(swapBytes(hostOff));
476     dataOut.writeShort(0x0000);
477     dataOut.write(host.getBytes());
478     dataOut.write(hostDomain.getBytes());
479     dataOut.flush();
480     return os.toByteArray();
481   }
482
483   /**
484    * <p>
485    * Extracts from the server challenge response the nonce required to perform
486    * the authentication.
487    * </p>
488    * <p>
489    * The received message should be decoded according to protocol specific rules
490    * (e.g. base 64 encoding).<br>
491    * The message format is discussed <a HREF="http://www.innovation.ch/java/ntlm.html">here</a>.
492    * </p>
493    *
494    * @param msg a byte array containing the server challenge message
495    *
496    * @exception IllegalArgumentException if a parameter has an illegal size
497    *
498    * @see <a HREF="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
499    */

500   public static byte[] getNonce(byte[] msg) throws IllegalArgumentException JavaDoc {
501     if(msg.length < 32)
502       throw new IllegalArgumentException JavaDoc("msg : illegal size");
503     byte[] nonce = new byte[8];
504     System.arraycopy(msg, 24, nonce, 0, 8);
505     return nonce;
506   }
507
508   /**
509    * <p>
510    * Builds the nonce response message.
511    * </p>
512    * <p>It requires the Lan Manager and NT hashed
513    * version of user password, that can be computed from the cleartext version by
514    * {@link #computeNTPassword(String) computeNTPassword} and
515    * {@link #computeNTLMResponse(byte[], byte[], byte[], byte[], byte[]) computeNTLMResponse},
516    * and the nonce obtained from the server by {@link #getNonce(byte[]) getNonce}.<br>
517    * The returned message should be encoded according to protocol specific rules
518    * (e.g. base 64 encoding).<br>
519    * The message format is discussed <a HREF="http://www.innovation.ch/java/ntlm.html">here</a>.
520    * </p>
521    *
522    * @param host the name of the host that is authenticating
523    * @param user the name of the user
524    * @param userDomain the name of the domain to which the user belongs
525    * @param lmPassword a 16-bytes array containing the Lan Manager hashed password
526    * @param ntPassword a 16-bytes array containing the NT hashed password
527    * @param nonce a 8-byte array containing the nonce sent by server to reply to the request message
528    *
529    * @return the challenge response message to send to server to complete the authentication procedure
530    *
531    * @exception IOException if an error occurs during the message formatting
532    * @exception IllegalArgumentException if a parameter has an illegal size
533    * @exception javax.crypto.NoSuchPaddingException if there isn't any suitable padding method
534    * @exception NoSuchAlgorithmException if there isn't any suitable cipher algorithm
535    *
536    * @see <a HREF="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
537    */

538   public static byte[] formatResponse(String JavaDoc host, String JavaDoc user, String JavaDoc userDomain, byte[] lmPassword, byte[] ntPassword, byte[] nonce) throws IllegalArgumentException JavaDoc, IOException, NoSuchAlgorithmException, NoSuchPaddingException {
539     if(host == null)
540       throw new IllegalArgumentException JavaDoc("host : null value not allowed");
541     if(user == null)
542       throw new IllegalArgumentException JavaDoc("user : null value not allowed");
543     if(userDomain == null)
544       throw new IllegalArgumentException JavaDoc("userDomain : null value not allowed");
545     if(lmPassword == null)
546       throw new IllegalArgumentException JavaDoc("lmPassword : null value not allowed");
547     if(ntPassword == null)
548       throw new IllegalArgumentException JavaDoc("ntPassword : null value not allowed");
549     if(nonce == null)
550       throw new IllegalArgumentException JavaDoc("nonce : null value not allowed");
551     if(lmPassword.length != 16)
552       throw new IllegalArgumentException JavaDoc("lmPassword : illegal size");
553     if(ntPassword.length != 16)
554       throw new IllegalArgumentException JavaDoc("ntPassword : illegal size");
555     if(nonce.length != 8)
556       throw new IllegalArgumentException JavaDoc("nonce : illegal size");
557
558     byte[] lmResponse = new byte[24];
559     byte[] ntResponse = new byte[24];
560
561     computeNTLMResponse(lmPassword, ntPassword, nonce, lmResponse, ntResponse);
562
563     userDomain = userDomain.toUpperCase();
564     host = host.toUpperCase();
565     short lmRespLen = (short)0x18;
566     short ntRespLen = (short)0x18;
567     short domainLen = (short)(2 * userDomain.length());
568     short hostLen = (short)(2 * host.length());
569     short userLen = (short)(2 * user.length());
570     short domainOff = (short)0x40;
571     short userOff = (short)(domainOff + domainLen);
572     short hostOff = (short)(userOff + userLen);
573     short lmRespOff = (short)(hostOff + hostLen);
574     short ntRespOff = (short)(lmRespOff + lmRespLen);
575     short msgLen = (short)(ntRespOff + ntRespLen);
576     ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
577     DataOutputStream dataOut = new DataOutputStream(os);
578     dataOut.writeBytes("NTLMSSP\0");
579     dataOut.writeByte(0x03);
580     dataOut.writeByte(0x00);
581     dataOut.writeByte(0x00);
582     dataOut.writeByte(0x00);
583     dataOut.writeShort(swapBytes(lmRespLen));
584     dataOut.writeShort(swapBytes(lmRespLen));
585     dataOut.writeShort(swapBytes(lmRespOff));
586     dataOut.writeShort(0x0000);
587     dataOut.writeShort(swapBytes(ntRespLen));
588     dataOut.writeShort(swapBytes(ntRespLen));
589     dataOut.writeShort(swapBytes(ntRespOff));
590     dataOut.writeShort(0x0000);
591     dataOut.writeShort(swapBytes(domainLen));
592     dataOut.writeShort(swapBytes(domainLen));
593     dataOut.writeShort(swapBytes(domainOff));
594     dataOut.writeShort(0x0000);
595     dataOut.writeShort(swapBytes(userLen));
596     dataOut.writeShort(swapBytes(userLen));
597     dataOut.writeShort(swapBytes(userOff));
598     dataOut.writeShort(0x0000);
599     dataOut.writeShort(swapBytes(hostLen));
600     dataOut.writeShort(swapBytes(hostLen));
601     dataOut.writeShort(swapBytes(hostOff));
602     dataOut.writeShort(0x0000);
603     dataOut.writeInt(0x00000000);
604     dataOut.writeShort(swapBytes(msgLen));
605     dataOut.writeShort(0x0000);
606     dataOut.writeShort(0x0000); // dataOut.writeShort(swapBytes((short)0x8201));
607
dataOut.writeShort(0x0000);
608
609     for(int i = 0; i < userDomain.length(); i++)
610       dataOut.writeShort(swapBytes((short)userDomain.charAt(i)));
611     for(int i = 0; i < user.length(); i++)
612       dataOut.writeShort(swapBytes((short)user.charAt(i)));
613     for(int i = 0; i < host.length(); i++)
614       dataOut.writeShort(swapBytes((short)host.charAt(i)));
615     dataOut.write(lmResponse);
616     dataOut.write(ntResponse);
617     dataOut.flush();
618     return os.toByteArray();
619   }
620 }
621
Popular Tags