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