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 {@encrypt(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[] clientChallenge = getClientChallenge();
452                 lm2SessionResponse = new byte[24];
453                 System.arraycopy(clientChallenge, 0, lm2SessionResponse, 0, clientChallenge.length);
454                 Arrays.fill(lm2SessionResponse, clientChallenge.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                 final byte[] lmHash = getLMHash();
464                 lmUserSessionKey = new byte[16];
465                 System.arraycopy(lmHash, 0, lmUserSessionKey, 0, 8);
466                 Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00);
467             }
468             return lmUserSessionKey;
469         }
470 
471         /** Get NTLMUserSessionKey */
472         public byte[] getNTLMUserSessionKey()
473             throws NTLMEngineException {
474             if (ntlmUserSessionKey == null) {
475                 final byte[] ntlmHash = getNTLMHash();
476                 final MD4 md4 = new MD4();
477                 md4.update(ntlmHash);
478                 ntlmUserSessionKey = md4.getOutput();
479             }
480             return ntlmUserSessionKey;
481         }
482 
483         /** GetNTLMv2UserSessionKey */
484         public byte[] getNTLMv2UserSessionKey()
485             throws NTLMEngineException {
486             if (ntlmv2UserSessionKey == null) {
487                 final byte[] ntlmv2hash = getNTLMv2Hash();
488                 final byte[] truncatedResponse = new byte[16];
489                 System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16);
490                 ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash);
491             }
492             return ntlmv2UserSessionKey;
493         }
494 
495         /** Get NTLM2SessionResponseUserSessionKey */
496         public byte[] getNTLM2SessionResponseUserSessionKey()
497             throws NTLMEngineException {
498             if (ntlm2SessionResponseUserSessionKey == null) {
499                 final byte[] ntlmUserSessionKey = getNTLMUserSessionKey();
500                 final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse();
501                 final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length];
502                 System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length);
503                 System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length);
504                 ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,ntlmUserSessionKey);
505             }
506             return ntlm2SessionResponseUserSessionKey;
507         }
508 
509         /** Get LAN Manager session key */
510         public byte[] getLanManagerSessionKey()
511             throws NTLMEngineException {
512             if (lanManagerSessionKey == null) {
513                 final byte[] lmHash = getLMHash();
514                 final byte[] lmResponse = getLMResponse();
515                 try {
516                     final byte[] keyBytes = new byte[14];
517                     System.arraycopy(lmHash, 0, keyBytes, 0, 8);
518                     Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd);
519                     final Key lowKey = createDESKey(keyBytes, 0);
520                     final Key highKey = createDESKey(keyBytes, 7);
521                     final byte[] truncatedResponse = new byte[8];
522                     System.arraycopy(lmResponse, 0, truncatedResponse, 0, truncatedResponse.length);
523                     Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
524                     des.init(Cipher.ENCRYPT_MODE, lowKey);
525                     final byte[] lowPart = des.doFinal(truncatedResponse);
526                     des = Cipher.getInstance("DES/ECB/NoPadding");
527                     des.init(Cipher.ENCRYPT_MODE, highKey);
528                     final byte[] highPart = des.doFinal(truncatedResponse);
529                     lanManagerSessionKey = new byte[16];
530                     System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length);
531                     System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length);
532                 } catch (final Exception e) {
533                     throw new NTLMEngineException(e.getMessage(), e);
534                 }
535             }
536             return lanManagerSessionKey;
537         }
538     }
539 
540     /** Calculates HMAC-MD5 */
541     static byte[] hmacMD5(final byte[] value, final byte[] key)
542         throws NTLMEngineException {
543         final HMACMD5 hmacMD5 = new HMACMD5(key);
544         hmacMD5.update(value);
545         return hmacMD5.getOutput();
546     }
547 
548     /** Calculates RC4 */
549     static byte[] RC4(final byte[] value, final byte[] key)
550         throws NTLMEngineException {
551         try {
552             final Cipher rc4 = Cipher.getInstance("RC4");
553             rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4"));
554             return rc4.doFinal(value);
555         } catch (final Exception e) {
556             throw new NTLMEngineException(e.getMessage(), e);
557         }
558     }
559 
560     /**
561      * Calculates the NTLM2 Session Response for the given challenge, using the
562      * specified password and client challenge.
563      *
564      * @return The NTLM2 Session Response. This is placed in the NTLM response
565      *         field of the Type 3 message; the LM response field contains the
566      *         client challenge, null-padded to 24 bytes.
567      */
568     static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge,
569             final byte[] clientChallenge) throws NTLMEngineException {
570         try {
571             // Look up MD5 algorithm (was necessary on jdk 1.4.2)
572             // This used to be needed, but java 1.5.0_07 includes the MD5
573             // algorithm (finally)
574             // Class x = Class.forName("gnu.crypto.hash.MD5");
575             // Method updateMethod = x.getMethod("update",new
576             // Class[]{byte[].class});
577             // Method digestMethod = x.getMethod("digest",new Class[0]);
578             // Object mdInstance = x.newInstance();
579             // updateMethod.invoke(mdInstance,new Object[]{challenge});
580             // updateMethod.invoke(mdInstance,new Object[]{clientChallenge});
581             // byte[] digest = (byte[])digestMethod.invoke(mdInstance,new
582             // Object[0]);
583 
584             final MessageDigest md5 = MessageDigest.getInstance("MD5");
585             md5.update(challenge);
586             md5.update(clientChallenge);
587             final byte[] digest = md5.digest();
588 
589             final byte[] sessionHash = new byte[8];
590             System.arraycopy(digest, 0, sessionHash, 0, 8);
591             return lmResponse(ntlmHash, sessionHash);
592         } catch (final Exception e) {
593             if (e instanceof NTLMEngineException) {
594                 throw (NTLMEngineException) e;
595             }
596             throw new NTLMEngineException(e.getMessage(), e);
597         }
598     }
599 
600     /**
601      * Creates the LM Hash of the user's password.
602      *
603      * @param password
604      *            The password.
605      *
606      * @return The LM Hash of the given password, used in the calculation of the
607      *         LM Response.
608      */
609     private static byte[] lmHash(final String password) throws NTLMEngineException {
610         try {
611             final byte[] oemPassword = password.toUpperCase(Locale.US).getBytes("US-ASCII");
612             final int length = Math.min(oemPassword.length, 14);
613             final byte[] keyBytes = new byte[14];
614             System.arraycopy(oemPassword, 0, keyBytes, 0, length);
615             final Key lowKey = createDESKey(keyBytes, 0);
616             final Key highKey = createDESKey(keyBytes, 7);
617             final byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII");
618             final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
619             des.init(Cipher.ENCRYPT_MODE, lowKey);
620             final byte[] lowHash = des.doFinal(magicConstant);
621             des.init(Cipher.ENCRYPT_MODE, highKey);
622             final byte[] highHash = des.doFinal(magicConstant);
623             final byte[] lmHash = new byte[16];
624             System.arraycopy(lowHash, 0, lmHash, 0, 8);
625             System.arraycopy(highHash, 0, lmHash, 8, 8);
626             return lmHash;
627         } catch (final Exception e) {
628             throw new NTLMEngineException(e.getMessage(), e);
629         }
630     }
631 
632     /**
633      * Creates the NTLM Hash of the user's password.
634      *
635      * @param password
636      *            The password.
637      *
638      * @return The NTLM Hash of the given password, used in the calculation of
639      *         the NTLM Response and the NTLMv2 and LMv2 Hashes.
640      */
641     private static byte[] ntlmHash(final String password) throws NTLMEngineException {
642         try {
643             final byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
644             final MD4 md4 = new MD4();
645             md4.update(unicodePassword);
646             return md4.getOutput();
647         } catch (final UnsupportedEncodingException e) {
648             throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e);
649         }
650     }
651 
652     /**
653      * Creates the LMv2 Hash of the user's password.
654      *
655      * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2
656      *         Responses.
657      */
658     private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash)
659             throws NTLMEngineException {
660         try {
661             final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
662             // Upper case username, upper case domain!
663             hmacMD5.update(user.toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked"));
664             if (domain != null) {
665                 hmacMD5.update(domain.toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked"));
666             }
667             return hmacMD5.getOutput();
668         } catch (final UnsupportedEncodingException e) {
669             throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e);
670         }
671     }
672 
673     /**
674      * Creates the NTLMv2 Hash of the user's password.
675      *
676      * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2
677      *         Responses.
678      */
679     private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash)
680             throws NTLMEngineException {
681         try {
682             final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
683             // Upper case username, mixed case target!!
684             hmacMD5.update(user.toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked"));
685             if (domain != null) {
686                 hmacMD5.update(domain.getBytes("UnicodeLittleUnmarked"));
687             }
688             return hmacMD5.getOutput();
689         } catch (final UnsupportedEncodingException e) {
690             throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e);
691         }
692     }
693 
694     /**
695      * Creates the LM Response from the given hash and Type 2 challenge.
696      *
697      * @param hash
698      *            The LM or NTLM Hash.
699      * @param challenge
700      *            The server challenge from the Type 2 message.
701      *
702      * @return The response (either LM or NTLM, depending on the provided hash).
703      */
704     private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException {
705         try {
706             final byte[] keyBytes = new byte[21];
707             System.arraycopy(hash, 0, keyBytes, 0, 16);
708             final Key lowKey = createDESKey(keyBytes, 0);
709             final Key middleKey = createDESKey(keyBytes, 7);
710             final Key highKey = createDESKey(keyBytes, 14);
711             final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
712             des.init(Cipher.ENCRYPT_MODE, lowKey);
713             final byte[] lowResponse = des.doFinal(challenge);
714             des.init(Cipher.ENCRYPT_MODE, middleKey);
715             final byte[] middleResponse = des.doFinal(challenge);
716             des.init(Cipher.ENCRYPT_MODE, highKey);
717             final byte[] highResponse = des.doFinal(challenge);
718             final byte[] lmResponse = new byte[24];
719             System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
720             System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
721             System.arraycopy(highResponse, 0, lmResponse, 16, 8);
722             return lmResponse;
723         } catch (final Exception e) {
724             throw new NTLMEngineException(e.getMessage(), e);
725         }
726     }
727 
728     /**
729      * Creates the LMv2 Response from the given hash, client data, and Type 2
730      * challenge.
731      *
732      * @param hash
733      *            The NTLMv2 Hash.
734      * @param clientData
735      *            The client data (blob or client challenge).
736      * @param challenge
737      *            The server challenge from the Type 2 message.
738      *
739      * @return The response (either NTLMv2 or LMv2, depending on the client
740      *         data).
741      */
742     private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData)
743             throws NTLMEngineException {
744         final HMACMD5 hmacMD5 = new HMACMD5(hash);
745         hmacMD5.update(challenge);
746         hmacMD5.update(clientData);
747         final byte[] mac = hmacMD5.getOutput();
748         final byte[] lmv2Response = new byte[mac.length + clientData.length];
749         System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
750         System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
751         return lmv2Response;
752     }
753 
754     /**
755      * Creates the NTLMv2 blob from the given target information block and
756      * client challenge.
757      *
758      * @param targetInformation
759      *            The target information block from the Type 2 message.
760      * @param clientChallenge
761      *            The random 8-byte client challenge.
762      *
763      * @return The blob, used in the calculation of the NTLMv2 Response.
764      */
765     private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) {
766         final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
767         final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
768         final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
769         final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
770         final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8
771                 + unknown1.length + targetInformation.length + unknown2.length];
772         int offset = 0;
773         System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
774         offset += blobSignature.length;
775         System.arraycopy(reserved, 0, blob, offset, reserved.length);
776         offset += reserved.length;
777         System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
778         offset += timestamp.length;
779         System.arraycopy(clientChallenge, 0, blob, offset, 8);
780         offset += 8;
781         System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
782         offset += unknown1.length;
783         System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
784         offset += targetInformation.length;
785         System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
786         offset += unknown2.length;
787         return blob;
788     }
789 
790     /**
791      * Creates a DES encryption key from the given key material.
792      *
793      * @param bytes
794      *            A byte array containing the DES key material.
795      * @param offset
796      *            The offset in the given byte array at which the 7-byte key
797      *            material starts.
798      *
799      * @return A DES encryption key created from the key material starting at
800      *         the specified offset in the given byte array.
801      */
802     private static Key createDESKey(final byte[] bytes, final int offset) {
803         final byte[] keyBytes = new byte[7];
804         System.arraycopy(bytes, offset, keyBytes, 0, 7);
805         final byte[] material = new byte[8];
806         material[0] = keyBytes[0];
807         material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
808         material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
809         material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
810         material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
811         material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
812         material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
813         material[7] = (byte) (keyBytes[6] << 1);
814         oddParity(material);
815         return new SecretKeySpec(material, "DES");
816     }
817 
818     /**
819      * Applies odd parity to the given byte array.
820      *
821      * @param bytes
822      *            The data whose parity bits are to be adjusted for odd parity.
823      */
824     private static void oddParity(final byte[] bytes) {
825         for (int i = 0; i < bytes.length; i++) {
826             final byte b = bytes[i];
827             final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3)
828                     ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
829             if (needsParity) {
830                 bytes[i] |= (byte) 0x01;
831             } else {
832                 bytes[i] &= (byte) 0xfe;
833             }
834         }
835     }
836 
837     /** NTLM message generation, base class */
838     static class NTLMMessage {
839         /** The current response */
840         private byte[] messageContents = null;
841 
842         /** The current output position */
843         private int currentOutputPosition = 0;
844 
845         /** Constructor to use when message contents are not yet known */
846         NTLMMessage() {
847         }
848 
849         /** Constructor to use when message contents are known */
850         NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException {
851             messageContents = Base64.decodeBase64(EncodingUtils.getBytes(messageBody,
852                     DEFAULT_CHARSET));
853             // Look for NTLM message
854             if (messageContents.length < SIGNATURE.length) {
855                 throw new NTLMEngineException("NTLM message decoding error - packet too short");
856             }
857             int i = 0;
858             while (i < SIGNATURE.length) {
859                 if (messageContents[i] != SIGNATURE[i]) {
860                     throw new NTLMEngineException(
861                             "NTLM message expected - instead got unrecognized bytes");
862                 }
863                 i++;
864             }
865 
866             // Check to be sure there's a type 2 message indicator next
867             final int type = readULong(SIGNATURE.length);
868             if (type != expectedType) {
869                 throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType)
870                         + " message expected - instead got type " + Integer.toString(type));
871             }
872 
873             currentOutputPosition = messageContents.length;
874         }
875 
876         /**
877          * Get the length of the signature and flags, so calculations can adjust
878          * offsets accordingly.
879          */
880         protected int getPreambleLength() {
881             return SIGNATURE.length + 4;
882         }
883 
884         /** Get the message length */
885         protected int getMessageLength() {
886             return currentOutputPosition;
887         }
888 
889         /** Read a byte from a position within the message buffer */
890         protected byte readByte(final int position) throws NTLMEngineException {
891             if (messageContents.length < position + 1) {
892                 throw new NTLMEngineException("NTLM: Message too short");
893             }
894             return messageContents[position];
895         }
896 
897         /** Read a bunch of bytes from a position in the message buffer */
898         protected void readBytes(final byte[] buffer, final int position) throws NTLMEngineException {
899             if (messageContents.length < position + buffer.length) {
900                 throw new NTLMEngineException("NTLM: Message too short");
901             }
902             System.arraycopy(messageContents, position, buffer, 0, buffer.length);
903         }
904 
905         /** Read a ushort from a position within the message buffer */
906         protected int readUShort(final int position) throws NTLMEngineException {
907             return NTLMEngineImpl.readUShort(messageContents, position);
908         }
909 
910         /** Read a ulong from a position within the message buffer */
911         protected int readULong(final int position) throws NTLMEngineException {
912             return NTLMEngineImpl.readULong(messageContents, position);
913         }
914 
915         /** Read a security buffer from a position within the message buffer */
916         protected byte[] readSecurityBuffer(final int position) throws NTLMEngineException {
917             return NTLMEngineImpl.readSecurityBuffer(messageContents, position);
918         }
919 
920         /**
921          * Prepares the object to create a response of the given length.
922          *
923          * @param maxlength
924          *            the maximum length of the response to prepare, not
925          *            including the type and the signature (which this method
926          *            adds).
927          */
928         protected void prepareResponse(final int maxlength, final int messageType) {
929             messageContents = new byte[maxlength];
930             currentOutputPosition = 0;
931             addBytes(SIGNATURE);
932             addULong(messageType);
933         }
934 
935         /**
936          * Adds the given byte to the response.
937          *
938          * @param b
939          *            the byte to add.
940          */
941         protected void addByte(final byte b) {
942             messageContents[currentOutputPosition] = b;
943             currentOutputPosition++;
944         }
945 
946         /**
947          * Adds the given bytes to the response.
948          *
949          * @param bytes
950          *            the bytes to add.
951          */
952         protected void addBytes(final byte[] bytes) {
953             if (bytes == null) {
954                 return;
955             }
956             for (final byte b : bytes) {
957                 messageContents[currentOutputPosition] = b;
958                 currentOutputPosition++;
959             }
960         }
961 
962         /** Adds a USHORT to the response */
963         protected void addUShort(final int value) {
964             addByte((byte) (value & 0xff));
965             addByte((byte) (value >> 8 & 0xff));
966         }
967 
968         /** Adds a ULong to the response */
969         protected void addULong(final int value) {
970             addByte((byte) (value & 0xff));
971             addByte((byte) (value >> 8 & 0xff));
972             addByte((byte) (value >> 16 & 0xff));
973             addByte((byte) (value >> 24 & 0xff));
974         }
975 
976         /**
977          * Returns the response that has been generated after shrinking the
978          * array if required and base64 encodes the response.
979          *
980          * @return The response as above.
981          */
982         String getResponse() {
983             final byte[] resp;
984             if (messageContents.length > currentOutputPosition) {
985                 final byte[] tmp = new byte[currentOutputPosition];
986                 System.arraycopy(messageContents, 0, tmp, 0, currentOutputPosition);
987                 resp = tmp;
988             } else {
989                 resp = messageContents;
990             }
991             return EncodingUtils.getAsciiString(Base64.encodeBase64(resp));
992         }
993 
994     }
995 
996     /** Type 1 message assembly class */
997     static class Type1Message extends NTLMMessage {
998         protected byte[] hostBytes;
999         protected byte[] domainBytes;
1000 
1001         /** Constructor. Include the arguments the message will need */
1002         Type1Message(final String domain, final String host) throws NTLMEngineException {
1003             super();
1004             try {
1005                 // Strip off domain name from the host!
1006                 final String unqualifiedHost = convertHost(host);
1007                 // Use only the base domain name!
1008                 final String unqualifiedDomain = convertDomain(domain);
1009 
1010                 hostBytes = unqualifiedHost != null? unqualifiedHost.getBytes("ASCII") : null;
1011                 domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1012                         .toUpperCase(Locale.US).getBytes("ASCII") : null;
1013             } catch (final UnsupportedEncodingException e) {
1014                 throw new NTLMEngineException("Unicode unsupported: " + e.getMessage(), e);
1015             }
1016         }
1017 
1018         /**
1019          * Getting the response involves building the message before returning
1020          * it
1021          */
1022         @Override
1023         String getResponse() {
1024             // Now, build the message. Calculate its length first, including
1025             // signature or type.
1026             final int finalLength = 32 + 8 /*+ hostBytes.length + domainBytes.length */;
1027 
1028             // Set up the response. This will initialize the signature, message
1029             // type, and flags.
1030             prepareResponse(finalLength, 1);
1031 
1032             // Flags. These are the complete set of flags we support.
1033             addULong(
1034                     //FLAG_WORKSTATION_PRESENT |
1035                     //FLAG_DOMAIN_PRESENT |
1036 
1037                     // Required flags
1038                     //FLAG_REQUEST_LAN_MANAGER_KEY |
1039                     FLAG_REQUEST_NTLMv1 |
1040                     FLAG_REQUEST_NTLM2_SESSION |
1041 
1042                     // Protocol version request
1043                     FLAG_REQUEST_VERSION |
1044 
1045                     // Recommended privacy settings
1046                     FLAG_REQUEST_ALWAYS_SIGN |
1047                     //FLAG_REQUEST_SEAL |
1048                     //FLAG_REQUEST_SIGN |
1049 
1050                     // These must be set according to documentation, based on use of SEAL above
1051                     FLAG_REQUEST_128BIT_KEY_EXCH |
1052                     FLAG_REQUEST_56BIT_ENCRYPTION |
1053                     //FLAG_REQUEST_EXPLICIT_KEY_EXCH |
1054 
1055                     FLAG_REQUEST_UNICODE_ENCODING);
1056 
1057             // Domain length (two times).
1058             addUShort(/*domainBytes.length*/0);
1059             addUShort(/*domainBytes.length*/0);
1060 
1061             // Domain offset.
1062             addULong(/*hostBytes.length +*/ 32 + 8);
1063 
1064             // Host length (two times).
1065             addUShort(/*hostBytes.length*/0);
1066             addUShort(/*hostBytes.length*/0);
1067 
1068             // Host offset (always 32 + 8).
1069             addULong(32 + 8);
1070 
1071             // Version
1072             addUShort(0x0105);
1073             // Build
1074             addULong(2600);
1075             // NTLM revision
1076             addUShort(0x0f00);
1077 
1078 
1079             // Host (workstation) String.
1080             //addBytes(hostBytes);
1081 
1082             // Domain String.
1083             //addBytes(domainBytes);
1084 
1085 
1086             return super.getResponse();
1087         }
1088 
1089     }
1090 
1091     /** Type 2 message class */
1092     static class Type2Message extends NTLMMessage {
1093         protected byte[] challenge;
1094         protected String target;
1095         protected byte[] targetInfo;
1096         protected int flags;
1097 
1098         Type2Message(final String message) throws NTLMEngineException {
1099             super(message, 2);
1100 
1101             // Type 2 message is laid out as follows:
1102             // First 8 bytes: NTLMSSP[0]
1103             // Next 4 bytes: Ulong, value 2
1104             // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset)
1105             // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235
1106             // Next 8 bytes, starting at offset 24: Challenge
1107             // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros)
1108             // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset)
1109             // Next 2 bytes, major/minor version number (e.g. 0x05 0x02)
1110             // Next 8 bytes, build number
1111             // Next 2 bytes, protocol version number (e.g. 0x00 0x0f)
1112             // Next, various text fields, and a ushort of value 0 at the end
1113 
1114             // Parse out the rest of the info we need from the message
1115             // The nonce is the 8 bytes starting from the byte in position 24.
1116             challenge = new byte[8];
1117             readBytes(challenge, 24);
1118 
1119             flags = readULong(20);
1120 
1121             if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) {
1122                 throw new NTLMEngineException(
1123                         "NTLM type 2 message has flags that make no sense: "
1124                                 + Integer.toString(flags));
1125             }
1126 
1127             // Do the target!
1128             target = null;
1129             // The TARGET_DESIRED flag is said to not have understood semantics
1130             // in Type2 messages, so use the length of the packet to decide
1131             // how to proceed instead
1132             if (getMessageLength() >= 12 + 8) {
1133                 final byte[] bytes = readSecurityBuffer(12);
1134                 if (bytes.length != 0) {
1135                     try {
1136                         target = new String(bytes, "UnicodeLittleUnmarked");
1137                     } catch (final UnsupportedEncodingException e) {
1138                         throw new NTLMEngineException(e.getMessage(), e);
1139                     }
1140                 }
1141             }
1142 
1143             // Do the target info!
1144             targetInfo = null;
1145             // TARGET_DESIRED flag cannot be relied on, so use packet length
1146             if (getMessageLength() >= 40 + 8) {
1147                 final byte[] bytes = readSecurityBuffer(40);
1148                 if (bytes.length != 0) {
1149                     targetInfo = bytes;
1150                 }
1151             }
1152         }
1153 
1154         /** Retrieve the challenge */
1155         byte[] getChallenge() {
1156             return challenge;
1157         }
1158 
1159         /** Retrieve the target */
1160         String getTarget() {
1161             return target;
1162         }
1163 
1164         /** Retrieve the target info */
1165         byte[] getTargetInfo() {
1166             return targetInfo;
1167         }
1168 
1169         /** Retrieve the response flags */
1170         int getFlags() {
1171             return flags;
1172         }
1173 
1174     }
1175 
1176     /** Type 3 message assembly class */
1177     static class Type3Message extends NTLMMessage {
1178         // Response flags from the type2 message
1179         protected int type2Flags;
1180 
1181         protected byte[] domainBytes;
1182         protected byte[] hostBytes;
1183         protected byte[] userBytes;
1184 
1185         protected byte[] lmResp;
1186         protected byte[] ntResp;
1187         protected byte[] sessionKey;
1188 
1189 
1190         /** Constructor. Pass the arguments we will need */
1191         Type3Message(final String domain, final String host, final String user, final String password, final byte[] nonce,
1192                 final int type2Flags, final String target, final byte[] targetInformation)
1193                 throws NTLMEngineException {
1194             // Save the flags
1195             this.type2Flags = type2Flags;
1196 
1197             // Strip off domain name from the host!
1198             final String unqualifiedHost = convertHost(host);
1199             // Use only the base domain name!
1200             final String unqualifiedDomain = convertDomain(domain);
1201 
1202             // Create a cipher generator class.  Use domain BEFORE it gets modified!
1203             final CipherGen gen = new CipherGen(unqualifiedDomain, user, password, nonce, target, targetInformation);
1204 
1205             // Use the new code to calculate the responses, including v2 if that
1206             // seems warranted.
1207             byte[] userSessionKey;
1208             try {
1209                 // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet
1210                 // been tested
1211                 if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) &&
1212                     targetInformation != null && target != null) {
1213                     // NTLMv2
1214                     ntResp = gen.getNTLMv2Response();
1215                     lmResp = gen.getLMv2Response();
1216                     if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1217                         userSessionKey = gen.getLanManagerSessionKey();
1218                     } else {
1219                         userSessionKey = gen.getNTLMv2UserSessionKey();
1220                     }
1221                 } else {
1222                     // NTLMv1
1223                     if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) {
1224                         // NTLM2 session stuff is requested
1225                         ntResp = gen.getNTLM2SessionResponse();
1226                         lmResp = gen.getLM2SessionResponse();
1227                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1228                             userSessionKey = gen.getLanManagerSessionKey();
1229                         } else {
1230                             userSessionKey = gen.getNTLM2SessionResponseUserSessionKey();
1231                         }
1232                     } else {
1233                         ntResp = gen.getNTLMResponse();
1234                         lmResp = gen.getLMResponse();
1235                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1236                             userSessionKey = gen.getLanManagerSessionKey();
1237                         } else {
1238                             userSessionKey = gen.getNTLMUserSessionKey();
1239                         }
1240                     }
1241                 }
1242             } catch (final NTLMEngineException e) {
1243                 // This likely means we couldn't find the MD4 hash algorithm -
1244                 // fail back to just using LM
1245                 ntResp = new byte[0];
1246                 lmResp = gen.getLMResponse();
1247                 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1248                     userSessionKey = gen.getLanManagerSessionKey();
1249                 } else {
1250                     userSessionKey = gen.getLMUserSessionKey();
1251                 }
1252             }
1253 
1254             if ((type2Flags & FLAG_REQUEST_SIGN) != 0) {
1255                 if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) {
1256                     sessionKey = RC4(gen.getSecondaryKey(), userSessionKey);
1257                 } else {
1258                     sessionKey = userSessionKey;
1259                 }
1260             } else {
1261                 sessionKey = null;
1262             }
1263 
1264             try {
1265                 hostBytes = unqualifiedHost != null ? unqualifiedHost
1266                         .getBytes("UnicodeLittleUnmarked") : null;
1267                 domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1268                         .toUpperCase(Locale.US).getBytes("UnicodeLittleUnmarked") : null;
1269                 userBytes = user.getBytes("UnicodeLittleUnmarked");
1270             } catch (final UnsupportedEncodingException e) {
1271                 throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e);
1272             }
1273         }
1274 
1275         /** Assemble the response */
1276         @Override
1277         String getResponse() {
1278             final int ntRespLen = ntResp.length;
1279             final int lmRespLen = lmResp.length;
1280 
1281             final int domainLen = domainBytes != null ? domainBytes.length : 0;
1282             final int hostLen = hostBytes != null ? hostBytes.length: 0;
1283             final int userLen = userBytes.length;
1284             final int sessionKeyLen;
1285             if (sessionKey != null) {
1286                 sessionKeyLen = sessionKey.length;
1287             } else {
1288                 sessionKeyLen = 0;
1289             }
1290 
1291             // Calculate the layout within the packet
1292             final int lmRespOffset = 72;  // allocate space for the version
1293             final int ntRespOffset = lmRespOffset + lmRespLen;
1294             final int domainOffset = ntRespOffset + ntRespLen;
1295             final int userOffset = domainOffset + domainLen;
1296             final int hostOffset = userOffset + userLen;
1297             final int sessionKeyOffset = hostOffset + hostLen;
1298             final int finalLength = sessionKeyOffset + sessionKeyLen;
1299 
1300             // Start the response. Length includes signature and type
1301             prepareResponse(finalLength, 3);
1302 
1303             // LM Resp Length (twice)
1304             addUShort(lmRespLen);
1305             addUShort(lmRespLen);
1306 
1307             // LM Resp Offset
1308             addULong(lmRespOffset);
1309 
1310             // NT Resp Length (twice)
1311             addUShort(ntRespLen);
1312             addUShort(ntRespLen);
1313 
1314             // NT Resp Offset
1315             addULong(ntRespOffset);
1316 
1317             // Domain length (twice)
1318             addUShort(domainLen);
1319             addUShort(domainLen);
1320 
1321             // Domain offset.
1322             addULong(domainOffset);
1323 
1324             // User Length (twice)
1325             addUShort(userLen);
1326             addUShort(userLen);
1327 
1328             // User offset
1329             addULong(userOffset);
1330 
1331             // Host length (twice)
1332             addUShort(hostLen);
1333             addUShort(hostLen);
1334 
1335             // Host offset
1336             addULong(hostOffset);
1337 
1338             // Session key length (twice)
1339             addUShort(sessionKeyLen);
1340             addUShort(sessionKeyLen);
1341 
1342             // Session key offset
1343             addULong(sessionKeyOffset);
1344 
1345             // Flags.
1346             addULong(
1347                     //FLAG_WORKSTATION_PRESENT |
1348                     //FLAG_DOMAIN_PRESENT |
1349 
1350                     // Required flags
1351                     (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) |
1352                     (type2Flags & FLAG_REQUEST_NTLMv1) |
1353                     (type2Flags & FLAG_REQUEST_NTLM2_SESSION) |
1354 
1355                     // Protocol version request
1356                     FLAG_REQUEST_VERSION |
1357 
1358                     // Recommended privacy settings
1359                     (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) |
1360                     (type2Flags & FLAG_REQUEST_SEAL) |
1361                     (type2Flags & FLAG_REQUEST_SIGN) |
1362 
1363                     // These must be set according to documentation, based on use of SEAL above
1364                     (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) |
1365                     (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) |
1366                     (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) |
1367 
1368                     (type2Flags & FLAG_TARGETINFO_PRESENT) |
1369                     (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) |
1370                     (type2Flags & FLAG_REQUEST_TARGET)
1371             );
1372 
1373             // Version
1374             addUShort(0x0105);
1375             // Build
1376             addULong(2600);
1377             // NTLM revision
1378             addUShort(0x0f00);
1379 
1380             // Add the actual data
1381             addBytes(lmResp);
1382             addBytes(ntResp);
1383             addBytes(domainBytes);
1384             addBytes(userBytes);
1385             addBytes(hostBytes);
1386             if (sessionKey != null) {
1387                 addBytes(sessionKey);
1388             }
1389 
1390             return super.getResponse();
1391         }
1392     }
1393 
1394     static void writeULong(final byte[] buffer, final int value, final int offset) {
1395         buffer[offset] = (byte) (value & 0xff);
1396         buffer[offset + 1] = (byte) (value >> 8 & 0xff);
1397         buffer[offset + 2] = (byte) (value >> 16 & 0xff);
1398         buffer[offset + 3] = (byte) (value >> 24 & 0xff);
1399     }
1400 
1401     static int F(final int x, final int y, final int z) {
1402         return ((x & y) | (~x & z));
1403     }
1404 
1405     static int G(final int x, final int y, final int z) {
1406         return ((x & y) | (x & z) | (y & z));
1407     }
1408 
1409     static int H(final int x, final int y, final int z) {
1410         return (x ^ y ^ z);
1411     }
1412 
1413     static int rotintlft(final int val, final int numbits) {
1414         return ((val << numbits) | (val >>> (32 - numbits)));
1415     }
1416 
1417     /**
1418      * Cryptography support - MD4. The following class was based loosely on the
1419      * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java.
1420      * Code correctness was verified by looking at MD4.java from the jcifs
1421      * library (http://jcifs.samba.org). It was massaged extensively to the
1422      * final form found here by Karl Wright (kwright@metacarta.com).
1423      */
1424     static class MD4 {
1425         protected int A = 0x67452301;
1426         protected int B = 0xefcdab89;
1427         protected int C = 0x98badcfe;
1428         protected int D = 0x10325476;
1429         protected long count = 0L;
1430         protected byte[] dataBuffer = new byte[64];
1431 
1432         MD4() {
1433         }
1434 
1435         void update(final byte[] input) {
1436             // We always deal with 512 bits at a time. Correspondingly, there is
1437             // a buffer 64 bytes long that we write data into until it gets
1438             // full.
1439             int curBufferPos = (int) (count & 63L);
1440             int inputIndex = 0;
1441             while (input.length - inputIndex + curBufferPos >= dataBuffer.length) {
1442                 // We have enough data to do the next step. Do a partial copy
1443                 // and a transform, updating inputIndex and curBufferPos
1444                 // accordingly
1445                 final int transferAmt = dataBuffer.length - curBufferPos;
1446                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1447                 count += transferAmt;
1448                 curBufferPos = 0;
1449                 inputIndex += transferAmt;
1450                 processBuffer();
1451             }
1452 
1453             // If there's anything left, copy it into the buffer and leave it.
1454             // We know there's not enough left to process.
1455             if (inputIndex < input.length) {
1456                 final int transferAmt = input.length - inputIndex;
1457                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1458                 count += transferAmt;
1459                 curBufferPos += transferAmt;
1460             }
1461         }
1462 
1463         byte[] getOutput() {
1464             // Feed pad/length data into engine. This must round out the input
1465             // to a multiple of 512 bits.
1466             final int bufferIndex = (int) (count & 63L);
1467             final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
1468             final byte[] postBytes = new byte[padLen + 8];
1469             // Leading 0x80, specified amount of zero padding, then length in
1470             // bits.
1471             postBytes[0] = (byte) 0x80;
1472             // Fill out the last 8 bytes with the length
1473             for (int i = 0; i < 8; i++) {
1474                 postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i));
1475             }
1476 
1477             // Update the engine
1478             update(postBytes);
1479 
1480             // Calculate final result
1481             final byte[] result = new byte[16];
1482             writeULong(result, A, 0);
1483             writeULong(result, B, 4);
1484             writeULong(result, C, 8);
1485             writeULong(result, D, 12);
1486             return result;
1487         }
1488 
1489         protected void processBuffer() {
1490             // Convert current buffer to 16 ulongs
1491             final int[] d = new int[16];
1492 
1493             for (int i = 0; i < 16; i++) {
1494                 d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8)
1495                         + ((dataBuffer[i * 4 + 2] & 0xff) << 16)
1496                         + ((dataBuffer[i * 4 + 3] & 0xff) << 24);
1497             }
1498 
1499             // Do a round of processing
1500             final int AA = A;
1501             final int BB = B;
1502             final int CC = C;
1503             final int DD = D;
1504             round1(d);
1505             round2(d);
1506             round3(d);
1507             A += AA;
1508             B += BB;
1509             C += CC;
1510             D += DD;
1511 
1512         }
1513 
1514         protected void round1(final int[] d) {
1515             A = rotintlft((A + F(B, C, D) + d[0]), 3);
1516             D = rotintlft((D + F(A, B, C) + d[1]), 7);
1517             C = rotintlft((C + F(D, A, B) + d[2]), 11);
1518             B = rotintlft((B + F(C, D, A) + d[3]), 19);
1519 
1520             A = rotintlft((A + F(B, C, D) + d[4]), 3);
1521             D = rotintlft((D + F(A, B, C) + d[5]), 7);
1522             C = rotintlft((C + F(D, A, B) + d[6]), 11);
1523             B = rotintlft((B + F(C, D, A) + d[7]), 19);
1524 
1525             A = rotintlft((A + F(B, C, D) + d[8]), 3);
1526             D = rotintlft((D + F(A, B, C) + d[9]), 7);
1527             C = rotintlft((C + F(D, A, B) + d[10]), 11);
1528             B = rotintlft((B + F(C, D, A) + d[11]), 19);
1529 
1530             A = rotintlft((A + F(B, C, D) + d[12]), 3);
1531             D = rotintlft((D + F(A, B, C) + d[13]), 7);
1532             C = rotintlft((C + F(D, A, B) + d[14]), 11);
1533             B = rotintlft((B + F(C, D, A) + d[15]), 19);
1534         }
1535 
1536         protected void round2(final int[] d) {
1537             A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
1538             D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
1539             C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
1540             B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
1541 
1542             A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
1543             D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
1544             C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
1545             B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
1546 
1547             A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
1548             D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
1549             C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
1550             B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
1551 
1552             A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
1553             D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
1554             C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
1555             B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
1556 
1557         }
1558 
1559         protected void round3(final int[] d) {
1560             A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
1561             D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
1562             C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
1563             B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
1564 
1565             A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
1566             D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
1567             C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
1568             B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
1569 
1570             A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
1571             D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
1572             C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
1573             B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
1574 
1575             A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
1576             D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
1577             C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
1578             B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
1579 
1580         }
1581 
1582     }
1583 
1584     /**
1585      * Cryptography support - HMACMD5 - algorithmically based on various web
1586      * resources by Karl Wright
1587      */
1588     static class HMACMD5 {
1589         protected byte[] ipad;
1590         protected byte[] opad;
1591         protected MessageDigest md5;
1592 
1593         HMACMD5(final byte[] input) throws NTLMEngineException {
1594             byte[] key = input;
1595             try {
1596                 md5 = MessageDigest.getInstance("MD5");
1597             } catch (final Exception ex) {
1598                 // Umm, the algorithm doesn't exist - throw an
1599                 // NTLMEngineException!
1600                 throw new NTLMEngineException(
1601                         "Error getting md5 message digest implementation: " + ex.getMessage(), ex);
1602             }
1603 
1604             // Initialize the pad buffers with the key
1605             ipad = new byte[64];
1606             opad = new byte[64];
1607 
1608             int keyLength = key.length;
1609             if (keyLength > 64) {
1610                 // Use MD5 of the key instead, as described in RFC 2104
1611                 md5.update(key);
1612                 key = md5.digest();
1613                 keyLength = key.length;
1614             }
1615             int i = 0;
1616             while (i < keyLength) {
1617                 ipad[i] = (byte) (key[i] ^ (byte) 0x36);
1618                 opad[i] = (byte) (key[i] ^ (byte) 0x5c);
1619                 i++;
1620             }
1621             while (i < 64) {
1622                 ipad[i] = (byte) 0x36;
1623                 opad[i] = (byte) 0x5c;
1624                 i++;
1625             }
1626 
1627             // Very important: update the digest with the ipad buffer
1628             md5.reset();
1629             md5.update(ipad);
1630 
1631         }
1632 
1633         /** Grab the current digest. This is the "answer". */
1634         byte[] getOutput() {
1635             final byte[] digest = md5.digest();
1636             md5.update(opad);
1637             return md5.digest(digest);
1638         }
1639 
1640         /** Update by adding a complete array */
1641         void update(final byte[] input) {
1642             md5.update(input);
1643         }
1644 
1645         /** Update the algorithm */
1646         void update(final byte[] input, final int offset, final int length) {
1647             md5.update(input, offset, length);
1648         }
1649 
1650     }
1651 
1652     public String generateType1Msg(
1653             final String domain,
1654             final String workstation) throws NTLMEngineException {
1655         return getType1Message(workstation, domain);
1656     }
1657 
1658     public String generateType3Msg(
1659             final String username,
1660             final String password,
1661             final String domain,
1662             final String workstation,
1663             final String challenge) throws NTLMEngineException {
1664         final Type2Message t2m = new Type2Message(challenge);
1665         return getType3Message(
1666                 username,
1667                 password,
1668                 workstation,
1669                 domain,
1670                 t2m.getChallenge(),
1671                 t2m.getFlags(),
1672                 t2m.getTarget(),
1673                 t2m.getTargetInfo());
1674     }
1675 
1676 }