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 }