View Javadoc

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