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