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