View Javadoc

1   /*
2    * ====================================================================
3    *
4    *  Licensed to the Apache Software Foundation (ASF) under one or more
5    *  contributor license agreements.  See the NOTICE file distributed with
6    *  this work for additional information regarding copyright ownership.
7    *  The ASF licenses this file to You under the Apache License, Version 2.0
8    *  (the "License"); you may not use this file except in compliance with
9    *  the License.  You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *  Unless required by applicable law or agreed to in writing, software
14   *  distributed under the License is distributed on an "AS IS" BASIS,
15   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *  See the License for the specific language governing permissions and
17   *  limitations under the License.
18   * ====================================================================
19   *
20   * This software consists of voluntary contributions made by many
21   * individuals on behalf of the Apache Software Foundation.  For more
22   * information on the Apache Software Foundation, please see
23   * <http://www.apache.org/>.
24   *
25   */
26  
27  package org.apache.http.impl.auth;
28  
29  import java.io.UnsupportedEncodingException;
30  import java.security.Key;
31  import java.security.MessageDigest;
32  import java.util.Arrays;
33  import java.util.Locale;
34  
35  import javax.crypto.Cipher;
36  import javax.crypto.spec.SecretKeySpec;
37  
38  import org.apache.commons.codec.binary.Base64;
39  import org.apache.http.util.EncodingUtils;
40  
41  /**
42   * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM
43   * authentication protocol.
44   *
45   * @since 4.1
46   */
47  final class NTLMEngineImpl implements NTLMEngine {
48  
49      // Flags we use; descriptions according to:
50      // http://davenport.sourceforge.net/ntlm.html
51      // and
52      // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx
53      protected final static int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001;      // Unicode string encoding requested
54      protected final static int FLAG_REQUEST_TARGET = 0x00000004;                      // Requests target field
55      protected final static int FLAG_REQUEST_SIGN = 0x00000010;  // Requests all messages have a signature attached, in NEGOTIATE message.
56      protected final static int FLAG_REQUEST_SEAL = 0x00000020;  // Request key exchange for message confidentiality in NEGOTIATE message.  MUST be used in conjunction with 56BIT.
57      protected final static int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080;    // Request Lan Manager key instead of user session key
58      protected final static int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security.  MUST be set in NEGOTIATE and CHALLENGE both
59      protected final static int FLAG_DOMAIN_PRESENT = 0x00001000;        // Domain is present in message
60      protected final static int FLAG_WORKSTATION_PRESENT = 0x00002000;   // Workstation is present in message
61      protected final static int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000;   // Requests a signature block on all messages.  Overridden by REQUEST_SIGN and REQUEST_SEAL.
62      protected final static int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security
63      protected final static int FLAG_REQUEST_VERSION = 0x02000000;       // Request protocol version
64      protected final static int FLAG_TARGETINFO_PRESENT = 0x00800000;    // From server in challenge message, indicating targetinfo is present
65      protected final static int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange
66      protected final static int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000;     // Request explicit key exchange
67      protected final static int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000;      // Must be used in conjunction with SEAL
68  
69  
70      /** Secure random generator */
71      private static final java.security.SecureRandom RND_GEN;
72      static {
73          java.security.SecureRandom rnd = null;
74          try {
75              rnd = java.security.SecureRandom.getInstance("SHA1PRNG");
76          } catch (Exception e) {
77          }
78          RND_GEN = rnd;
79      }
80  
81      /** Character encoding */
82      static final String DEFAULT_CHARSET = "ASCII";
83  
84      /** The character set to use for encoding the credentials */
85      private String credentialCharset = DEFAULT_CHARSET;
86  
87      /** The signature string as bytes in the default encoding */
88      private static byte[] SIGNATURE;
89  
90      static {
91          byte[] bytesWithoutNull = EncodingUtils.getBytes("NTLMSSP", "ASCII");
92          SIGNATURE = new byte[bytesWithoutNull.length + 1];
93          System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length);
94          SIGNATURE[bytesWithoutNull.length] = (byte) 0x00;
95      }
96  
97      /**
98       * Returns the response for the given message.
99       *
100      * @param message
101      *            the message that was received from the server.
102      * @param username
103      *            the username to authenticate with.
104      * @param password
105      *            the password to authenticate with.
106      * @param host
107      *            The host.
108      * @param domain
109      *            the NT domain to authenticate in.
110      * @return The response.
111      * @throws HttpException
112      *             If the messages cannot be retrieved.
113      */
114     final String getResponseFor(String message, String username, String password,
115             String host, String domain) throws NTLMEngineException {
116 
117         final String response;
118         if (message == null || message.trim().equals("")) {
119             response = getType1Message(host, domain);
120         } else {
121             Type2Message t2m = new Type2Message(message);
122             response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m
123                     .getFlags(), t2m.getTarget(), t2m.getTargetInfo());
124         }
125         return response;
126     }
127 
128     /**
129      * Creates the first message (type 1 message) in the NTLM authentication
130      * sequence. This message includes the user name, domain and host for the
131      * authentication session.
132      *
133      * @param host
134      *            the computer name of the host requesting authentication.
135      * @param domain
136      *            The domain to authenticate with.
137      * @return String the message to add to the HTTP request header.
138      */
139     String getType1Message(String host, String domain) throws NTLMEngineException {
140         return new Type1Message(domain, host).getResponse();
141     }
142 
143     /**
144      * Creates the type 3 message using the given server nonce. The type 3
145      * message includes all the information for authentication, host, domain,
146      * username and the result of encrypting the nonce sent by the server using
147      * the user's password as the key.
148      *
149      * @param user
150      *            The user name. This should not include the domain name.
151      * @param password
152      *            The password.
153      * @param host
154      *            The host that is originating the authentication request.
155      * @param domain
156      *            The domain to authenticate within.
157      * @param nonce
158      *            the 8 byte array the server sent.
159      * @return The type 3 message.
160      * @throws NTLMEngineException
161      *             If {@encrypt(byte[],byte[])} fails.
162      */
163     String getType3Message(String user, String password, String host, String domain,
164             byte[] nonce, int type2Flags, String target, byte[] targetInformation)
165             throws NTLMEngineException {
166         return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
167                 targetInformation).getResponse();
168     }
169 
170     /**
171      * @return Returns the credentialCharset.
172      */
173     String getCredentialCharset() {
174         return credentialCharset;
175     }
176 
177     /**
178      * @param credentialCharset
179      *            The credentialCharset to set.
180      */
181     void setCredentialCharset(String credentialCharset) {
182         this.credentialCharset = credentialCharset;
183     }
184 
185     /** Strip dot suffix from a name */
186     private static String stripDotSuffix(String value) {
187         if (value == null) {
188             return null;
189         }
190         int index = value.indexOf(".");
191         if (index != -1)
192             return value.substring(0, index);
193         return value;
194     }
195 
196     /** Convert host to standard form */
197     private static String convertHost(String host) {
198         return stripDotSuffix(host);
199     }
200 
201     /** Convert domain to standard form */
202     private static String convertDomain(String domain) {
203         return stripDotSuffix(domain);
204     }
205 
206     private static int readULong(byte[] src, int index) throws NTLMEngineException {
207         if (src.length < index + 4)
208             throw new NTLMEngineException("NTLM authentication - buffer too small for DWORD");
209         return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8)
210                 | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24);
211     }
212 
213     private static int readUShort(byte[] src, int index) throws NTLMEngineException {
214         if (src.length < index + 2)
215             throw new NTLMEngineException("NTLM authentication - buffer too small for WORD");
216         return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);
217     }
218 
219     private static byte[] readSecurityBuffer(byte[] src, int index) throws NTLMEngineException {
220         int length = readUShort(src, index);
221         int offset = readULong(src, index + 4);
222         if (src.length < offset + length)
223             throw new NTLMEngineException(
224                     "NTLM authentication - buffer too small for data item");
225         byte[] buffer = new byte[length];
226         System.arraycopy(src, offset, buffer, 0, length);
227         return buffer;
228     }
229 
230     /** Calculate a challenge block */
231     private static byte[] makeRandomChallenge() throws NTLMEngineException {
232         if (RND_GEN == null) {
233             throw new NTLMEngineException("Random generator not available");
234         }
235         byte[] rval = new byte[8];
236         synchronized (RND_GEN) {
237             RND_GEN.nextBytes(rval);
238         }
239         return rval;
240     }
241 
242     /** Calculate a 16-byte secondary key */
243     private static byte[] makeSecondaryKey() throws NTLMEngineException {
244         if (RND_GEN == null) {
245             throw new NTLMEngineException("Random generator not available");
246         }
247         byte[] rval = new byte[16];
248         synchronized (RND_GEN) {
249             RND_GEN.nextBytes(rval);
250         }
251         return rval;
252     }
253 
254     protected static class CipherGen {
255 
256         protected final String domain;
257         protected final String user;
258         protected final String password;
259         protected final byte[] challenge;
260         protected final String target;
261         protected final byte[] targetInformation;
262 
263         // Information we can generate but may be passed in (for testing)
264         protected byte[] clientChallenge;
265         protected byte[] clientChallenge2;
266         protected byte[] secondaryKey;
267         protected byte[] timestamp;
268 
269         // Stuff we always generate
270         protected byte[] lmHash = null;
271         protected byte[] lmResponse = null;
272         protected byte[] ntlmHash = null;
273         protected byte[] ntlmResponse = null;
274         protected byte[] ntlmv2Hash = null;
275         protected byte[] lmv2Hash = null;
276         protected byte[] lmv2Response = null;
277         protected byte[] ntlmv2Blob = null;
278         protected byte[] ntlmv2Response = null;
279         protected byte[] ntlm2SessionResponse = null;
280         protected byte[] lm2SessionResponse = null;
281         protected byte[] lmUserSessionKey = null;
282         protected byte[] ntlmUserSessionKey = null;
283         protected byte[] ntlmv2UserSessionKey = null;
284         protected byte[] ntlm2SessionResponseUserSessionKey = null;
285         protected byte[] lanManagerSessionKey = null;
286 
287         public CipherGen(String domain, String user, String password,
288             byte[] challenge, String target, byte[] targetInformation,
289             byte[] clientChallenge, byte[] clientChallenge2,
290             byte[] secondaryKey, byte[] timestamp) {
291             this.domain = domain;
292             this.target = target;
293             this.user = user;
294             this.password = password;
295             this.challenge = challenge;
296             this.targetInformation = targetInformation;
297             this.clientChallenge = clientChallenge;
298             this.clientChallenge2 = clientChallenge2;
299             this.secondaryKey = secondaryKey;
300             this.timestamp = timestamp;
301         }
302 
303         public CipherGen(String domain, String user, String password,
304             byte[] challenge, String target, byte[] targetInformation) {
305             this(domain, user, password, challenge, target, targetInformation, null, null, null, null);
306         }
307 
308         /** Calculate and return client challenge */
309         public byte[] getClientChallenge()
310             throws NTLMEngineException {
311             if (clientChallenge == null)
312                 clientChallenge = makeRandomChallenge();
313             return clientChallenge;
314         }
315 
316         /** Calculate and return second client challenge */
317         public byte[] getClientChallenge2()
318             throws NTLMEngineException {
319             if (clientChallenge2 == null)
320                 clientChallenge2 = makeRandomChallenge();
321             return clientChallenge2;
322         }
323 
324         /** Calculate and return random secondary key */
325         public byte[] getSecondaryKey()
326             throws NTLMEngineException {
327             if (secondaryKey == null)
328                 secondaryKey = makeSecondaryKey();
329             return secondaryKey;
330         }
331 
332         /** Calculate and return the LMHash */
333         public byte[] getLMHash()
334             throws NTLMEngineException {
335             if (lmHash == null)
336                 lmHash = lmHash(password);
337             return lmHash;
338         }
339 
340         /** Calculate and return the LMResponse */
341         public byte[] getLMResponse()
342             throws NTLMEngineException {
343             if (lmResponse == null)
344                 lmResponse = lmResponse(getLMHash(),challenge);
345             return lmResponse;
346         }
347 
348         /** Calculate and return the NTLMHash */
349         public byte[] getNTLMHash()
350             throws NTLMEngineException {
351             if (ntlmHash == null)
352                 ntlmHash = ntlmHash(password);
353             return ntlmHash;
354         }
355 
356         /** Calculate and return the NTLMResponse */
357         public byte[] getNTLMResponse()
358             throws NTLMEngineException {
359             if (ntlmResponse == null)
360                 ntlmResponse = lmResponse(getNTLMHash(),challenge);
361             return ntlmResponse;
362         }
363 
364         /** Calculate the LMv2 hash */
365         public byte[] getLMv2Hash()
366             throws NTLMEngineException {
367             if (lmv2Hash == null)
368                 lmv2Hash = lmv2Hash(domain, user, getNTLMHash());
369             return lmv2Hash;
370         }
371 
372         /** Calculate the NTLMv2 hash */
373         public byte[] getNTLMv2Hash()
374             throws NTLMEngineException {
375             if (ntlmv2Hash == null)
376                 ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash());
377             return ntlmv2Hash;
378         }
379 
380         /** Calculate a timestamp */
381         public byte[] getTimestamp() {
382             if (timestamp == null) {
383                 long time = System.currentTimeMillis();
384                 time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch.
385                 time *= 10000; // tenths of a microsecond.
386                 // convert to little-endian byte array.
387                 timestamp = new byte[8];
388                 for (int i = 0; i < 8; i++) {
389                     timestamp[i] = (byte) time;
390                     time >>>= 8;
391                 }
392             }
393             return timestamp;
394         }
395 
396         /** Calculate the NTLMv2Blob */
397         public byte[] getNTLMv2Blob()
398             throws NTLMEngineException {
399             if (ntlmv2Blob == null)
400                 ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp());
401             return ntlmv2Blob;
402         }
403 
404         /** Calculate the NTLMv2Response */
405         public byte[] getNTLMv2Response()
406             throws NTLMEngineException {
407             if (ntlmv2Response == null)
408                 ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob());
409             return ntlmv2Response;
410         }
411 
412         /** Calculate the LMv2Response */
413         public byte[] getLMv2Response()
414             throws NTLMEngineException {
415             if (lmv2Response == null)
416                 lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge());
417             return lmv2Response;
418         }
419 
420         /** Get NTLM2SessionResponse */
421         public byte[] getNTLM2SessionResponse()
422             throws NTLMEngineException {
423             if (ntlm2SessionResponse == null)
424                 ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge());
425             return ntlm2SessionResponse;
426         }
427 
428         /** Calculate and return LM2 session response */
429         public byte[] getLM2SessionResponse()
430             throws NTLMEngineException {
431             if (lm2SessionResponse == null) {
432                 byte[] clientChallenge = getClientChallenge();
433                 lm2SessionResponse = new byte[24];
434                 System.arraycopy(clientChallenge, 0, lm2SessionResponse, 0, clientChallenge.length);
435                 Arrays.fill(lm2SessionResponse, clientChallenge.length, lm2SessionResponse.length, (byte) 0x00);
436             }
437             return lm2SessionResponse;
438         }
439 
440         /** Get LMUserSessionKey */
441         public byte[] getLMUserSessionKey()
442             throws NTLMEngineException {
443             if (lmUserSessionKey == null) {
444                 byte[] lmHash = getLMHash();
445                 lmUserSessionKey = new byte[16];
446                 System.arraycopy(lmHash, 0, lmUserSessionKey, 0, 8);
447                 Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00);
448             }
449             return lmUserSessionKey;
450         }
451 
452         /** Get NTLMUserSessionKey */
453         public byte[] getNTLMUserSessionKey()
454             throws NTLMEngineException {
455             if (ntlmUserSessionKey == null) {
456                 byte[] ntlmHash = getNTLMHash();
457                 MD4 md4 = new MD4();
458                 md4.update(ntlmHash);
459                 ntlmUserSessionKey = md4.getOutput();
460             }
461             return ntlmUserSessionKey;
462         }
463 
464         /** GetNTLMv2UserSessionKey */
465         public byte[] getNTLMv2UserSessionKey()
466             throws NTLMEngineException {
467             if (ntlmv2UserSessionKey == null) {
468                 byte[] ntlmv2hash = getNTLMv2Hash();
469                 byte[] truncatedResponse = new byte[16];
470                 System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16);
471                 ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash);
472             }
473             return ntlmv2UserSessionKey;
474         }
475 
476         /** Get NTLM2SessionResponseUserSessionKey */
477         public byte[] getNTLM2SessionResponseUserSessionKey()
478             throws NTLMEngineException {
479             if (ntlm2SessionResponseUserSessionKey == null) {
480                 byte[] ntlmUserSessionKey = getNTLMUserSessionKey();
481                 byte[] ntlm2SessionResponseNonce = getLM2SessionResponse();
482                 byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length];
483                 System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length);
484                 System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length);
485                 ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,ntlmUserSessionKey);
486             }
487             return ntlm2SessionResponseUserSessionKey;
488         }
489 
490         /** Get LAN Manager session key */
491         public byte[] getLanManagerSessionKey()
492             throws NTLMEngineException {
493             if (lanManagerSessionKey == null) {
494                 byte[] lmHash = getLMHash();
495                 byte[] lmResponse = getLMResponse();
496                 try {
497                     byte[] keyBytes = new byte[14];
498                     System.arraycopy(lmHash, 0, keyBytes, 0, 8);
499                     Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd);
500                     Key lowKey = createDESKey(keyBytes, 0);
501                     Key highKey = createDESKey(keyBytes, 7);
502                     byte[] truncatedResponse = new byte[8];
503                     System.arraycopy(lmResponse, 0, truncatedResponse, 0, truncatedResponse.length);
504                     Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
505                     des.init(Cipher.ENCRYPT_MODE, lowKey);
506                     byte[] lowPart = des.doFinal(truncatedResponse);
507                     des = Cipher.getInstance("DES/ECB/NoPadding");
508                     des.init(Cipher.ENCRYPT_MODE, highKey);
509                     byte[] highPart = des.doFinal(truncatedResponse);
510                     lanManagerSessionKey = new byte[16];
511                     System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length);
512                     System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length);
513                 } catch (Exception e) {
514                     throw new NTLMEngineException(e.getMessage(), e);
515                 }
516             }
517             return lanManagerSessionKey;
518         }
519     }
520 
521     /** Calculates HMAC-MD5 */
522     static byte[] hmacMD5(byte[] value, byte[] key)
523         throws NTLMEngineException {
524         HMACMD5 hmacMD5 = new HMACMD5(key);
525         hmacMD5.update(value);
526         return hmacMD5.getOutput();
527     }
528 
529     /** Calculates RC4 */
530     static byte[] RC4(byte[] value, byte[] key)
531         throws NTLMEngineException {
532         try {
533             Cipher rc4 = Cipher.getInstance("RC4");
534             rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4"));
535             return rc4.doFinal(value);
536         } catch (Exception e) {
537             throw new NTLMEngineException(e.getMessage(), e);
538         }
539     }
540 
541     /**
542      * Calculates the NTLM2 Session Response for the given challenge, using the
543      * specified password and client challenge.
544      *
545      * @param password
546      *            The user's password.
547      * @param challenge
548      *            The Type 2 challenge from the server.
549      * @param clientChallenge
550      *            The random 8-byte client challenge.
551      *
552      * @return The NTLM2 Session Response. This is placed in the NTLM response
553      *         field of the Type 3 message; the LM response field contains the
554      *         client challenge, null-padded to 24 bytes.
555      */
556     static byte[] ntlm2SessionResponse(byte[] ntlmHash, byte[] challenge,
557             byte[] clientChallenge) throws NTLMEngineException {
558         try {
559             // Look up MD5 algorithm (was necessary on jdk 1.4.2)
560             // This used to be needed, but java 1.5.0_07 includes the MD5
561             // algorithm (finally)
562             // Class x = Class.forName("gnu.crypto.hash.MD5");
563             // Method updateMethod = x.getMethod("update",new
564             // Class[]{byte[].class});
565             // Method digestMethod = x.getMethod("digest",new Class[0]);
566             // Object mdInstance = x.newInstance();
567             // updateMethod.invoke(mdInstance,new Object[]{challenge});
568             // updateMethod.invoke(mdInstance,new Object[]{clientChallenge});
569             // byte[] digest = (byte[])digestMethod.invoke(mdInstance,new
570             // Object[0]);
571 
572             MessageDigest md5 = MessageDigest.getInstance("MD5");
573             md5.update(challenge);
574             md5.update(clientChallenge);
575             byte[] digest = md5.digest();
576 
577             byte[] sessionHash = new byte[8];
578             System.arraycopy(digest, 0, sessionHash, 0, 8);
579             return lmResponse(ntlmHash, sessionHash);
580         } catch (Exception e) {
581             if (e instanceof NTLMEngineException)
582                 throw (NTLMEngineException) e;
583             throw new NTLMEngineException(e.getMessage(), e);
584         }
585     }
586 
587     /**
588      * Creates the LM Hash of the user's password.
589      *
590      * @param password
591      *            The password.
592      *
593      * @return The LM Hash of the given password, used in the calculation of the
594      *         LM Response.
595      */
596     private static byte[] lmHash(String password) throws NTLMEngineException {
597         try {
598             byte[] oemPassword = password.toUpperCase(Locale.US).getBytes("US-ASCII");
599             int length = Math.min(oemPassword.length, 14);
600             byte[] keyBytes = new byte[14];
601             System.arraycopy(oemPassword, 0, keyBytes, 0, length);
602             Key lowKey = createDESKey(keyBytes, 0);
603             Key highKey = createDESKey(keyBytes, 7);
604             byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII");
605             Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
606             des.init(Cipher.ENCRYPT_MODE, lowKey);
607             byte[] lowHash = des.doFinal(magicConstant);
608             des.init(Cipher.ENCRYPT_MODE, highKey);
609             byte[] highHash = des.doFinal(magicConstant);
610             byte[] lmHash = new byte[16];
611             System.arraycopy(lowHash, 0, lmHash, 0, 8);
612             System.arraycopy(highHash, 0, lmHash, 8, 8);
613             return lmHash;
614         } catch (Exception e) {
615             throw new NTLMEngineException(e.getMessage(), e);
616         }
617     }
618 
619     /**
620      * Creates the NTLM Hash of the user's password.
621      *
622      * @param password
623      *            The password.
624      *
625      * @return The NTLM Hash of the given password, used in the calculation of
626      *         the NTLM Response and the NTLMv2 and LMv2 Hashes.
627      */
628     private static byte[] ntlmHash(String password) throws NTLMEngineException {
629         try {
630             byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
631             MD4 md4 = new MD4();
632             md4.update(unicodePassword);
633             return md4.getOutput();
634         } catch (UnsupportedEncodingException e) {
635             throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e);
636         }
637     }
638 
639     /**
640      * Creates the LMv2 Hash of the user's password.
641      *
642      * @param target
643      *            The authentication target (i.e., domain).
644      * @param user
645      *            The username.
646      * @param password
647      *            The password.
648      *
649      * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2
650      *         Responses.
651      */
652     private static byte[] lmv2Hash(String domain, String user, byte[] ntlmHash)
653             throws NTLMEngineException {
654         try {
655             HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
656             // Upper case username, upper case domain!
657             hmacMD5.update(user.toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked"));
658             if (domain != null) {
659                 hmacMD5.update(domain.toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked"));
660             }
661             return hmacMD5.getOutput();
662         } catch (UnsupportedEncodingException e) {
663             throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e);
664         }
665     }
666 
667     /**
668      * Creates the NTLMv2 Hash of the user's password.
669      *
670      * @param target
671      *            The authentication target (i.e., domain).
672      * @param user
673      *            The username.
674      * @param password
675      *            The password.
676      *
677      * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2
678      *         Responses.
679      */
680     private static byte[] ntlmv2Hash(String domain, String user, byte[] ntlmHash)
681             throws NTLMEngineException {
682         try {
683             HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
684             // Upper case username, mixed case target!!
685             hmacMD5.update(user.toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked"));
686             if (domain != null) {
687                 hmacMD5.update(domain.getBytes("UnicodeLittleUnmarked"));
688             }
689             return hmacMD5.getOutput();
690         } catch (UnsupportedEncodingException e) {
691             throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e);
692         }
693     }
694 
695     /**
696      * Creates the LM Response from the given hash and Type 2 challenge.
697      *
698      * @param hash
699      *            The LM or NTLM Hash.
700      * @param challenge
701      *            The server challenge from the Type 2 message.
702      *
703      * @return The response (either LM or NTLM, depending on the provided hash).
704      */
705     private static byte[] lmResponse(byte[] hash, byte[] challenge) throws NTLMEngineException {
706         try {
707             byte[] keyBytes = new byte[21];
708             System.arraycopy(hash, 0, keyBytes, 0, 16);
709             Key lowKey = createDESKey(keyBytes, 0);
710             Key middleKey = createDESKey(keyBytes, 7);
711             Key highKey = createDESKey(keyBytes, 14);
712             Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
713             des.init(Cipher.ENCRYPT_MODE, lowKey);
714             byte[] lowResponse = des.doFinal(challenge);
715             des.init(Cipher.ENCRYPT_MODE, middleKey);
716             byte[] middleResponse = des.doFinal(challenge);
717             des.init(Cipher.ENCRYPT_MODE, highKey);
718             byte[] highResponse = des.doFinal(challenge);
719             byte[] lmResponse = new byte[24];
720             System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
721             System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
722             System.arraycopy(highResponse, 0, lmResponse, 16, 8);
723             return lmResponse;
724         } catch (Exception e) {
725             throw new NTLMEngineException(e.getMessage(), e);
726         }
727     }
728 
729     /**
730      * Creates the LMv2 Response from the given hash, client data, and Type 2
731      * challenge.
732      *
733      * @param hash
734      *            The NTLMv2 Hash.
735      * @param clientData
736      *            The client data (blob or client challenge).
737      * @param challenge
738      *            The server challenge from the Type 2 message.
739      *
740      * @return The response (either NTLMv2 or LMv2, depending on the client
741      *         data).
742      */
743     private static byte[] lmv2Response(byte[] hash, byte[] challenge, byte[] clientData)
744             throws NTLMEngineException {
745         HMACMD5 hmacMD5 = new HMACMD5(hash);
746         hmacMD5.update(challenge);
747         hmacMD5.update(clientData);
748         byte[] mac = hmacMD5.getOutput();
749         byte[] lmv2Response = new byte[mac.length + clientData.length];
750         System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
751         System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
752         return lmv2Response;
753     }
754 
755     /**
756      * Creates the NTLMv2 blob from the given target information block and
757      * client challenge.
758      *
759      * @param targetInformation
760      *            The target information block from the Type 2 message.
761      * @param clientChallenge
762      *            The random 8-byte client challenge.
763      *
764      * @return The blob, used in the calculation of the NTLMv2 Response.
765      */
766     private static byte[] createBlob(byte[] clientChallenge, byte[] targetInformation, byte[] timestamp) {
767         byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
768         byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
769         byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
770         byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
771         byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8
772                 + unknown1.length + targetInformation.length + unknown2.length];
773         int offset = 0;
774         System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
775         offset += blobSignature.length;
776         System.arraycopy(reserved, 0, blob, offset, reserved.length);
777         offset += reserved.length;
778         System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
779         offset += timestamp.length;
780         System.arraycopy(clientChallenge, 0, blob, offset, 8);
781         offset += 8;
782         System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
783         offset += unknown1.length;
784         System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
785         offset += targetInformation.length;
786         System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
787         offset += unknown2.length;
788         return blob;
789     }
790 
791     /**
792      * Creates a DES encryption key from the given key material.
793      *
794      * @param bytes
795      *            A byte array containing the DES key material.
796      * @param offset
797      *            The offset in the given byte array at which the 7-byte key
798      *            material starts.
799      *
800      * @return A DES encryption key created from the key material starting at
801      *         the specified offset in the given byte array.
802      */
803     private static Key createDESKey(byte[] bytes, int offset) {
804         byte[] keyBytes = new byte[7];
805         System.arraycopy(bytes, offset, keyBytes, 0, 7);
806         byte[] material = new byte[8];
807         material[0] = keyBytes[0];
808         material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
809         material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
810         material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
811         material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
812         material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
813         material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
814         material[7] = (byte) (keyBytes[6] << 1);
815         oddParity(material);
816         return new SecretKeySpec(material, "DES");
817     }
818 
819     /**
820      * Applies odd parity to the given byte array.
821      *
822      * @param bytes
823      *            The data whose parity bits are to be adjusted for odd parity.
824      */
825     private static void oddParity(byte[] bytes) {
826         for (int i = 0; i < bytes.length; i++) {
827             byte b = bytes[i];
828             boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3)
829                     ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
830             if (needsParity) {
831                 bytes[i] |= (byte) 0x01;
832             } else {
833                 bytes[i] &= (byte) 0xfe;
834             }
835         }
836     }
837 
838     /** NTLM message generation, base class */
839     static class NTLMMessage {
840         /** The current response */
841         private byte[] messageContents = null;
842 
843         /** The current output position */
844         private int currentOutputPosition = 0;
845 
846         /** Constructor to use when message contents are not yet known */
847         NTLMMessage() {
848         }
849 
850         /** Constructor to use when message contents are known */
851         NTLMMessage(String messageBody, int expectedType) throws NTLMEngineException {
852             messageContents = Base64.decodeBase64(EncodingUtils.getBytes(messageBody,
853                     DEFAULT_CHARSET));
854             // Look for NTLM message
855             if (messageContents.length < SIGNATURE.length)
856                 throw new NTLMEngineException("NTLM message decoding error - packet too short");
857             int i = 0;
858             while (i < SIGNATURE.length) {
859                 if (messageContents[i] != SIGNATURE[i])
860                     throw new NTLMEngineException(
861                             "NTLM message expected - instead got unrecognized bytes");
862                 i++;
863             }
864 
865             // Check to be sure there's a type 2 message indicator next
866             int type = readULong(SIGNATURE.length);
867             if (type != expectedType)
868                 throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType)
869                         + " message expected - instead got type " + Integer.toString(type));
870 
871             currentOutputPosition = messageContents.length;
872         }
873 
874         /**
875          * Get the length of the signature and flags, so calculations can adjust
876          * offsets accordingly.
877          */
878         protected int getPreambleLength() {
879             return SIGNATURE.length + 4;
880         }
881 
882         /** Get the message length */
883         protected int getMessageLength() {
884             return currentOutputPosition;
885         }
886 
887         /** Read a byte from a position within the message buffer */
888         protected byte readByte(int position) throws NTLMEngineException {
889             if (messageContents.length < position + 1)
890                 throw new NTLMEngineException("NTLM: Message too short");
891             return messageContents[position];
892         }
893 
894         /** Read a bunch of bytes from a position in the message buffer */
895         protected void readBytes(byte[] buffer, int position) throws NTLMEngineException {
896             if (messageContents.length < position + buffer.length)
897                 throw new NTLMEngineException("NTLM: Message too short");
898             System.arraycopy(messageContents, position, buffer, 0, buffer.length);
899         }
900 
901         /** Read a ushort from a position within the message buffer */
902         protected int readUShort(int position) throws NTLMEngineException {
903             return NTLMEngineImpl.readUShort(messageContents, position);
904         }
905 
906         /** Read a ulong from a position within the message buffer */
907         protected int readULong(int position) throws NTLMEngineException {
908             return NTLMEngineImpl.readULong(messageContents, position);
909         }
910 
911         /** Read a security buffer from a position within the message buffer */
912         protected byte[] readSecurityBuffer(int position) throws NTLMEngineException {
913             return NTLMEngineImpl.readSecurityBuffer(messageContents, position);
914         }
915 
916         /**
917          * Prepares the object to create a response of the given length.
918          *
919          * @param length
920          *            the maximum length of the response to prepare, not
921          *            including the type and the signature (which this method
922          *            adds).
923          */
924         protected void prepareResponse(int maxlength, int messageType) {
925             messageContents = new byte[maxlength];
926             currentOutputPosition = 0;
927             addBytes(SIGNATURE);
928             addULong(messageType);
929         }
930 
931         /**
932          * Adds the given byte to the response.
933          *
934          * @param b
935          *            the byte to add.
936          */
937         protected void addByte(byte b) {
938             messageContents[currentOutputPosition] = b;
939             currentOutputPosition++;
940         }
941 
942         /**
943          * Adds the given bytes to the response.
944          *
945          * @param bytes
946          *            the bytes to add.
947          */
948         protected void addBytes(byte[] bytes) {
949             if (bytes == null) {
950                 return;
951             }
952             for (byte b : bytes) {
953                 messageContents[currentOutputPosition] = b;
954                 currentOutputPosition++;
955             }
956         }
957 
958         /** Adds a USHORT to the response */
959         protected void addUShort(int value) {
960             addByte((byte) (value & 0xff));
961             addByte((byte) (value >> 8 & 0xff));
962         }
963 
964         /** Adds a ULong to the response */
965         protected void addULong(int value) {
966             addByte((byte) (value & 0xff));
967             addByte((byte) (value >> 8 & 0xff));
968             addByte((byte) (value >> 16 & 0xff));
969             addByte((byte) (value >> 24 & 0xff));
970         }
971 
972         /**
973          * Returns the response that has been generated after shrinking the
974          * array if required and base64 encodes the response.
975          *
976          * @return The response as above.
977          */
978         String getResponse() {
979             byte[] resp;
980             if (messageContents.length > currentOutputPosition) {
981                 byte[] tmp = new byte[currentOutputPosition];
982                 for (int i = 0; i < currentOutputPosition; i++) {
983                     tmp[i] = messageContents[i];
984                 }
985                 resp = tmp;
986             } else {
987                 resp = messageContents;
988             }
989             return EncodingUtils.getAsciiString(Base64.encodeBase64(resp));
990         }
991 
992     }
993 
994     /** Type 1 message assembly class */
995     static class Type1Message extends NTLMMessage {
996         protected byte[] hostBytes;
997         protected byte[] domainBytes;
998 
999         /** Constructor. Include the arguments the message will need */
1000         Type1Message(String domain, String host) throws NTLMEngineException {
1001             super();
1002             try {
1003                 // Strip off domain name from the host!
1004                 String unqualifiedHost = convertHost(host);
1005                 // Use only the base domain name!
1006                 String unqualifiedDomain = convertDomain(domain);
1007 
1008                 hostBytes = unqualifiedHost != null? unqualifiedHost.getBytes("ASCII") : null;
1009                 domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1010                         .toUpperCase(Locale.US).getBytes("ASCII") : null;
1011             } catch (UnsupportedEncodingException e) {
1012                 throw new NTLMEngineException("Unicode unsupported: " + e.getMessage(), e);
1013             }
1014         }
1015 
1016         /**
1017          * Getting the response involves building the message before returning
1018          * it
1019          */
1020         @Override
1021         String getResponse() {
1022             // Now, build the message. Calculate its length first, including
1023             // signature or type.
1024             int finalLength = 32 + 8 /*+ hostBytes.length + domainBytes.length */;
1025 
1026             // Set up the response. This will initialize the signature, message
1027             // type, and flags.
1028             prepareResponse(finalLength, 1);
1029 
1030             // Flags. These are the complete set of flags we support.
1031             addULong(
1032                     //FLAG_WORKSTATION_PRESENT |
1033                     //FLAG_DOMAIN_PRESENT |
1034 
1035                     // Required flags
1036                     //FLAG_REQUEST_LAN_MANAGER_KEY |
1037                     FLAG_REQUEST_NTLMv1 |
1038                     FLAG_REQUEST_NTLM2_SESSION |
1039 
1040                     // Protocol version request
1041                     FLAG_REQUEST_VERSION |
1042 
1043                     // Recommended privacy settings
1044                     FLAG_REQUEST_ALWAYS_SIGN |
1045                     //FLAG_REQUEST_SEAL |
1046                     //FLAG_REQUEST_SIGN |
1047 
1048                     // These must be set according to documentation, based on use of SEAL above
1049                     FLAG_REQUEST_128BIT_KEY_EXCH |
1050                     FLAG_REQUEST_56BIT_ENCRYPTION |
1051                     //FLAG_REQUEST_EXPLICIT_KEY_EXCH |
1052 
1053                     FLAG_REQUEST_UNICODE_ENCODING);
1054 
1055             // Domain length (two times).
1056             addUShort(/*domainBytes.length*/0);
1057             addUShort(/*domainBytes.length*/0);
1058 
1059             // Domain offset.
1060             addULong(/*hostBytes.length +*/ 32 + 8);
1061 
1062             // Host length (two times).
1063             addUShort(/*hostBytes.length*/0);
1064             addUShort(/*hostBytes.length*/0);
1065 
1066             // Host offset (always 32 + 8).
1067             addULong(32 + 8);
1068 
1069             // Version
1070             addUShort(0x0105);
1071             // Build
1072             addULong(2600);
1073             // NTLM revision
1074             addUShort(0x0f00);
1075 
1076 
1077             // Host (workstation) String.
1078             //addBytes(hostBytes);
1079 
1080             // Domain String.
1081             //addBytes(domainBytes);
1082 
1083 
1084             return super.getResponse();
1085         }
1086 
1087     }
1088 
1089     /** Type 2 message class */
1090     static class Type2Message extends NTLMMessage {
1091         protected byte[] challenge;
1092         protected String target;
1093         protected byte[] targetInfo;
1094         protected int flags;
1095 
1096         Type2Message(String message) throws NTLMEngineException {
1097             super(message, 2);
1098 
1099             // Type 2 message is laid out as follows:
1100             // First 8 bytes: NTLMSSP[0]
1101             // Next 4 bytes: Ulong, value 2
1102             // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset)
1103             // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235
1104             // Next 8 bytes, starting at offset 24: Challenge
1105             // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros)
1106             // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset)
1107             // Next 2 bytes, major/minor version number (e.g. 0x05 0x02)
1108             // Next 8 bytes, build number
1109             // Next 2 bytes, protocol version number (e.g. 0x00 0x0f)
1110             // Next, various text fields, and a ushort of value 0 at the end
1111 
1112             // Parse out the rest of the info we need from the message
1113             // The nonce is the 8 bytes starting from the byte in position 24.
1114             challenge = new byte[8];
1115             readBytes(challenge, 24);
1116 
1117             flags = readULong(20);
1118 
1119             if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0)
1120                 throw new NTLMEngineException(
1121                         "NTLM type 2 message has flags that make no sense: "
1122                                 + Integer.toString(flags));
1123 
1124             // Do the target!
1125             target = null;
1126             // The TARGET_DESIRED flag is said to not have understood semantics
1127             // in Type2 messages, so use the length of the packet to decide
1128             // how to proceed instead
1129             if (getMessageLength() >= 12 + 8) {
1130                 byte[] bytes = readSecurityBuffer(12);
1131                 if (bytes.length != 0) {
1132                     try {
1133                         target = new String(bytes, "UnicodeLittleUnmarked");
1134                     } catch (UnsupportedEncodingException e) {
1135                         throw new NTLMEngineException(e.getMessage(), e);
1136                     }
1137                 }
1138             }
1139 
1140             // Do the target info!
1141             targetInfo = null;
1142             // TARGET_DESIRED flag cannot be relied on, so use packet length
1143             if (getMessageLength() >= 40 + 8) {
1144                 byte[] bytes = readSecurityBuffer(40);
1145                 if (bytes.length != 0) {
1146                     targetInfo = bytes;
1147                 }
1148             }
1149         }
1150 
1151         /** Retrieve the challenge */
1152         byte[] getChallenge() {
1153             return challenge;
1154         }
1155 
1156         /** Retrieve the target */
1157         String getTarget() {
1158             return target;
1159         }
1160 
1161         /** Retrieve the target info */
1162         byte[] getTargetInfo() {
1163             return targetInfo;
1164         }
1165 
1166         /** Retrieve the response flags */
1167         int getFlags() {
1168             return flags;
1169         }
1170 
1171     }
1172 
1173     /** Type 3 message assembly class */
1174     static class Type3Message extends NTLMMessage {
1175         // Response flags from the type2 message
1176         protected int type2Flags;
1177 
1178         protected byte[] domainBytes;
1179         protected byte[] hostBytes;
1180         protected byte[] userBytes;
1181 
1182         protected byte[] lmResp;
1183         protected byte[] ntResp;
1184         protected byte[] sessionKey;
1185 
1186 
1187         /** Constructor. Pass the arguments we will need */
1188         Type3Message(String domain, String host, String user, String password, byte[] nonce,
1189                 int type2Flags, String target, byte[] targetInformation)
1190                 throws NTLMEngineException {
1191             // Save the flags
1192             this.type2Flags = type2Flags;
1193 
1194             // Strip off domain name from the host!
1195             String unqualifiedHost = convertHost(host);
1196             // Use only the base domain name!
1197             String unqualifiedDomain = convertDomain(domain);
1198 
1199             // Create a cipher generator class.  Use domain BEFORE it gets modified!
1200             CipherGen gen = new CipherGen(unqualifiedDomain, user, password, nonce, target, targetInformation);
1201 
1202             // Use the new code to calculate the responses, including v2 if that
1203             // seems warranted.
1204             byte[] userSessionKey;
1205             try {
1206                 // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet
1207                 // been tested
1208                 if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) &&
1209                     targetInformation != null && target != null) {
1210                     // NTLMv2
1211                     ntResp = gen.getNTLMv2Response();
1212                     lmResp = gen.getLMv2Response();
1213                     if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0)
1214                         userSessionKey = gen.getLanManagerSessionKey();
1215                     else
1216                         userSessionKey = gen.getNTLMv2UserSessionKey();
1217                 } else {
1218                     // NTLMv1
1219                     if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) {
1220                         // NTLM2 session stuff is requested
1221                         ntResp = gen.getNTLM2SessionResponse();
1222                         lmResp = gen.getLM2SessionResponse();
1223                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0)
1224                             userSessionKey = gen.getLanManagerSessionKey();
1225                         else
1226                             userSessionKey = gen.getNTLM2SessionResponseUserSessionKey();
1227                     } else {
1228                         ntResp = gen.getNTLMResponse();
1229                         lmResp = gen.getLMResponse();
1230                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0)
1231                             userSessionKey = gen.getLanManagerSessionKey();
1232                         else
1233                             userSessionKey = gen.getNTLMUserSessionKey();
1234                     }
1235                 }
1236             } catch (NTLMEngineException e) {
1237                 // This likely means we couldn't find the MD4 hash algorithm -
1238                 // fail back to just using LM
1239                 ntResp = new byte[0];
1240                 lmResp = gen.getLMResponse();
1241                 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0)
1242                     userSessionKey = gen.getLanManagerSessionKey();
1243                 else
1244                     userSessionKey = gen.getLMUserSessionKey();
1245             }
1246 
1247             if ((type2Flags & FLAG_REQUEST_SIGN) != 0) {
1248                 if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0)
1249                     sessionKey = RC4(gen.getSecondaryKey(), userSessionKey);
1250                 else
1251                     sessionKey = userSessionKey;
1252             } else {
1253                 sessionKey = null;
1254             }
1255 
1256             try {
1257                 hostBytes = unqualifiedHost != null ? unqualifiedHost
1258                         .getBytes("UnicodeLittleUnmarked") : null;
1259                 domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1260                         .toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked") : null;
1261                 userBytes = user.getBytes("UnicodeLittleUnmarked");
1262             } catch (UnsupportedEncodingException e) {
1263                 throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e);
1264             }
1265         }
1266 
1267         /** Assemble the response */
1268         @Override
1269         String getResponse() {
1270             int ntRespLen = ntResp.length;
1271             int lmRespLen = lmResp.length;
1272 
1273             int domainLen = domainBytes != null ? domainBytes.length : 0;
1274             int hostLen = hostBytes != null ? hostBytes.length: 0;
1275             int userLen = userBytes.length;
1276             int sessionKeyLen;
1277             if (sessionKey != null)
1278                 sessionKeyLen = sessionKey.length;
1279             else
1280                 sessionKeyLen = 0;
1281 
1282             // Calculate the layout within the packet
1283             int lmRespOffset = 72;  // allocate space for the version
1284             int ntRespOffset = lmRespOffset + lmRespLen;
1285             int domainOffset = ntRespOffset + ntRespLen;
1286             int userOffset = domainOffset + domainLen;
1287             int hostOffset = userOffset + userLen;
1288             int sessionKeyOffset = hostOffset + hostLen;
1289             int finalLength = sessionKeyOffset + sessionKeyLen;
1290 
1291             // Start the response. Length includes signature and type
1292             prepareResponse(finalLength, 3);
1293             
1294             // LM Resp Length (twice)
1295             addUShort(lmRespLen);
1296             addUShort(lmRespLen);
1297 
1298             // LM Resp Offset
1299             addULong(lmRespOffset);
1300 
1301             // NT Resp Length (twice)
1302             addUShort(ntRespLen);
1303             addUShort(ntRespLen);
1304 
1305             // NT Resp Offset
1306             addULong(ntRespOffset);
1307 
1308             // Domain length (twice)
1309             addUShort(domainLen);
1310             addUShort(domainLen);
1311 
1312             // Domain offset.
1313             addULong(domainOffset);
1314 
1315             // User Length (twice)
1316             addUShort(userLen);
1317             addUShort(userLen);
1318 
1319             // User offset
1320             addULong(userOffset);
1321 
1322             // Host length (twice)
1323             addUShort(hostLen);
1324             addUShort(hostLen);
1325 
1326             // Host offset
1327             addULong(hostOffset);
1328 
1329             // Session key length (twice)
1330             addUShort(sessionKeyLen);
1331             addUShort(sessionKeyLen);
1332 
1333             // Session key offset
1334             addULong(sessionKeyOffset);
1335 
1336             // Flags.
1337             addULong(
1338                     //FLAG_WORKSTATION_PRESENT |
1339                     //FLAG_DOMAIN_PRESENT |
1340 
1341                     // Required flags
1342                     (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) |
1343                     (type2Flags & FLAG_REQUEST_NTLMv1) |
1344                     (type2Flags & FLAG_REQUEST_NTLM2_SESSION) |
1345 
1346                     // Protocol version request
1347                     FLAG_REQUEST_VERSION |
1348 
1349                     // Recommended privacy settings
1350                     (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) |
1351                     (type2Flags & FLAG_REQUEST_SEAL) |
1352                     (type2Flags & FLAG_REQUEST_SIGN) |
1353 
1354                     // These must be set according to documentation, based on use of SEAL above
1355                     (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) |
1356                     (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) |
1357                     (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) |
1358 
1359                     (type2Flags & FLAG_TARGETINFO_PRESENT) |
1360                     (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) |
1361                     (type2Flags & FLAG_REQUEST_TARGET)
1362             );
1363 
1364             // Version
1365             addUShort(0x0105);
1366             // Build
1367             addULong(2600);
1368             // NTLM revision
1369             addUShort(0x0f00);
1370 
1371             // Add the actual data
1372             addBytes(lmResp);
1373             addBytes(ntResp);
1374             addBytes(domainBytes);
1375             addBytes(userBytes);
1376             addBytes(hostBytes);
1377             if (sessionKey != null)
1378                 addBytes(sessionKey);
1379 
1380             return super.getResponse();
1381         }
1382     }
1383 
1384     static void writeULong(byte[] buffer, int value, int offset) {
1385         buffer[offset] = (byte) (value & 0xff);
1386         buffer[offset + 1] = (byte) (value >> 8 & 0xff);
1387         buffer[offset + 2] = (byte) (value >> 16 & 0xff);
1388         buffer[offset + 3] = (byte) (value >> 24 & 0xff);
1389     }
1390 
1391     static int F(int x, int y, int z) {
1392         return ((x & y) | (~x & z));
1393     }
1394 
1395     static int G(int x, int y, int z) {
1396         return ((x & y) | (x & z) | (y & z));
1397     }
1398 
1399     static int H(int x, int y, int z) {
1400         return (x ^ y ^ z);
1401     }
1402 
1403     static int rotintlft(int val, int numbits) {
1404         return ((val << numbits) | (val >>> (32 - numbits)));
1405     }
1406 
1407     /**
1408      * Cryptography support - MD4. The following class was based loosely on the
1409      * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java.
1410      * Code correctness was verified by looking at MD4.java from the jcifs
1411      * library (http://jcifs.samba.org). It was massaged extensively to the
1412      * final form found here by Karl Wright (kwright@metacarta.com).
1413      */
1414     static class MD4 {
1415         protected int A = 0x67452301;
1416         protected int B = 0xefcdab89;
1417         protected int C = 0x98badcfe;
1418         protected int D = 0x10325476;
1419         protected long count = 0L;
1420         protected byte[] dataBuffer = new byte[64];
1421 
1422         MD4() {
1423         }
1424 
1425         void update(byte[] input) {
1426             // We always deal with 512 bits at a time. Correspondingly, there is
1427             // a buffer 64 bytes long that we write data into until it gets
1428             // full.
1429             int curBufferPos = (int) (count & 63L);
1430             int inputIndex = 0;
1431             while (input.length - inputIndex + curBufferPos >= dataBuffer.length) {
1432                 // We have enough data to do the next step. Do a partial copy
1433                 // and a transform, updating inputIndex and curBufferPos
1434                 // accordingly
1435                 int transferAmt = dataBuffer.length - curBufferPos;
1436                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1437                 count += transferAmt;
1438                 curBufferPos = 0;
1439                 inputIndex += transferAmt;
1440                 processBuffer();
1441             }
1442 
1443             // If there's anything left, copy it into the buffer and leave it.
1444             // We know there's not enough left to process.
1445             if (inputIndex < input.length) {
1446                 int transferAmt = input.length - inputIndex;
1447                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1448                 count += transferAmt;
1449                 curBufferPos += transferAmt;
1450             }
1451         }
1452 
1453         byte[] getOutput() {
1454             // Feed pad/length data into engine. This must round out the input
1455             // to a multiple of 512 bits.
1456             int bufferIndex = (int) (count & 63L);
1457             int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
1458             byte[] postBytes = new byte[padLen + 8];
1459             // Leading 0x80, specified amount of zero padding, then length in
1460             // bits.
1461             postBytes[0] = (byte) 0x80;
1462             // Fill out the last 8 bytes with the length
1463             for (int i = 0; i < 8; i++) {
1464                 postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i));
1465             }
1466 
1467             // Update the engine
1468             update(postBytes);
1469 
1470             // Calculate final result
1471             byte[] result = new byte[16];
1472             writeULong(result, A, 0);
1473             writeULong(result, B, 4);
1474             writeULong(result, C, 8);
1475             writeULong(result, D, 12);
1476             return result;
1477         }
1478 
1479         protected void processBuffer() {
1480             // Convert current buffer to 16 ulongs
1481             int[] d = new int[16];
1482 
1483             for (int i = 0; i < 16; i++) {
1484                 d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8)
1485                         + ((dataBuffer[i * 4 + 2] & 0xff) << 16)
1486                         + ((dataBuffer[i * 4 + 3] & 0xff) << 24);
1487             }
1488 
1489             // Do a round of processing
1490             int AA = A;
1491             int BB = B;
1492             int CC = C;
1493             int DD = D;
1494             round1(d);
1495             round2(d);
1496             round3(d);
1497             A += AA;
1498             B += BB;
1499             C += CC;
1500             D += DD;
1501 
1502         }
1503 
1504         protected void round1(int[] d) {
1505             A = rotintlft((A + F(B, C, D) + d[0]), 3);
1506             D = rotintlft((D + F(A, B, C) + d[1]), 7);
1507             C = rotintlft((C + F(D, A, B) + d[2]), 11);
1508             B = rotintlft((B + F(C, D, A) + d[3]), 19);
1509 
1510             A = rotintlft((A + F(B, C, D) + d[4]), 3);
1511             D = rotintlft((D + F(A, B, C) + d[5]), 7);
1512             C = rotintlft((C + F(D, A, B) + d[6]), 11);
1513             B = rotintlft((B + F(C, D, A) + d[7]), 19);
1514 
1515             A = rotintlft((A + F(B, C, D) + d[8]), 3);
1516             D = rotintlft((D + F(A, B, C) + d[9]), 7);
1517             C = rotintlft((C + F(D, A, B) + d[10]), 11);
1518             B = rotintlft((B + F(C, D, A) + d[11]), 19);
1519 
1520             A = rotintlft((A + F(B, C, D) + d[12]), 3);
1521             D = rotintlft((D + F(A, B, C) + d[13]), 7);
1522             C = rotintlft((C + F(D, A, B) + d[14]), 11);
1523             B = rotintlft((B + F(C, D, A) + d[15]), 19);
1524         }
1525 
1526         protected void round2(int[] d) {
1527             A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
1528             D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
1529             C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
1530             B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
1531 
1532             A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
1533             D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
1534             C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
1535             B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
1536 
1537             A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
1538             D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
1539             C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
1540             B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
1541 
1542             A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
1543             D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
1544             C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
1545             B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
1546 
1547         }
1548 
1549         protected void round3(int[] d) {
1550             A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
1551             D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
1552             C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
1553             B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
1554 
1555             A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
1556             D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
1557             C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
1558             B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
1559 
1560             A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
1561             D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
1562             C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
1563             B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
1564 
1565             A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
1566             D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
1567             C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
1568             B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
1569 
1570         }
1571 
1572     }
1573 
1574     /**
1575      * Cryptography support - HMACMD5 - algorithmically based on various web
1576      * resources by Karl Wright
1577      */
1578     static class HMACMD5 {
1579         protected byte[] ipad;
1580         protected byte[] opad;
1581         protected MessageDigest md5;
1582 
1583         HMACMD5(byte[] key) throws NTLMEngineException {
1584             try {
1585                 md5 = MessageDigest.getInstance("MD5");
1586             } catch (Exception ex) {
1587                 // Umm, the algorithm doesn't exist - throw an
1588                 // NTLMEngineException!
1589                 throw new NTLMEngineException(
1590                         "Error getting md5 message digest implementation: " + ex.getMessage(), ex);
1591             }
1592 
1593             // Initialize the pad buffers with the key
1594             ipad = new byte[64];
1595             opad = new byte[64];
1596 
1597             int keyLength = key.length;
1598             if (keyLength > 64) {
1599                 // Use MD5 of the key instead, as described in RFC 2104
1600                 md5.update(key);
1601                 key = md5.digest();
1602                 keyLength = key.length;
1603             }
1604             int i = 0;
1605             while (i < keyLength) {
1606                 ipad[i] = (byte) (key[i] ^ (byte) 0x36);
1607                 opad[i] = (byte) (key[i] ^ (byte) 0x5c);
1608                 i++;
1609             }
1610             while (i < 64) {
1611                 ipad[i] = (byte) 0x36;
1612                 opad[i] = (byte) 0x5c;
1613                 i++;
1614             }
1615 
1616             // Very important: update the digest with the ipad buffer
1617             md5.reset();
1618             md5.update(ipad);
1619 
1620         }
1621 
1622         /** Grab the current digest. This is the "answer". */
1623         byte[] getOutput() {
1624             byte[] digest = md5.digest();
1625             md5.update(opad);
1626             return md5.digest(digest);
1627         }
1628 
1629         /** Update by adding a complete array */
1630         void update(byte[] input) {
1631             md5.update(input);
1632         }
1633 
1634         /** Update the algorithm */
1635         void update(byte[] input, int offset, int length) {
1636             md5.update(input, offset, length);
1637         }
1638 
1639     }
1640 
1641     public String generateType1Msg(
1642             final String domain,
1643             final String workstation) throws NTLMEngineException {
1644         return getType1Message(workstation, domain);
1645     }
1646 
1647     public String generateType3Msg(
1648             final String username,
1649             final String password,
1650             final String domain,
1651             final String workstation,
1652             final String challenge) throws NTLMEngineException {
1653         Type2Message t2m = new Type2Message(challenge);
1654         return getType3Message(
1655                 username,
1656                 password,
1657                 workstation,
1658                 domain,
1659                 t2m.getChallenge(),
1660                 t2m.getFlags(),
1661                 t2m.getTarget(),
1662                 t2m.getTargetInfo());
1663     }
1664 
1665 }