View Javadoc

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