1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  package org.apache.http.impl.auth;
28  
29  import java.nio.charset.Charset;
30  import java.security.Key;
31  import java.security.MessageDigest;
32  import java.security.NoSuchAlgorithmException;
33  import java.security.cert.Certificate;
34  import java.security.cert.CertificateEncodingException;
35  import java.util.Arrays;
36  import java.util.Locale;
37  import java.util.Random;
38  
39  import javax.crypto.Cipher;
40  import javax.crypto.spec.SecretKeySpec;
41  
42  import org.apache.commons.codec.binary.Base64;
43  import org.apache.http.Consts;
44  
45  
46  
47  
48  
49  
50  
51  final class NTLMEngineImpl implements NTLMEngine {
52  
53      
54      private static final Charset UNICODE_LITTLE_UNMARKED = Charset.forName("UnicodeLittleUnmarked");
55      
56      private static final Charset DEFAULT_CHARSET = Consts.ASCII;
57  
58      
59      
60      
61      
62      
63      static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001;      
64      static final int FLAG_REQUEST_OEM_ENCODING = 0x00000002;      
65      static final int FLAG_REQUEST_TARGET = 0x00000004;                      
66      static final int FLAG_REQUEST_SIGN = 0x00000010;  
67      static final int FLAG_REQUEST_SEAL = 0x00000020;  
68      static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080;    
69      static final int FLAG_REQUEST_NTLMv1 = 0x00000200; 
70      static final int FLAG_DOMAIN_PRESENT = 0x00001000;        
71      static final int FLAG_WORKSTATION_PRESENT = 0x00002000;   
72      static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000;   
73      static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; 
74      static final int FLAG_REQUEST_VERSION = 0x02000000;       
75      static final int FLAG_TARGETINFO_PRESENT = 0x00800000;    
76      static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; 
77      static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000;     
78      static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000;      
79  
80      
81      
82      static final int MSV_AV_EOL = 0x0000; 
83      static final int MSV_AV_NB_COMPUTER_NAME = 0x0001; 
84      static final int MSV_AV_NB_DOMAIN_NAME = 0x0002; 
85      static final int MSV_AV_DNS_COMPUTER_NAME = 0x0003; 
86      static final int MSV_AV_DNS_DOMAIN_NAME = 0x0004; 
87      static final int MSV_AV_DNS_TREE_NAME = 0x0005; 
88      static final int MSV_AV_FLAGS = 0x0006; 
89      static final int MSV_AV_TIMESTAMP = 0x0007; 
90      static final int MSV_AV_SINGLE_HOST = 0x0008; 
91      static final int MSV_AV_TARGET_NAME = 0x0009; 
92      static final int MSV_AV_CHANNEL_BINDINGS = 0x000A; 
93  
94      static final int MSV_AV_FLAGS_ACCOUNT_AUTH_CONSTAINED = 0x00000001; 
95      static final int MSV_AV_FLAGS_MIC = 0x00000002; 
96      static final int MSV_AV_FLAGS_UNTRUSTED_TARGET_SPN = 0x00000004; 
97  
98      
99      private static final java.security.SecureRandom RND_GEN;
100     static {
101         java.security.SecureRandom rnd = null;
102         try {
103             rnd = java.security.SecureRandom.getInstance("SHA1PRNG");
104         } catch (final Exception ignore) {
105         }
106         RND_GEN = rnd;
107     }
108 
109     
110     private static final byte[] SIGNATURE = getNullTerminatedAsciiString("NTLMSSP");
111 
112     
113     
114     private static final byte[] SIGN_MAGIC_SERVER = getNullTerminatedAsciiString(
115         "session key to server-to-client signing key magic constant");
116     private static final byte[] SIGN_MAGIC_CLIENT = getNullTerminatedAsciiString(
117         "session key to client-to-server signing key magic constant");
118     private static final byte[] SEAL_MAGIC_SERVER = getNullTerminatedAsciiString(
119         "session key to server-to-client sealing key magic constant");
120     private static final byte[] SEAL_MAGIC_CLIENT = getNullTerminatedAsciiString(
121         "session key to client-to-server sealing key magic constant");
122 
123     
124     private static final byte[] MAGIC_TLS_SERVER_ENDPOINT = "tls-server-end-point:".getBytes(Consts.ASCII);
125 
126     private static byte[] getNullTerminatedAsciiString( final String source )
127     {
128         final byte[] bytesWithoutNull = source.getBytes(Consts.ASCII);
129         final byte[] target = new byte[bytesWithoutNull.length + 1];
130         System.arraycopy(bytesWithoutNull, 0, target, 0, bytesWithoutNull.length);
131         target[bytesWithoutNull.length] = (byte) 0x00;
132         return target;
133     }
134 
135     private static final String TYPE_1_MESSAGE = new Type1Message().getResponse();
136 
137     NTLMEngineImpl() {
138     }
139 
140     
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151     static String getType1Message(final String host, final String domain) {
152         
153         
154         return TYPE_1_MESSAGE;
155     }
156 
157     
158 
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 
174 
175 
176 
177 
178     static String getType3Message(final String user, final String password, final String host, final String domain,
179             final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation)
180             throws NTLMEngineException {
181         return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
182                 targetInformation).getResponse();
183     }
184 
185     
186 
187 
188 
189 
190 
191 
192 
193 
194 
195 
196 
197 
198 
199 
200 
201 
202 
203 
204 
205 
206     static String getType3Message(final String user, final String password, final String host, final String domain,
207             final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation,
208             final Certificate peerServerCertificate, final byte[] type1Message, final byte[] type2Message)
209             throws NTLMEngineException {
210         return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
211                 targetInformation, peerServerCertificate, type1Message, type2Message).getResponse();
212     }
213 
214     private static int readULong(final byte[] src, final int index) {
215         if (src.length < index + 4) {
216             return 0;
217         }
218         return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8)
219                 | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24);
220     }
221 
222     private static int readUShort(final byte[] src, final int index) {
223         if (src.length < index + 2) {
224             return 0;
225         }
226         return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);
227     }
228 
229     private static byte[] readSecurityBuffer(final byte[] src, final int index) {
230         final int length = readUShort(src, index);
231         final int offset = readULong(src, index + 4);
232         if (src.length < offset + length) {
233             return new byte[length];
234         }
235         final byte[] buffer = new byte[length];
236         System.arraycopy(src, offset, buffer, 0, length);
237         return buffer;
238     }
239 
240     
241     private static byte[] makeRandomChallenge(final Random random) {
242         final byte[] rval = new byte[8];
243         synchronized (random) {
244             random.nextBytes(rval);
245         }
246         return rval;
247     }
248 
249     
250     private static byte[] makeSecondaryKey(final Random random) {
251         final byte[] rval = new byte[16];
252         synchronized (random) {
253             random.nextBytes(rval);
254         }
255         return rval;
256     }
257 
258     protected static class CipherGen {
259 
260         protected final Random random;
261         protected final long currentTime;
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         
271         protected byte[] clientChallenge;
272         protected byte[] clientChallenge2;
273         protected byte[] secondaryKey;
274         protected byte[] timestamp;
275 
276         
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 
294         
295 
296 
297         @Deprecated
298         public CipherGen(final String domain, final String user, final String password,
299             final byte[] challenge, final String target, final byte[] targetInformation,
300             final byte[] clientChallenge, final byte[] clientChallenge2,
301             final byte[] secondaryKey, final byte[] timestamp) {
302             this(RND_GEN, System.currentTimeMillis(),
303                 domain, user, password, challenge, target, targetInformation,
304                 clientChallenge, clientChallenge2,
305                 secondaryKey, timestamp);
306         }
307 
308         public CipherGen(final Random random, final long currentTime,
309             final String domain, final String user, final String password,
310             final byte[] challenge, final String target, final byte[] targetInformation,
311             final byte[] clientChallenge, final byte[] clientChallenge2,
312             final byte[] secondaryKey, final byte[] timestamp) {
313             this.random = random;
314             this.currentTime = currentTime;
315 
316             this.domain = domain;
317             this.target = target;
318             this.user = user;
319             this.password = password;
320             this.challenge = challenge;
321             this.targetInformation = targetInformation;
322             this.clientChallenge = clientChallenge;
323             this.clientChallenge2 = clientChallenge2;
324             this.secondaryKey = secondaryKey;
325             this.timestamp = timestamp;
326         }
327 
328         
329 
330 
331         @Deprecated
332         public CipherGen(final String domain,
333             final String user,
334             final String password,
335             final byte[] challenge,
336             final String target,
337             final byte[] targetInformation) {
338             this(RND_GEN, System.currentTimeMillis(), domain, user, password, challenge, target, targetInformation);
339         }
340 
341         public CipherGen(final Random random, final long currentTime,
342             final String domain,
343             final String user,
344             final String password,
345             final byte[] challenge,
346             final String target,
347             final byte[] targetInformation) {
348             this(random, currentTime, domain, user, password, challenge, target, targetInformation, null, null, null, null);
349         }
350 
351         
352         public byte[] getClientChallenge()
353             throws NTLMEngineException {
354             if (clientChallenge == null) {
355                 clientChallenge = makeRandomChallenge(random);
356             }
357             return clientChallenge;
358         }
359 
360         
361         public byte[] getClientChallenge2()
362             throws NTLMEngineException {
363             if (clientChallenge2 == null) {
364                 clientChallenge2 = makeRandomChallenge(random);
365             }
366             return clientChallenge2;
367         }
368 
369         
370         public byte[] getSecondaryKey()
371             throws NTLMEngineException {
372             if (secondaryKey == null) {
373                 secondaryKey = makeSecondaryKey(random);
374             }
375             return secondaryKey;
376         }
377 
378         
379         public byte[] getLMHash()
380             throws NTLMEngineException {
381             if (lmHash == null) {
382                 lmHash = lmHash(password);
383             }
384             return lmHash;
385         }
386 
387         
388         public byte[] getLMResponse()
389             throws NTLMEngineException {
390             if (lmResponse == null) {
391                 lmResponse = lmResponse(getLMHash(),challenge);
392             }
393             return lmResponse;
394         }
395 
396         
397         public byte[] getNTLMHash()
398             throws NTLMEngineException {
399             if (ntlmHash == null) {
400                 ntlmHash = ntlmHash(password);
401             }
402             return ntlmHash;
403         }
404 
405         
406         public byte[] getNTLMResponse()
407             throws NTLMEngineException {
408             if (ntlmResponse == null) {
409                 ntlmResponse = lmResponse(getNTLMHash(),challenge);
410             }
411             return ntlmResponse;
412         }
413 
414         
415         public byte[] getLMv2Hash()
416             throws NTLMEngineException {
417             if (lmv2Hash == null) {
418                 lmv2Hash = lmv2Hash(domain, user, getNTLMHash());
419             }
420             return lmv2Hash;
421         }
422 
423         
424         public byte[] getNTLMv2Hash()
425             throws NTLMEngineException {
426             if (ntlmv2Hash == null) {
427                 ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash());
428             }
429             return ntlmv2Hash;
430         }
431 
432         
433         public byte[] getTimestamp() {
434             if (timestamp == null) {
435                 long time = this.currentTime;
436                 time += 11644473600000L; 
437                 time *= 10000; 
438                 
439                 timestamp = new byte[8];
440                 for (int i = 0; i < 8; i++) {
441                     timestamp[i] = (byte) time;
442                     time >>>= 8;
443                 }
444             }
445             return timestamp;
446         }
447 
448         
449         public byte[] getNTLMv2Blob()
450             throws NTLMEngineException {
451             if (ntlmv2Blob == null) {
452                 ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp());
453             }
454             return ntlmv2Blob;
455         }
456 
457         
458         public byte[] getNTLMv2Response()
459             throws NTLMEngineException {
460             if (ntlmv2Response == null) {
461                 ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob());
462             }
463             return ntlmv2Response;
464         }
465 
466         
467         public byte[] getLMv2Response()
468             throws NTLMEngineException {
469             if (lmv2Response == null) {
470                 lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge());
471             }
472             return lmv2Response;
473         }
474 
475         
476         public byte[] getNTLM2SessionResponse()
477             throws NTLMEngineException {
478             if (ntlm2SessionResponse == null) {
479                 ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge());
480             }
481             return ntlm2SessionResponse;
482         }
483 
484         
485         public byte[] getLM2SessionResponse()
486             throws NTLMEngineException {
487             if (lm2SessionResponse == null) {
488                 final byte[] clntChallenge = getClientChallenge();
489                 lm2SessionResponse = new byte[24];
490                 System.arraycopy(clntChallenge, 0, lm2SessionResponse, 0, clntChallenge.length);
491                 Arrays.fill(lm2SessionResponse, clntChallenge.length, lm2SessionResponse.length, (byte) 0x00);
492             }
493             return lm2SessionResponse;
494         }
495 
496         
497         public byte[] getLMUserSessionKey()
498             throws NTLMEngineException {
499             if (lmUserSessionKey == null) {
500                 lmUserSessionKey = new byte[16];
501                 System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8);
502                 Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00);
503             }
504             return lmUserSessionKey;
505         }
506 
507         
508         public byte[] getNTLMUserSessionKey()
509             throws NTLMEngineException {
510             if (ntlmUserSessionKey == null) {
511                 final MD4 md4 = new MD4();
512                 md4.update(getNTLMHash());
513                 ntlmUserSessionKey = md4.getOutput();
514             }
515             return ntlmUserSessionKey;
516         }
517 
518         
519         public byte[] getNTLMv2UserSessionKey()
520             throws NTLMEngineException {
521             if (ntlmv2UserSessionKey == null) {
522                 final byte[] ntlmv2hash = getNTLMv2Hash();
523                 final byte[] truncatedResponse = new byte[16];
524                 System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16);
525                 ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash);
526             }
527             return ntlmv2UserSessionKey;
528         }
529 
530         
531         public byte[] getNTLM2SessionResponseUserSessionKey()
532             throws NTLMEngineException {
533             if (ntlm2SessionResponseUserSessionKey == null) {
534                 final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse();
535                 final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length];
536                 System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length);
537                 System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length);
538                 ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,getNTLMUserSessionKey());
539             }
540             return ntlm2SessionResponseUserSessionKey;
541         }
542 
543         
544         public byte[] getLanManagerSessionKey()
545             throws NTLMEngineException {
546             if (lanManagerSessionKey == null) {
547                 try {
548                     final byte[] keyBytes = new byte[14];
549                     System.arraycopy(getLMHash(), 0, keyBytes, 0, 8);
550                     Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd);
551                     final Key lowKey = createDESKey(keyBytes, 0);
552                     final Key highKey = createDESKey(keyBytes, 7);
553                     final byte[] truncatedResponse = new byte[8];
554                     System.arraycopy(getLMResponse(), 0, truncatedResponse, 0, truncatedResponse.length);
555                     Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
556                     des.init(Cipher.ENCRYPT_MODE, lowKey);
557                     final byte[] lowPart = des.doFinal(truncatedResponse);
558                     des = Cipher.getInstance("DES/ECB/NoPadding");
559                     des.init(Cipher.ENCRYPT_MODE, highKey);
560                     final byte[] highPart = des.doFinal(truncatedResponse);
561                     lanManagerSessionKey = new byte[16];
562                     System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length);
563                     System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length);
564                 } catch (final Exception e) {
565                     throw new NTLMEngineException(e.getMessage(), e);
566                 }
567             }
568             return lanManagerSessionKey;
569         }
570     }
571 
572     
573     static byte[] hmacMD5(final byte[] value, final byte[] key)
574         throws NTLMEngineException {
575         final HMACMD5 hmacMD5 = new HMACMD5(key);
576         hmacMD5.update(value);
577         return hmacMD5.getOutput();
578     }
579 
580     
581     static byte[] RC4(final byte[] value, final byte[] key)
582         throws NTLMEngineException {
583         try {
584             final Cipher rc4 = Cipher.getInstance("RC4");
585             rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4"));
586             return rc4.doFinal(value);
587         } catch (final Exception e) {
588             throw new NTLMEngineException(e.getMessage(), e);
589         }
590     }
591 
592     
593 
594 
595 
596 
597 
598 
599 
600     static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge,
601             final byte[] clientChallenge) throws NTLMEngineException {
602         try {
603             final MessageDigest md5 = getMD5();
604             md5.update(challenge);
605             md5.update(clientChallenge);
606             final byte[] digest = md5.digest();
607 
608             final byte[] sessionHash = new byte[8];
609             System.arraycopy(digest, 0, sessionHash, 0, 8);
610             return lmResponse(ntlmHash, sessionHash);
611         } catch (final Exception e) {
612             if (e instanceof NTLMEngineException) {
613                 throw (NTLMEngineException) e;
614             }
615             throw new NTLMEngineException(e.getMessage(), e);
616         }
617     }
618 
619     
620 
621 
622 
623 
624 
625 
626 
627 
628     private static byte[] lmHash(final String password) throws NTLMEngineException {
629         try {
630             final byte[] oemPassword = password.toUpperCase(Locale.ROOT).getBytes(Consts.ASCII);
631             final int length = Math.min(oemPassword.length, 14);
632             final byte[] keyBytes = new byte[14];
633             System.arraycopy(oemPassword, 0, keyBytes, 0, length);
634             final Key lowKey = createDESKey(keyBytes, 0);
635             final Key highKey = createDESKey(keyBytes, 7);
636             final byte[] magicConstant = "KGS!@#$%".getBytes(Consts.ASCII);
637             final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
638             des.init(Cipher.ENCRYPT_MODE, lowKey);
639             final byte[] lowHash = des.doFinal(magicConstant);
640             des.init(Cipher.ENCRYPT_MODE, highKey);
641             final byte[] highHash = des.doFinal(magicConstant);
642             final byte[] lmHash = new byte[16];
643             System.arraycopy(lowHash, 0, lmHash, 0, 8);
644             System.arraycopy(highHash, 0, lmHash, 8, 8);
645             return lmHash;
646         } catch (final Exception e) {
647             throw new NTLMEngineException(e.getMessage(), e);
648         }
649     }
650 
651     
652 
653 
654 
655 
656 
657 
658 
659 
660     private static byte[] ntlmHash(final String password) throws NTLMEngineException {
661         if (UNICODE_LITTLE_UNMARKED == null) {
662             throw new NTLMEngineException("Unicode not supported");
663         }
664         final byte[] unicodePassword = password.getBytes(UNICODE_LITTLE_UNMARKED);
665         final MD4 md4 = new MD4();
666         md4.update(unicodePassword);
667         return md4.getOutput();
668     }
669 
670     
671 
672 
673 
674 
675 
676     private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash)
677             throws NTLMEngineException {
678         if (UNICODE_LITTLE_UNMARKED == null) {
679             throw new NTLMEngineException("Unicode not supported");
680         }
681         final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
682         
683         hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
684         if (domain != null) {
685             hmacMD5.update(domain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
686         }
687         return hmacMD5.getOutput();
688     }
689 
690     
691 
692 
693 
694 
695 
696     private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash)
697             throws NTLMEngineException {
698         if (UNICODE_LITTLE_UNMARKED == null) {
699             throw new NTLMEngineException("Unicode not supported");
700         }
701         final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
702         
703         hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
704         if (domain != null) {
705             hmacMD5.update(domain.getBytes(UNICODE_LITTLE_UNMARKED));
706         }
707         return hmacMD5.getOutput();
708     }
709 
710     
711 
712 
713 
714 
715 
716 
717 
718 
719 
720     private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException {
721         try {
722             final byte[] keyBytes = new byte[21];
723             System.arraycopy(hash, 0, keyBytes, 0, 16);
724             final Key lowKey = createDESKey(keyBytes, 0);
725             final Key middleKey = createDESKey(keyBytes, 7);
726             final Key highKey = createDESKey(keyBytes, 14);
727             final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
728             des.init(Cipher.ENCRYPT_MODE, lowKey);
729             final byte[] lowResponse = des.doFinal(challenge);
730             des.init(Cipher.ENCRYPT_MODE, middleKey);
731             final byte[] middleResponse = des.doFinal(challenge);
732             des.init(Cipher.ENCRYPT_MODE, highKey);
733             final byte[] highResponse = des.doFinal(challenge);
734             final byte[] lmResponse = new byte[24];
735             System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
736             System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
737             System.arraycopy(highResponse, 0, lmResponse, 16, 8);
738             return lmResponse;
739         } catch (final Exception e) {
740             throw new NTLMEngineException(e.getMessage(), e);
741         }
742     }
743 
744     
745 
746 
747 
748 
749 
750 
751 
752 
753 
754 
755 
756 
757 
758     private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData) {
759         final HMACMD5 hmacMD5 = new HMACMD5(hash);
760         hmacMD5.update(challenge);
761         hmacMD5.update(clientData);
762         final byte[] mac = hmacMD5.getOutput();
763         final byte[] lmv2Response = new byte[mac.length + clientData.length];
764         System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
765         System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
766         return lmv2Response;
767     }
768 
769     enum Mode
770     {
771         CLIENT, SERVER;
772     }
773 
774     static class Handle
775     {
776         final private byte[] exportedSessionKey;
777         private byte[] signingKey;
778         private byte[] sealingKey;
779         private final Cipher rc4;
780         final Mode mode;
781         final private boolean isConnection;
782         int sequenceNumber = 0;
783 
784 
785         Handle( final byte[] exportedSessionKey, final Mode mode, final boolean isConnection )
786             throws NTLMEngineException
787         {
788             this.exportedSessionKey = exportedSessionKey;
789             this.isConnection = isConnection;
790             this.mode = mode;
791             try
792             {
793                 final MessageDigest signMd5 = getMD5();
794                 final MessageDigest sealMd5 = getMD5();
795                 signMd5.update( exportedSessionKey );
796                 sealMd5.update( exportedSessionKey );
797                 if ( mode == Mode.CLIENT )
798                 {
799                     signMd5.update( SIGN_MAGIC_CLIENT );
800                     sealMd5.update( SEAL_MAGIC_CLIENT );
801                 }
802                 else
803                 {
804                     signMd5.update( SIGN_MAGIC_SERVER );
805                     sealMd5.update( SEAL_MAGIC_SERVER );
806                 }
807                 signingKey = signMd5.digest();
808                 sealingKey = sealMd5.digest();
809             }
810             catch ( final Exception e )
811             {
812                 throw new NTLMEngineException( e.getMessage(), e );
813             }
814             rc4 = initCipher();
815         }
816 
817         public byte[] getSigningKey()
818         {
819             return signingKey;
820         }
821 
822 
823         public byte[] getSealingKey()
824         {
825             return sealingKey;
826         }
827 
828         private Cipher initCipher() throws NTLMEngineException
829         {
830             final Cipher cipher;
831             try
832             {
833                 cipher = Cipher.getInstance( "RC4" );
834                 if ( mode == Mode.CLIENT )
835                 {
836                     cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
837                 }
838                 else
839                 {
840                     cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
841                 }
842             }
843             catch ( final Exception e )
844             {
845                 throw new NTLMEngineException( e.getMessage(), e );
846             }
847             return cipher;
848         }
849 
850 
851         private void advanceMessageSequence() throws NTLMEngineException
852         {
853             if ( !isConnection )
854             {
855                 final MessageDigest sealMd5 = getMD5();
856                 sealMd5.update( sealingKey );
857                 final byte[] seqNumBytes = new byte[4];
858                 writeULong( seqNumBytes, sequenceNumber, 0 );
859                 sealMd5.update( seqNumBytes );
860                 sealingKey = sealMd5.digest();
861                 initCipher();
862             }
863             sequenceNumber++;
864         }
865 
866         private byte[] encrypt( final byte[] data )
867         {
868             return rc4.update( data );
869         }
870 
871         private byte[] decrypt( final byte[] data )
872         {
873             return rc4.update( data );
874         }
875 
876         private byte[] computeSignature( final byte[] message )
877         {
878             final byte[] sig = new byte[16];
879 
880             
881             sig[0] = 0x01;
882             sig[1] = 0x00;
883             sig[2] = 0x00;
884             sig[3] = 0x00;
885 
886             
887             final HMACMD5 hmacMD5 = new HMACMD5( signingKey );
888             hmacMD5.update( encodeLong( sequenceNumber ) );
889             hmacMD5.update( message );
890             final byte[] hmac = hmacMD5.getOutput();
891             final byte[] trimmedHmac = new byte[8];
892             System.arraycopy( hmac, 0, trimmedHmac, 0, 8 );
893             final byte[] encryptedHmac = encrypt( trimmedHmac );
894             System.arraycopy( encryptedHmac, 0, sig, 4, 8 );
895 
896             
897             encodeLong( sig, 12, sequenceNumber );
898 
899             return sig;
900         }
901 
902         private boolean validateSignature( final byte[] signature, final byte message[] )
903         {
904             final byte[] computedSignature = computeSignature( message );
905             
906             
907             
908             return Arrays.equals( signature, computedSignature );
909         }
910 
911         public byte[] signAndEncryptMessage( final byte[] cleartextMessage ) throws NTLMEngineException
912         {
913             final byte[] encryptedMessage = encrypt( cleartextMessage );
914             final byte[] signature = computeSignature( cleartextMessage );
915             final byte[] outMessage = new byte[signature.length + encryptedMessage.length];
916             System.arraycopy( signature, 0, outMessage, 0, signature.length );
917             System.arraycopy( encryptedMessage, 0, outMessage, signature.length, encryptedMessage.length );
918             advanceMessageSequence();
919             return outMessage;
920         }
921 
922         public byte[] decryptAndVerifySignedMessage( final byte[] inMessage ) throws NTLMEngineException
923         {
924             final byte[] signature = new byte[16];
925             System.arraycopy( inMessage, 0, signature, 0, signature.length );
926             final byte[] encryptedMessage = new byte[inMessage.length - 16];
927             System.arraycopy( inMessage, 16, encryptedMessage, 0, encryptedMessage.length );
928             final byte[] cleartextMessage = decrypt( encryptedMessage );
929             if ( !validateSignature( signature, cleartextMessage ) )
930             {
931                 throw new NTLMEngineException( "Wrong signature" );
932             }
933             advanceMessageSequence();
934             return cleartextMessage;
935         }
936 
937     }
938 
939     private static byte[] encodeLong( final int value )
940     {
941         final byte[] enc = new byte[4];
942         encodeLong( enc, 0, value );
943         return enc;
944     }
945 
946     private static void encodeLong( final byte[] buf, final int offset, final int value )
947     {
948         buf[offset + 0] = ( byte ) ( value & 0xff );
949         buf[offset + 1] = ( byte ) ( value >> 8 & 0xff );
950         buf[offset + 2] = ( byte ) ( value >> 16 & 0xff );
951         buf[offset + 3] = ( byte ) ( value >> 24 & 0xff );
952     }
953 
954     
955 
956 
957 
958 
959 
960 
961 
962 
963 
964 
965     private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) {
966         final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
967         final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
968         final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
969         final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
970         final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8
971                 + unknown1.length + targetInformation.length + unknown2.length];
972         int offset = 0;
973         System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
974         offset += blobSignature.length;
975         System.arraycopy(reserved, 0, blob, offset, reserved.length);
976         offset += reserved.length;
977         System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
978         offset += timestamp.length;
979         System.arraycopy(clientChallenge, 0, blob, offset, 8);
980         offset += 8;
981         System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
982         offset += unknown1.length;
983         System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
984         offset += targetInformation.length;
985         System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
986         offset += unknown2.length;
987         return blob;
988     }
989 
990     
991 
992 
993 
994 
995 
996 
997 
998 
999 
1000 
1001 
1002     private static Key createDESKey(final byte[] bytes, final int offset) {
1003         final byte[] keyBytes = new byte[7];
1004         System.arraycopy(bytes, offset, keyBytes, 0, 7);
1005         final byte[] material = new byte[8];
1006         material[0] = keyBytes[0];
1007         material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
1008         material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
1009         material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
1010         material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
1011         material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
1012         material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
1013         material[7] = (byte) (keyBytes[6] << 1);
1014         oddParity(material);
1015         return new SecretKeySpec(material, "DES");
1016     }
1017 
1018     
1019 
1020 
1021 
1022 
1023 
1024     private static void oddParity(final byte[] bytes) {
1025         for (int i = 0; i < bytes.length; i++) {
1026             final byte b = bytes[i];
1027             final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3)
1028                     ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
1029             if (needsParity) {
1030                 bytes[i] |= (byte) 0x01;
1031             } else {
1032                 bytes[i] &= (byte) 0xfe;
1033             }
1034         }
1035     }
1036 
1037     
1038 
1039 
1040 
1041 
1042     private static Charset getCharset(final int flags) throws NTLMEngineException
1043     {
1044         if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) {
1045             return DEFAULT_CHARSET;
1046         }
1047         if (UNICODE_LITTLE_UNMARKED == null) {
1048             throw new NTLMEngineException( "Unicode not supported" );
1049         }
1050         return UNICODE_LITTLE_UNMARKED;
1051     }
1052 
1053     
1054     private static String stripDotSuffix(final String value) {
1055         if (value == null) {
1056             return null;
1057         }
1058         final int index = value.indexOf('.');
1059         if (index != -1) {
1060             return value.substring(0, index);
1061         }
1062         return value;
1063     }
1064 
1065     
1066     private static String convertHost(final String host) {
1067         return stripDotSuffix(host);
1068     }
1069 
1070     
1071     private static String convertDomain(final String domain) {
1072         return stripDotSuffix(domain);
1073     }
1074 
1075     
1076     static class NTLMMessage {
1077         
1078         protected byte[] messageContents = null;
1079 
1080         
1081         protected int currentOutputPosition = 0;
1082 
1083         
1084         NTLMMessage() {
1085         }
1086 
1087         
1088         NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException {
1089             this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)), expectedType);
1090         }
1091 
1092         
1093         NTLMMessage(final byte[] message, final int expectedType) throws NTLMEngineException {
1094             messageContents = message;
1095             
1096             if (messageContents.length < SIGNATURE.length) {
1097                 throw new NTLMEngineException("NTLM message decoding error - packet too short");
1098             }
1099             int i = 0;
1100             while (i < SIGNATURE.length) {
1101                 if (messageContents[i] != SIGNATURE[i]) {
1102                     throw new NTLMEngineException(
1103                             "NTLM message expected - instead got unrecognized bytes");
1104                 }
1105                 i++;
1106             }
1107 
1108             
1109             final int type = readULong(SIGNATURE.length);
1110             if (type != expectedType) {
1111                 throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType)
1112                         + " message expected - instead got type " + Integer.toString(type));
1113             }
1114 
1115             currentOutputPosition = messageContents.length;
1116         }
1117 
1118         
1119 
1120 
1121 
1122         protected int getPreambleLength() {
1123             return SIGNATURE.length + 4;
1124         }
1125 
1126         
1127         protected int getMessageLength() {
1128             return currentOutputPosition;
1129         }
1130 
1131         
1132         protected byte readByte(final int position) throws NTLMEngineException {
1133             if (messageContents.length < position + 1) {
1134                 throw new NTLMEngineException("NTLM: Message too short");
1135             }
1136             return messageContents[position];
1137         }
1138 
1139         
1140         protected void readBytes(final byte[] buffer, final int position) throws NTLMEngineException {
1141             if (messageContents.length < position + buffer.length) {
1142                 throw new NTLMEngineException("NTLM: Message too short");
1143             }
1144             System.arraycopy(messageContents, position, buffer, 0, buffer.length);
1145         }
1146 
1147         
1148         protected int readUShort(final int position) throws NTLMEngineException {
1149             return NTLMEngineImpl.readUShort(messageContents, position);
1150         }
1151 
1152         
1153         protected int readULong(final int position) throws NTLMEngineException {
1154             return NTLMEngineImpl.readULong(messageContents, position);
1155         }
1156 
1157         
1158         protected byte[] readSecurityBuffer(final int position) throws NTLMEngineException {
1159             return NTLMEngineImpl.readSecurityBuffer(messageContents, position);
1160         }
1161 
1162         
1163 
1164 
1165 
1166 
1167 
1168 
1169 
1170         protected void prepareResponse(final int maxlength, final int messageType) {
1171             messageContents = new byte[maxlength];
1172             currentOutputPosition = 0;
1173             addBytes(SIGNATURE);
1174             addULong(messageType);
1175         }
1176 
1177         
1178 
1179 
1180 
1181 
1182 
1183         protected void addByte(final byte b) {
1184             messageContents[currentOutputPosition] = b;
1185             currentOutputPosition++;
1186         }
1187 
1188         
1189 
1190 
1191 
1192 
1193 
1194         protected void addBytes(final byte[] bytes) {
1195             if (bytes == null) {
1196                 return;
1197             }
1198             for (final byte b : bytes) {
1199                 messageContents[currentOutputPosition] = b;
1200                 currentOutputPosition++;
1201             }
1202         }
1203 
1204         
1205         protected void addUShort(final int value) {
1206             addByte((byte) (value & 0xff));
1207             addByte((byte) (value >> 8 & 0xff));
1208         }
1209 
1210         
1211         protected void addULong(final int value) {
1212             addByte((byte) (value & 0xff));
1213             addByte((byte) (value >> 8 & 0xff));
1214             addByte((byte) (value >> 16 & 0xff));
1215             addByte((byte) (value >> 24 & 0xff));
1216         }
1217 
1218         
1219 
1220 
1221 
1222 
1223 
1224         public String getResponse() {
1225             return new String(Base64.encodeBase64(getBytes()), Consts.ASCII);
1226         }
1227 
1228         public byte[] getBytes() {
1229             if (messageContents == null) {
1230                 buildMessage();
1231             }
1232             final byte[] resp;
1233             if ( messageContents.length > currentOutputPosition ) {
1234                 final byte[] tmp = new byte[currentOutputPosition];
1235                 System.arraycopy( messageContents, 0, tmp, 0, currentOutputPosition );
1236                 messageContents = tmp;
1237             }
1238             return messageContents;
1239         }
1240 
1241         protected void buildMessage() {
1242             throw new RuntimeException("Message builder not implemented for "+getClass().getName());
1243         }
1244     }
1245 
1246     
1247     static class Type1Message extends NTLMMessage {
1248 
1249         private final byte[] hostBytes;
1250         private final byte[] domainBytes;
1251         private final int flags;
1252 
1253         Type1Message(final String domain, final String host) throws NTLMEngineException {
1254             this(domain, host, null);
1255         }
1256 
1257         Type1Message(final String domain, final String host, final Integer flags) throws NTLMEngineException {
1258             super();
1259             this.flags = ((flags == null)?getDefaultFlags():flags);
1260 
1261             
1262             final String unqualifiedHost = convertHost(host);
1263             
1264             final String unqualifiedDomain = convertDomain(domain);
1265 
1266             hostBytes = unqualifiedHost != null ?
1267                     unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null;
1268             domainBytes = unqualifiedDomain != null ?
1269                     unqualifiedDomain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null;
1270         }
1271 
1272         Type1Message() {
1273             super();
1274             hostBytes = null;
1275             domainBytes = null;
1276             flags = getDefaultFlags();
1277         }
1278 
1279         private int getDefaultFlags() {
1280             return
1281                 
1282                 
1283 
1284                 
1285                 
1286                 FLAG_REQUEST_NTLMv1 |
1287                 FLAG_REQUEST_NTLM2_SESSION |
1288 
1289                 
1290                 FLAG_REQUEST_VERSION |
1291 
1292                 
1293                 FLAG_REQUEST_ALWAYS_SIGN |
1294                 
1295                 
1296 
1297                 
1298                 FLAG_REQUEST_128BIT_KEY_EXCH |
1299                 FLAG_REQUEST_56BIT_ENCRYPTION |
1300                 
1301 
1302                 FLAG_REQUEST_UNICODE_ENCODING;
1303 
1304         }
1305 
1306         
1307 
1308 
1309 
1310         @Override
1311         protected void buildMessage() {
1312             int domainBytesLength = 0;
1313             if ( domainBytes != null ) {
1314                 domainBytesLength = domainBytes.length;
1315             }
1316             int hostBytesLength = 0;
1317             if ( hostBytes != null ) {
1318                 hostBytesLength = hostBytes.length;
1319             }
1320 
1321             
1322             
1323             final int finalLength = 32 + 8 + hostBytesLength + domainBytesLength;
1324 
1325             
1326             
1327             prepareResponse(finalLength, 1);
1328 
1329             
1330             addULong(flags);
1331 
1332             
1333             addUShort(domainBytesLength);
1334             addUShort(domainBytesLength);
1335 
1336             
1337             addULong(hostBytesLength + 32 + 8);
1338 
1339             
1340             addUShort(hostBytesLength);
1341             addUShort(hostBytesLength);
1342 
1343             
1344             addULong(32 + 8);
1345 
1346             
1347             addUShort(0x0105);
1348             
1349             addULong(2600);
1350             
1351             addUShort(0x0f00);
1352 
1353             
1354             if (hostBytes != null) {
1355                 addBytes(hostBytes);
1356             }
1357             
1358             if (domainBytes != null) {
1359                 addBytes(domainBytes);
1360             }
1361         }
1362 
1363     }
1364 
1365     
1366     static class Type2Message extends NTLMMessage {
1367         protected final byte[] challenge;
1368         protected String target;
1369         protected byte[] targetInfo;
1370         protected final int flags;
1371 
1372         Type2Message(final String messageBody) throws NTLMEngineException {
1373             this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)));
1374         }
1375 
1376         Type2Message(final byte[] message) throws NTLMEngineException {
1377             super(message, 2);
1378 
1379             
1380             
1381             
1382             
1383             
1384             
1385             
1386             
1387             
1388             
1389             
1390             
1391 
1392             
1393             
1394             challenge = new byte[8];
1395             readBytes(challenge, 24);
1396 
1397             flags = readULong(20);
1398 
1399             
1400             target = null;
1401             
1402             
1403             
1404             if (getMessageLength() >= 12 + 8) {
1405                 final byte[] bytes = readSecurityBuffer(12);
1406                 if (bytes.length != 0) {
1407                     target = new String(bytes, getCharset(flags));
1408                 }
1409             }
1410 
1411             
1412             targetInfo = null;
1413             
1414             if (getMessageLength() >= 40 + 8) {
1415                 final byte[] bytes = readSecurityBuffer(40);
1416                 if (bytes.length != 0) {
1417                     targetInfo = bytes;
1418                 }
1419             }
1420         }
1421 
1422         
1423         byte[] getChallenge() {
1424             return challenge;
1425         }
1426 
1427         
1428         String getTarget() {
1429             return target;
1430         }
1431 
1432         
1433         byte[] getTargetInfo() {
1434             return targetInfo;
1435         }
1436 
1437         
1438         int getFlags() {
1439             return flags;
1440         }
1441 
1442     }
1443 
1444     
1445     static class Type3Message extends NTLMMessage {
1446         
1447         protected final byte[] type1Message;
1448         protected final byte[] type2Message;
1449         
1450         protected final int type2Flags;
1451 
1452         protected final byte[] domainBytes;
1453         protected final byte[] hostBytes;
1454         protected final byte[] userBytes;
1455 
1456         protected byte[] lmResp;
1457         protected byte[] ntResp;
1458         protected final byte[] sessionKey;
1459         protected final byte[] exportedSessionKey;
1460 
1461         protected final boolean computeMic;
1462 
1463         
1464 
1465         Type3Message(final String domain,
1466             final String host,
1467             final String user,
1468             final String password,
1469             final byte[] nonce,
1470             final int type2Flags,
1471             final String target,
1472             final byte[] targetInformation)
1473             throws NTLMEngineException {
1474             this(domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
1475         }
1476 
1477         
1478 
1479         Type3Message(final Random random, final long currentTime,
1480             final String domain,
1481             final String host,
1482             final String user,
1483             final String password,
1484             final byte[] nonce,
1485             final int type2Flags,
1486             final String target,
1487             final byte[] targetInformation)
1488             throws NTLMEngineException {
1489             this(random, currentTime, domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
1490         }
1491 
1492         
1493         Type3Message(final String domain,
1494             final String host,
1495             final String user,
1496             final String password,
1497             final byte[] nonce,
1498             final int type2Flags,
1499             final String target,
1500             final byte[] targetInformation,
1501             final Certificate peerServerCertificate,
1502             final byte[] type1Message,
1503             final byte[] type2Message)
1504             throws NTLMEngineException {
1505             this(RND_GEN, System.currentTimeMillis(), domain, host, user, password, nonce, type2Flags, target, targetInformation, peerServerCertificate, type1Message, type2Message);
1506         }
1507 
1508         
1509         Type3Message(final Random random, final long currentTime,
1510             final String domain,
1511             final String host,
1512             final String user,
1513             final String password,
1514             final byte[] nonce,
1515             final int type2Flags,
1516             final String target,
1517             final byte[] targetInformation,
1518             final Certificate peerServerCertificate,
1519             final byte[] type1Message,
1520             final byte[] type2Message)
1521             throws NTLMEngineException {
1522 
1523             if (random == null) {
1524                 throw new NTLMEngineException("Random generator not available");
1525             }
1526 
1527             
1528             this.type2Flags = type2Flags;
1529             this.type1Message = type1Message;
1530             this.type2Message = type2Message;
1531 
1532             
1533             final String unqualifiedHost = convertHost(host);
1534             
1535             final String unqualifiedDomain = convertDomain(domain);
1536 
1537             byte[] responseTargetInformation = targetInformation;
1538             if (peerServerCertificate != null) {
1539                 responseTargetInformation = addGssMicAvsToTargetInfo(targetInformation, peerServerCertificate);
1540                 computeMic = true;
1541             } else {
1542                 computeMic = false;
1543             }
1544 
1545              
1546             final CipherGen gen = new CipherGen(random, currentTime,
1547                 unqualifiedDomain,
1548                 user,
1549                 password,
1550                 nonce,
1551                 target,
1552                 responseTargetInformation);
1553 
1554             
1555             
1556             byte[] userSessionKey;
1557             try {
1558                 
1559                 
1560                 if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) &&
1561                     targetInformation != null && target != null) {
1562                     
1563                     ntResp = gen.getNTLMv2Response();
1564                     lmResp = gen.getLMv2Response();
1565                     if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1566                         userSessionKey = gen.getLanManagerSessionKey();
1567                     } else {
1568                         userSessionKey = gen.getNTLMv2UserSessionKey();
1569                     }
1570                 } else {
1571                     
1572                     if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) {
1573                         
1574                         ntResp = gen.getNTLM2SessionResponse();
1575                         lmResp = gen.getLM2SessionResponse();
1576                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1577                             userSessionKey = gen.getLanManagerSessionKey();
1578                         } else {
1579                             userSessionKey = gen.getNTLM2SessionResponseUserSessionKey();
1580                         }
1581                     } else {
1582                         ntResp = gen.getNTLMResponse();
1583                         lmResp = gen.getLMResponse();
1584                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1585                             userSessionKey = gen.getLanManagerSessionKey();
1586                         } else {
1587                             userSessionKey = gen.getNTLMUserSessionKey();
1588                         }
1589                     }
1590                 }
1591             } catch (final NTLMEngineException e) {
1592                 
1593                 
1594                 ntResp = new byte[0];
1595                 lmResp = gen.getLMResponse();
1596                 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1597                     userSessionKey = gen.getLanManagerSessionKey();
1598                 } else {
1599                     userSessionKey = gen.getLMUserSessionKey();
1600                 }
1601             }
1602 
1603             if ((type2Flags & FLAG_REQUEST_SIGN) != 0) {
1604                 if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) {
1605                     exportedSessionKey = gen.getSecondaryKey();
1606                     sessionKey = RC4(exportedSessionKey, userSessionKey);
1607                 } else {
1608                     sessionKey = userSessionKey;
1609                     exportedSessionKey = sessionKey;
1610                 }
1611             } else {
1612                 if (computeMic) {
1613                     throw new NTLMEngineException("Cannot sign/seal: no exported session key");
1614                 }
1615                 sessionKey = null;
1616                 exportedSessionKey = null;
1617             }
1618             final Charset charset = getCharset(type2Flags);
1619             hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(charset) : null;
1620              domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1621                 .toUpperCase(Locale.ROOT).getBytes(charset) : null;
1622             userBytes = user.getBytes(charset);
1623         }
1624 
1625         public byte[] getEncryptedRandomSessionKey() {
1626             return sessionKey;
1627         }
1628 
1629         public byte[] getExportedSessionKey() {
1630             return exportedSessionKey;
1631         }
1632 
1633         
1634         @Override
1635         protected void buildMessage() {
1636             final int ntRespLen = ntResp.length;
1637             final int lmRespLen = lmResp.length;
1638 
1639             final int domainLen = domainBytes != null ? domainBytes.length : 0;
1640             final int hostLen = hostBytes != null ? hostBytes.length: 0;
1641             final int userLen = userBytes.length;
1642             final int sessionKeyLen;
1643             if (sessionKey != null) {
1644                 sessionKeyLen = sessionKey.length;
1645             } else {
1646                 sessionKeyLen = 0;
1647             }
1648 
1649             
1650             final int lmRespOffset = 72 + 
1651                 ( computeMic ? 16 : 0 ); 
1652             final int ntRespOffset = lmRespOffset + lmRespLen;
1653             final int domainOffset = ntRespOffset + ntRespLen;
1654             final int userOffset = domainOffset + domainLen;
1655             final int hostOffset = userOffset + userLen;
1656             final int sessionKeyOffset = hostOffset + hostLen;
1657             final int finalLength = sessionKeyOffset + sessionKeyLen;
1658 
1659             
1660             prepareResponse(finalLength, 3);
1661 
1662             
1663             addUShort(lmRespLen);
1664             addUShort(lmRespLen);
1665 
1666             
1667             addULong(lmRespOffset);
1668 
1669             
1670             addUShort(ntRespLen);
1671             addUShort(ntRespLen);
1672 
1673             
1674             addULong(ntRespOffset);
1675 
1676             
1677             addUShort(domainLen);
1678             addUShort(domainLen);
1679 
1680             
1681             addULong(domainOffset);
1682 
1683             
1684             addUShort(userLen);
1685             addUShort(userLen);
1686 
1687             
1688             addULong(userOffset);
1689 
1690             
1691             addUShort(hostLen);
1692             addUShort(hostLen);
1693 
1694             
1695             addULong(hostOffset);
1696 
1697             
1698             addUShort(sessionKeyLen);
1699             addUShort(sessionKeyLen);
1700 
1701             
1702             addULong(sessionKeyOffset);
1703 
1704             
1705             addULong(
1706                     
1707 
1708 
1709 
1710 
1711 
1712 
1713 
1714 
1715 
1716 
1717 
1718 
1719 
1720 
1721 
1722 
1723 
1724 
1725 
1726 
1727 
1728 
1729 
1730 
1731 
1732                 type2Flags
1733             );
1734 
1735             
1736             addUShort(0x0105);
1737             
1738             addULong(2600);
1739             
1740             addUShort(0x0f00);
1741 
1742             int micPosition = -1;
1743             if ( computeMic ) {
1744                 micPosition = currentOutputPosition;
1745                 currentOutputPosition += 16;
1746             }
1747 
1748             
1749             addBytes(lmResp);
1750             addBytes(ntResp);
1751             addBytes(domainBytes);
1752             addBytes(userBytes);
1753             addBytes(hostBytes);
1754             if (sessionKey != null) {
1755                 addBytes(sessionKey);
1756             }
1757 
1758             
1759 
1760             if (computeMic) {
1761                 
1762                 final HMACMD5 hmacMD5 = new HMACMD5( exportedSessionKey );
1763                 hmacMD5.update( type1Message );
1764                 hmacMD5.update( type2Message );
1765                 hmacMD5.update( messageContents );
1766                 final byte[] mic = hmacMD5.getOutput();
1767                 System.arraycopy( mic, 0, messageContents, micPosition, mic.length );
1768             }
1769         }
1770 
1771         
1772 
1773 
1774 
1775         private byte[] addGssMicAvsToTargetInfo( final byte[] originalTargetInfo,
1776             final Certificate peerServerCertificate ) throws NTLMEngineException
1777         {
1778             final byte[] newTargetInfo = new byte[originalTargetInfo.length + 8 + 20];
1779             final int appendLength = originalTargetInfo.length - 4; 
1780             System.arraycopy( originalTargetInfo, 0, newTargetInfo, 0, appendLength );
1781             writeUShort( newTargetInfo, MSV_AV_FLAGS, appendLength );
1782             writeUShort( newTargetInfo, 4, appendLength + 2 );
1783             writeULong( newTargetInfo, MSV_AV_FLAGS_MIC, appendLength + 4 );
1784             writeUShort( newTargetInfo, MSV_AV_CHANNEL_BINDINGS, appendLength + 8 );
1785             writeUShort( newTargetInfo, 16, appendLength + 10 );
1786 
1787             final byte[] channelBindingsHash;
1788             try
1789             {
1790                 final byte[] certBytes = peerServerCertificate.getEncoded();
1791                 final MessageDigest sha256 = MessageDigest.getInstance( "SHA-256" );
1792                 final byte[] certHashBytes = sha256.digest( certBytes );
1793                 final byte[] channelBindingStruct = new byte[16 + 4 + MAGIC_TLS_SERVER_ENDPOINT.length
1794                     + certHashBytes.length];
1795                 writeULong( channelBindingStruct, 0x00000035, 16 );
1796                 System.arraycopy( MAGIC_TLS_SERVER_ENDPOINT, 0, channelBindingStruct, 20,
1797                     MAGIC_TLS_SERVER_ENDPOINT.length );
1798                 System.arraycopy( certHashBytes, 0, channelBindingStruct, 20 + MAGIC_TLS_SERVER_ENDPOINT.length,
1799                     certHashBytes.length );
1800                 final MessageDigest md5 = getMD5();
1801                 channelBindingsHash = md5.digest( channelBindingStruct );
1802             }
1803             catch ( final CertificateEncodingException e )
1804             {
1805                 throw new NTLMEngineException( e.getMessage(), e );
1806             }
1807             catch ( final NoSuchAlgorithmException e )
1808             {
1809                 throw new NTLMEngineException( e.getMessage(), e );
1810             }
1811 
1812             System.arraycopy( channelBindingsHash, 0, newTargetInfo, appendLength + 12, 16 );
1813             return newTargetInfo;
1814          }
1815 
1816     }
1817 
1818     static void writeUShort(final byte[] buffer, final int value, final int offset) {
1819         buffer[offset] = ( byte ) ( value & 0xff );
1820         buffer[offset + 1] = ( byte ) ( value >> 8 & 0xff );
1821     }
1822 
1823     static void writeULong(final byte[] buffer, final int value, final int offset) {
1824         buffer[offset] = (byte) (value & 0xff);
1825         buffer[offset + 1] = (byte) (value >> 8 & 0xff);
1826         buffer[offset + 2] = (byte) (value >> 16 & 0xff);
1827         buffer[offset + 3] = (byte) (value >> 24 & 0xff);
1828     }
1829 
1830     static int F(final int x, final int y, final int z) {
1831         return ((x & y) | (~x & z));
1832     }
1833 
1834     static int G(final int x, final int y, final int z) {
1835         return ((x & y) | (x & z) | (y & z));
1836     }
1837 
1838     static int H(final int x, final int y, final int z) {
1839         return (x ^ y ^ z);
1840     }
1841 
1842     static int rotintlft(final int val, final int numbits) {
1843         return ((val << numbits) | (val >>> (32 - numbits)));
1844     }
1845 
1846     static MessageDigest getMD5() {
1847         try {
1848             return MessageDigest.getInstance("MD5");
1849         } catch (final NoSuchAlgorithmException ex) {
1850             throw new RuntimeException("MD5 message digest doesn't seem to exist - fatal error: "+ex.getMessage(), ex);
1851         }
1852     }
1853 
1854     
1855 
1856 
1857 
1858 
1859 
1860 
1861     static class MD4 {
1862         protected int A = 0x67452301;
1863         protected int B = 0xefcdab89;
1864         protected int C = 0x98badcfe;
1865         protected int D = 0x10325476;
1866         protected long count = 0L;
1867         protected final byte[] dataBuffer = new byte[64];
1868 
1869         MD4() {
1870         }
1871 
1872         void update(final byte[] input) {
1873             
1874             
1875             
1876             int curBufferPos = (int) (count & 63L);
1877             int inputIndex = 0;
1878             while (input.length - inputIndex + curBufferPos >= dataBuffer.length) {
1879                 
1880                 
1881                 
1882                 final int transferAmt = dataBuffer.length - curBufferPos;
1883                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1884                 count += transferAmt;
1885                 curBufferPos = 0;
1886                 inputIndex += transferAmt;
1887                 processBuffer();
1888             }
1889 
1890             
1891             
1892             if (inputIndex < input.length) {
1893                 final int transferAmt = input.length - inputIndex;
1894                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1895                 count += transferAmt;
1896                 curBufferPos += transferAmt;
1897             }
1898         }
1899 
1900         byte[] getOutput() {
1901             
1902             
1903             final int bufferIndex = (int) (count & 63L);
1904             final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
1905             final byte[] postBytes = new byte[padLen + 8];
1906             
1907             
1908             postBytes[0] = (byte) 0x80;
1909             
1910             for (int i = 0; i < 8; i++) {
1911                 postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i));
1912             }
1913 
1914             
1915             update(postBytes);
1916 
1917             
1918             final byte[] result = new byte[16];
1919             writeULong(result, A, 0);
1920             writeULong(result, B, 4);
1921             writeULong(result, C, 8);
1922             writeULong(result, D, 12);
1923             return result;
1924         }
1925 
1926         protected void processBuffer() {
1927             
1928             final int[] d = new int[16];
1929 
1930             for (int i = 0; i < 16; i++) {
1931                 d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8)
1932                         + ((dataBuffer[i * 4 + 2] & 0xff) << 16)
1933                         + ((dataBuffer[i * 4 + 3] & 0xff) << 24);
1934             }
1935 
1936             
1937             final int AA = A;
1938             final int BB = B;
1939             final int CC = C;
1940             final int DD = D;
1941             round1(d);
1942             round2(d);
1943             round3(d);
1944             A += AA;
1945             B += BB;
1946             C += CC;
1947             D += DD;
1948 
1949         }
1950 
1951         protected void round1(final int[] d) {
1952             A = rotintlft((A + F(B, C, D) + d[0]), 3);
1953             D = rotintlft((D + F(A, B, C) + d[1]), 7);
1954             C = rotintlft((C + F(D, A, B) + d[2]), 11);
1955             B = rotintlft((B + F(C, D, A) + d[3]), 19);
1956 
1957             A = rotintlft((A + F(B, C, D) + d[4]), 3);
1958             D = rotintlft((D + F(A, B, C) + d[5]), 7);
1959             C = rotintlft((C + F(D, A, B) + d[6]), 11);
1960             B = rotintlft((B + F(C, D, A) + d[7]), 19);
1961 
1962             A = rotintlft((A + F(B, C, D) + d[8]), 3);
1963             D = rotintlft((D + F(A, B, C) + d[9]), 7);
1964             C = rotintlft((C + F(D, A, B) + d[10]), 11);
1965             B = rotintlft((B + F(C, D, A) + d[11]), 19);
1966 
1967             A = rotintlft((A + F(B, C, D) + d[12]), 3);
1968             D = rotintlft((D + F(A, B, C) + d[13]), 7);
1969             C = rotintlft((C + F(D, A, B) + d[14]), 11);
1970             B = rotintlft((B + F(C, D, A) + d[15]), 19);
1971         }
1972 
1973         protected void round2(final int[] d) {
1974             A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
1975             D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
1976             C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
1977             B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
1978 
1979             A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
1980             D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
1981             C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
1982             B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
1983 
1984             A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
1985             D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
1986             C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
1987             B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
1988 
1989             A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
1990             D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
1991             C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
1992             B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
1993 
1994         }
1995 
1996         protected void round3(final int[] d) {
1997             A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
1998             D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
1999             C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
2000             B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
2001 
2002             A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
2003             D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
2004             C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
2005             B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
2006 
2007             A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
2008             D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
2009             C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
2010             B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
2011 
2012             A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
2013             D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
2014             C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
2015             B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
2016 
2017         }
2018 
2019     }
2020 
2021     
2022 
2023 
2024 
2025     static class HMACMD5 {
2026         protected final byte[] ipad;
2027         protected final byte[] opad;
2028         protected final MessageDigest md5;
2029 
2030         HMACMD5(final byte[] input) {
2031             byte[] key = input;
2032             md5 = getMD5();
2033 
2034             
2035             ipad = new byte[64];
2036             opad = new byte[64];
2037 
2038             int keyLength = key.length;
2039             if (keyLength > 64) {
2040                 
2041                 md5.update(key);
2042                 key = md5.digest();
2043                 keyLength = key.length;
2044             }
2045             int i = 0;
2046             while (i < keyLength) {
2047                 ipad[i] = (byte) (key[i] ^ (byte) 0x36);
2048                 opad[i] = (byte) (key[i] ^ (byte) 0x5c);
2049                 i++;
2050             }
2051             while (i < 64) {
2052                 ipad[i] = (byte) 0x36;
2053                 opad[i] = (byte) 0x5c;
2054                 i++;
2055             }
2056 
2057             
2058             md5.reset();
2059             md5.update(ipad);
2060 
2061         }
2062 
2063         
2064         byte[] getOutput() {
2065             final byte[] digest = md5.digest();
2066             md5.update(opad);
2067             return md5.digest(digest);
2068         }
2069 
2070         
2071         void update(final byte[] input) {
2072             md5.update(input);
2073         }
2074 
2075         
2076         void update(final byte[] input, final int offset, final int length) {
2077             md5.update(input, offset, length);
2078         }
2079 
2080     }
2081 
2082     @Override
2083     public String generateType1Msg(
2084             final String domain,
2085             final String workstation) throws NTLMEngineException {
2086         return getType1Message(workstation, domain);
2087     }
2088 
2089     @Override
2090     public String generateType3Msg(
2091             final String username,
2092             final String password,
2093             final String domain,
2094             final String workstation,
2095             final String challenge) throws NTLMEngineException {
2096         final Type2Message t2m = new Type2Message(challenge);
2097         return getType3Message(
2098                 username,
2099                 password,
2100                 workstation,
2101                 domain,
2102                 t2m.getChallenge(),
2103                 t2m.getFlags(),
2104                 t2m.getTarget(),
2105                 t2m.getTargetInfo());
2106     }
2107 
2108 }