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