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.hc.client5.http.utils.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 final byte[] unicodePassword = new ByteArrayBuilder()
691 .charset(UNICODE_LITTLE_UNMARKED).append(password).toByteArray();
692 final MD4 md4 = new MD4();
693 md4.update(unicodePassword);
694 return md4.getOutput();
695 }
696
697
698
699
700
701
702
703 private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash)
704 throws NTLMEngineException {
705 final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
706
707 hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
708 if (domain != null) {
709 hmacMD5.update(domain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
710 }
711 return hmacMD5.getOutput();
712 }
713
714
715
716
717
718
719
720 private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash)
721 throws NTLMEngineException {
722 final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
723
724 hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
725 if (domain != null) {
726 hmacMD5.update(domain.getBytes(UNICODE_LITTLE_UNMARKED));
727 }
728 return hmacMD5.getOutput();
729 }
730
731
732
733
734
735
736
737
738
739
740
741 private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException {
742 try {
743 final byte[] keyBytes = new byte[21];
744 System.arraycopy(hash, 0, keyBytes, 0, 16);
745 final Key lowKey = createDESKey(keyBytes, 0);
746 final Key middleKey = createDESKey(keyBytes, 7);
747 final Key highKey = createDESKey(keyBytes, 14);
748 final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
749 des.init(Cipher.ENCRYPT_MODE, lowKey);
750 final byte[] lowResponse = des.doFinal(challenge);
751 des.init(Cipher.ENCRYPT_MODE, middleKey);
752 final byte[] middleResponse = des.doFinal(challenge);
753 des.init(Cipher.ENCRYPT_MODE, highKey);
754 final byte[] highResponse = des.doFinal(challenge);
755 final byte[] lmResponse = new byte[24];
756 System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
757 System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
758 System.arraycopy(highResponse, 0, lmResponse, 16, 8);
759 return lmResponse;
760 } catch (final Exception e) {
761 throw new NTLMEngineException(e.getMessage(), e);
762 }
763 }
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779 private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData) {
780 final HMACMD5 hmacMD5 = new HMACMD5(hash);
781 hmacMD5.update(challenge);
782 hmacMD5.update(clientData);
783 final byte[] mac = hmacMD5.getOutput();
784 final byte[] lmv2Response = new byte[mac.length + clientData.length];
785 System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
786 System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
787 return lmv2Response;
788 }
789
790 enum Mode
791 {
792 CLIENT, SERVER
793 }
794
795 static class Handle
796 {
797 private final byte[] signingKey;
798 private byte[] sealingKey;
799 private final Cipher rc4;
800 final Mode mode;
801 final private boolean isConnection;
802 int sequenceNumber;
803
804
805 Handle( final byte[] exportedSessionKey, final Mode mode, final boolean isConnection ) throws NTLMEngineException
806 {
807 this.isConnection = isConnection;
808 this.mode = mode;
809 try
810 {
811 final MessageDigest signMd5 = getMD5();
812 final MessageDigest sealMd5 = getMD5();
813 signMd5.update( exportedSessionKey );
814 sealMd5.update( exportedSessionKey );
815 if ( mode == Mode.CLIENT )
816 {
817 signMd5.update( SIGN_MAGIC_CLIENT );
818 sealMd5.update( SEAL_MAGIC_CLIENT );
819 }
820 else
821 {
822 signMd5.update( SIGN_MAGIC_SERVER );
823 sealMd5.update( SEAL_MAGIC_SERVER );
824 }
825 signingKey = signMd5.digest();
826 sealingKey = sealMd5.digest();
827 }
828 catch ( final Exception e )
829 {
830 throw new NTLMEngineException( e.getMessage(), e );
831 }
832 rc4 = initCipher();
833 }
834
835 public byte[] getSigningKey()
836 {
837 return signingKey;
838 }
839
840
841 public byte[] getSealingKey()
842 {
843 return sealingKey;
844 }
845
846 private Cipher initCipher() throws NTLMEngineException
847 {
848 final Cipher cipher;
849 try
850 {
851 cipher = Cipher.getInstance( "RC4" );
852 if ( mode == Mode.CLIENT )
853 {
854 cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
855 }
856 else
857 {
858 cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
859 }
860 }
861 catch ( final Exception e )
862 {
863 throw new NTLMEngineException( e.getMessage(), e );
864 }
865 return cipher;
866 }
867
868
869 private void advanceMessageSequence() throws NTLMEngineException
870 {
871 if ( !isConnection )
872 {
873 final MessageDigest sealMd5 = getMD5();
874 sealMd5.update( sealingKey );
875 final byte[] seqNumBytes = new byte[4];
876 writeULong( seqNumBytes, sequenceNumber, 0 );
877 sealMd5.update( seqNumBytes );
878 sealingKey = sealMd5.digest();
879 initCipher();
880 }
881 sequenceNumber++;
882 }
883
884 private byte[] encrypt( final byte[] data )
885 {
886 return rc4.update( data );
887 }
888
889 private byte[] decrypt( final byte[] data )
890 {
891 return rc4.update( data );
892 }
893
894 private byte[] computeSignature( final byte[] message )
895 {
896 final byte[] sig = new byte[16];
897
898
899 sig[0] = 0x01;
900 sig[1] = 0x00;
901 sig[2] = 0x00;
902 sig[3] = 0x00;
903
904
905 final HMACMD5 hmacMD5 = new HMACMD5( signingKey );
906 hmacMD5.update( encodeLong( sequenceNumber ) );
907 hmacMD5.update( message );
908 final byte[] hmac = hmacMD5.getOutput();
909 final byte[] trimmedHmac = new byte[8];
910 System.arraycopy( hmac, 0, trimmedHmac, 0, 8 );
911 final byte[] encryptedHmac = encrypt( trimmedHmac );
912 System.arraycopy( encryptedHmac, 0, sig, 4, 8 );
913
914
915 encodeLong( sig, 12, sequenceNumber );
916
917 return sig;
918 }
919
920 private boolean validateSignature( final byte[] signature, final byte[] message )
921 {
922 final byte[] computedSignature = computeSignature( message );
923
924
925
926 return Arrays.equals( signature, computedSignature );
927 }
928
929 public byte[] signAndEncryptMessage( final byte[] cleartextMessage ) throws NTLMEngineException
930 {
931 final byte[] encryptedMessage = encrypt( cleartextMessage );
932 final byte[] signature = computeSignature( cleartextMessage );
933 final byte[] outMessage = new byte[signature.length + encryptedMessage.length];
934 System.arraycopy( signature, 0, outMessage, 0, signature.length );
935 System.arraycopy( encryptedMessage, 0, outMessage, signature.length, encryptedMessage.length );
936 advanceMessageSequence();
937 return outMessage;
938 }
939
940 public byte[] decryptAndVerifySignedMessage( final byte[] inMessage ) throws NTLMEngineException
941 {
942 final byte[] signature = new byte[16];
943 System.arraycopy( inMessage, 0, signature, 0, signature.length );
944 final byte[] encryptedMessage = new byte[inMessage.length - 16];
945 System.arraycopy( inMessage, 16, encryptedMessage, 0, encryptedMessage.length );
946 final byte[] cleartextMessage = decrypt( encryptedMessage );
947 if ( !validateSignature( signature, cleartextMessage ) )
948 {
949 throw new NTLMEngineException( "Wrong signature" );
950 }
951 advanceMessageSequence();
952 return cleartextMessage;
953 }
954
955 }
956
957 private static byte[] encodeLong( final int value )
958 {
959 final byte[] enc = new byte[4];
960 encodeLong( enc, 0, value );
961 return enc;
962 }
963
964 private static void encodeLong( final byte[] buf, final int offset, final int value )
965 {
966 buf[offset + 0] = ( byte ) ( value & 0xff );
967 buf[offset + 1] = ( byte ) ( value >> 8 & 0xff );
968 buf[offset + 2] = ( byte ) ( value >> 16 & 0xff );
969 buf[offset + 3] = ( byte ) ( value >> 24 & 0xff );
970 }
971
972
973
974
975
976
977
978
979
980
981
982
983 private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) {
984 final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
985 final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
986 final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
987 final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
988 final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8
989 + unknown1.length + targetInformation.length + unknown2.length];
990 int offset = 0;
991 System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
992 offset += blobSignature.length;
993 System.arraycopy(reserved, 0, blob, offset, reserved.length);
994 offset += reserved.length;
995 System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
996 offset += timestamp.length;
997 System.arraycopy(clientChallenge, 0, blob, offset, 8);
998 offset += 8;
999 System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
1000 offset += unknown1.length;
1001 System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
1002 offset += targetInformation.length;
1003 System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
1004 offset += unknown2.length;
1005 return blob;
1006 }
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020 private static Key createDESKey(final byte[] bytes, final int offset) {
1021 final byte[] keyBytes = new byte[7];
1022 System.arraycopy(bytes, offset, keyBytes, 0, 7);
1023 final byte[] material = new byte[8];
1024 material[0] = keyBytes[0];
1025 material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
1026 material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
1027 material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
1028 material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
1029 material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
1030 material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
1031 material[7] = (byte) (keyBytes[6] << 1);
1032 oddParity(material);
1033 return new SecretKeySpec(material, "DES");
1034 }
1035
1036
1037
1038
1039
1040
1041
1042 private static void oddParity(final byte[] bytes) {
1043 for (int i = 0; i < bytes.length; i++) {
1044 final byte b = bytes[i];
1045 final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3)
1046 ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
1047 if (needsParity) {
1048 bytes[i] |= (byte) 0x01;
1049 } else {
1050 bytes[i] &= (byte) 0xfe;
1051 }
1052 }
1053 }
1054
1055
1056
1057
1058
1059
1060 private static Charset getCharset(final int flags) throws NTLMEngineException
1061 {
1062 if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) {
1063 return DEFAULT_CHARSET;
1064 }
1065 return UNICODE_LITTLE_UNMARKED;
1066 }
1067
1068
1069 static class NTLMMessage {
1070
1071 byte[] messageContents;
1072
1073
1074 int currentOutputPosition;
1075
1076
1077 NTLMMessage() {
1078 }
1079
1080
1081 NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException {
1082 this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)), expectedType);
1083 }
1084
1085
1086 NTLMMessage(final byte[] message, final int expectedType) throws NTLMEngineException {
1087 messageContents = message;
1088
1089 if (messageContents.length < SIGNATURE.length) {
1090 throw new NTLMEngineException("NTLM message decoding error - packet too short");
1091 }
1092 int i = 0;
1093 while (i < SIGNATURE.length) {
1094 if (messageContents[i] != SIGNATURE[i]) {
1095 throw new NTLMEngineException(
1096 "NTLM message expected - instead got unrecognized bytes");
1097 }
1098 i++;
1099 }
1100
1101
1102 final int type = readULong(SIGNATURE.length);
1103 if (type != expectedType) {
1104 throw new NTLMEngineException("NTLM type " + expectedType
1105 + " message expected - instead got type " + type);
1106 }
1107
1108 currentOutputPosition = messageContents.length;
1109 }
1110
1111
1112
1113
1114
1115 int getPreambleLength() {
1116 return SIGNATURE.length + 4;
1117 }
1118
1119
1120 int getMessageLength() {
1121 return currentOutputPosition;
1122 }
1123
1124
1125 byte readByte(final int position) throws NTLMEngineException {
1126 if (messageContents.length < position + 1) {
1127 throw new NTLMEngineException("NTLM: Message too short");
1128 }
1129 return messageContents[position];
1130 }
1131
1132
1133 void readBytes(final byte[] buffer, final int position) throws NTLMEngineException {
1134 if (messageContents.length < position + buffer.length) {
1135 throw new NTLMEngineException("NTLM: Message too short");
1136 }
1137 System.arraycopy(messageContents, position, buffer, 0, buffer.length);
1138 }
1139
1140
1141 int readUShort(final int position) {
1142 return NTLMEngineImpl.readUShort(messageContents, position);
1143 }
1144
1145
1146 int readULong(final int position) {
1147 return NTLMEngineImpl.readULong(messageContents, position);
1148 }
1149
1150
1151 byte[] readSecurityBuffer(final int position) {
1152 return NTLMEngineImpl.readSecurityBuffer(messageContents, position);
1153 }
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163 void prepareResponse(final int maxlength, final int messageType) {
1164 messageContents = new byte[maxlength];
1165 currentOutputPosition = 0;
1166 addBytes(SIGNATURE);
1167 addULong(messageType);
1168 }
1169
1170
1171
1172
1173
1174
1175
1176 void addByte(final byte b) {
1177 messageContents[currentOutputPosition] = b;
1178 currentOutputPosition++;
1179 }
1180
1181
1182
1183
1184
1185
1186
1187 void addBytes(final byte[] bytes) {
1188 if (bytes == null) {
1189 return;
1190 }
1191 for (final byte b : bytes) {
1192 messageContents[currentOutputPosition] = b;
1193 currentOutputPosition++;
1194 }
1195 }
1196
1197
1198 void addUShort(final int value) {
1199 addByte((byte) (value & 0xff));
1200 addByte((byte) (value >> 8 & 0xff));
1201 }
1202
1203
1204 void addULong(final int value) {
1205 addByte((byte) (value & 0xff));
1206 addByte((byte) (value >> 8 & 0xff));
1207 addByte((byte) (value >> 16 & 0xff));
1208 addByte((byte) (value >> 24 & 0xff));
1209 }
1210
1211
1212
1213
1214
1215
1216
1217 public String getResponse() {
1218 return new String(Base64.encodeBase64(getBytes()), StandardCharsets.US_ASCII);
1219 }
1220
1221 public byte[] getBytes() {
1222 if (messageContents == null) {
1223 buildMessage();
1224 }
1225 if (messageContents.length > currentOutputPosition) {
1226 final byte[] tmp = new byte[currentOutputPosition];
1227 System.arraycopy( messageContents, 0, tmp, 0, currentOutputPosition );
1228 messageContents = tmp;
1229 }
1230 return messageContents;
1231 }
1232
1233 void buildMessage() {
1234 throw new RuntimeException("Message builder not implemented for "+getClass().getName());
1235 }
1236 }
1237
1238
1239 static class Type1Message extends NTLMMessage {
1240
1241 private final byte[] hostBytes;
1242 private final byte[] domainBytes;
1243 private final int flags;
1244
1245 Type1Message(final String domain, final String host) {
1246 this(domain, host, null);
1247 }
1248
1249 Type1Message(final String domain, final String host, final Integer flags) {
1250 super();
1251 this.flags = ((flags == null)?getDefaultFlags(): flags.intValue());
1252
1253
1254 final String unqualifiedHost = host;
1255 final String unqualifiedDomain = domain;
1256
1257 hostBytes = unqualifiedHost != null ?
1258 unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null;
1259 domainBytes = unqualifiedDomain != null ?
1260 unqualifiedDomain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null;
1261 }
1262
1263 Type1Message() {
1264 super();
1265 hostBytes = null;
1266 domainBytes = null;
1267 flags = getDefaultFlags();
1268 }
1269
1270 private int getDefaultFlags() {
1271 return
1272
1273
1274
1275
1276
1277 FLAG_REQUEST_NTLMv1 |
1278 FLAG_REQUEST_NTLM2_SESSION |
1279
1280
1281 FLAG_REQUEST_VERSION |
1282
1283
1284 FLAG_REQUEST_ALWAYS_SIGN |
1285
1286
1287
1288
1289 FLAG_REQUEST_128BIT_KEY_EXCH |
1290 FLAG_REQUEST_56BIT_ENCRYPTION |
1291
1292
1293 FLAG_REQUEST_UNICODE_ENCODING;
1294
1295 }
1296
1297
1298
1299
1300
1301 @Override
1302 void buildMessage() {
1303 int domainBytesLength = 0;
1304 if ( domainBytes != null ) {
1305 domainBytesLength = domainBytes.length;
1306 }
1307 int hostBytesLength = 0;
1308 if ( hostBytes != null ) {
1309 hostBytesLength = hostBytes.length;
1310 }
1311
1312
1313
1314 final int finalLength = 32 + 8 + hostBytesLength + domainBytesLength;
1315
1316
1317
1318 prepareResponse(finalLength, 1);
1319
1320
1321 addULong(flags);
1322
1323
1324 addUShort(domainBytesLength);
1325 addUShort(domainBytesLength);
1326
1327
1328 addULong(hostBytesLength + 32 + 8);
1329
1330
1331 addUShort(hostBytesLength);
1332 addUShort(hostBytesLength);
1333
1334
1335 addULong(32 + 8);
1336
1337
1338 addUShort(0x0105);
1339
1340 addULong(2600);
1341
1342 addUShort(0x0f00);
1343
1344
1345 if (hostBytes != null) {
1346 addBytes(hostBytes);
1347 }
1348
1349 if (domainBytes != null) {
1350 addBytes(domainBytes);
1351 }
1352 }
1353
1354 }
1355
1356
1357 static class Type2Message extends NTLMMessage {
1358 final byte[] challenge;
1359 String target;
1360 byte[] targetInfo;
1361 final int flags;
1362
1363 Type2Message(final String messageBody) throws NTLMEngineException {
1364 this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)));
1365 }
1366
1367 Type2Message(final byte[] message) throws NTLMEngineException {
1368 super(message, 2);
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385 challenge = new byte[8];
1386 readBytes(challenge, 24);
1387
1388 flags = readULong(20);
1389
1390
1391 target = null;
1392
1393
1394
1395 if (getMessageLength() >= 12 + 8) {
1396 final byte[] bytes = readSecurityBuffer(12);
1397 if (bytes.length != 0) {
1398 target = new String(bytes, getCharset(flags));
1399 }
1400 }
1401
1402
1403 targetInfo = null;
1404
1405 if (getMessageLength() >= 40 + 8) {
1406 final byte[] bytes = readSecurityBuffer(40);
1407 if (bytes.length != 0) {
1408 targetInfo = bytes;
1409 }
1410 }
1411 }
1412
1413
1414 byte[] getChallenge() {
1415 return challenge;
1416 }
1417
1418
1419 String getTarget() {
1420 return target;
1421 }
1422
1423
1424 byte[] getTargetInfo() {
1425 return targetInfo;
1426 }
1427
1428
1429 int getFlags() {
1430 return flags;
1431 }
1432
1433 }
1434
1435
1436 static class Type3Message extends NTLMMessage {
1437
1438 final byte[] type1Message;
1439 final byte[] type2Message;
1440
1441 final int type2Flags;
1442
1443 final byte[] domainBytes;
1444 final byte[] hostBytes;
1445 final byte[] userBytes;
1446
1447 byte[] lmResp;
1448 byte[] ntResp;
1449 final byte[] sessionKey;
1450 final byte[] exportedSessionKey;
1451
1452 final boolean computeMic;
1453
1454
1455
1456 Type3Message(final String domain,
1457 final String host,
1458 final String user,
1459 final char[] password,
1460 final byte[] nonce,
1461 final int type2Flags,
1462 final String target,
1463 final byte[] targetInformation)
1464 throws NTLMEngineException {
1465 this(domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
1466 }
1467
1468
1469
1470 Type3Message(final Random random, final long currentTime,
1471 final String domain,
1472 final String host,
1473 final String user,
1474 final char[] password,
1475 final byte[] nonce,
1476 final int type2Flags,
1477 final String target,
1478 final byte[] targetInformation)
1479 throws NTLMEngineException {
1480 this(random, currentTime, domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
1481 }
1482
1483
1484 Type3Message(final String domain,
1485 final String host,
1486 final String user,
1487 final char[] password,
1488 final byte[] nonce,
1489 final int type2Flags,
1490 final String target,
1491 final byte[] targetInformation,
1492 final Certificate peerServerCertificate,
1493 final byte[] type1Message,
1494 final byte[] type2Message)
1495 throws NTLMEngineException {
1496 this(RND_GEN, System.currentTimeMillis(), domain, host, user, password, nonce, type2Flags, target, targetInformation, peerServerCertificate, type1Message, type2Message);
1497 }
1498
1499
1500 Type3Message(final Random random, final long currentTime,
1501 final String domain,
1502 final String host,
1503 final String user,
1504 final char[] password,
1505 final byte[] nonce,
1506 final int type2Flags,
1507 final String target,
1508 final byte[] targetInformation,
1509 final Certificate peerServerCertificate,
1510 final byte[] type1Message,
1511 final byte[] type2Message)
1512 throws NTLMEngineException {
1513
1514 if (random == null) {
1515 throw new NTLMEngineException("Random generator not available");
1516 }
1517
1518
1519 this.type2Flags = type2Flags;
1520 this.type1Message = type1Message;
1521 this.type2Message = type2Message;
1522
1523
1524 final String unqualifiedHost = host;
1525
1526 final String unqualifiedDomain = domain;
1527
1528 byte[] responseTargetInformation = targetInformation;
1529 if (peerServerCertificate != null) {
1530 responseTargetInformation = addGssMicAvsToTargetInfo(targetInformation, peerServerCertificate);
1531 computeMic = true;
1532 } else {
1533 computeMic = false;
1534 }
1535
1536
1537 final CipherGen gen = new CipherGen(random, currentTime,
1538 unqualifiedDomain,
1539 user,
1540 password,
1541 nonce,
1542 target,
1543 responseTargetInformation);
1544
1545
1546
1547 byte[] userSessionKey;
1548 try {
1549
1550
1551 if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) &&
1552 targetInformation != null && target != null) {
1553
1554 ntResp = gen.getNTLMv2Response();
1555 lmResp = gen.getLMv2Response();
1556 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1557 userSessionKey = gen.getLanManagerSessionKey();
1558 } else {
1559 userSessionKey = gen.getNTLMv2UserSessionKey();
1560 }
1561 } else {
1562
1563 if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) {
1564
1565 ntResp = gen.getNTLM2SessionResponse();
1566 lmResp = gen.getLM2SessionResponse();
1567 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1568 userSessionKey = gen.getLanManagerSessionKey();
1569 } else {
1570 userSessionKey = gen.getNTLM2SessionResponseUserSessionKey();
1571 }
1572 } else {
1573 ntResp = gen.getNTLMResponse();
1574 lmResp = gen.getLMResponse();
1575 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1576 userSessionKey = gen.getLanManagerSessionKey();
1577 } else {
1578 userSessionKey = gen.getNTLMUserSessionKey();
1579 }
1580 }
1581 }
1582 } catch (final NTLMEngineException e) {
1583
1584
1585 ntResp = new byte[0];
1586 lmResp = gen.getLMResponse();
1587 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1588 userSessionKey = gen.getLanManagerSessionKey();
1589 } else {
1590 userSessionKey = gen.getLMUserSessionKey();
1591 }
1592 }
1593
1594 if ((type2Flags & FLAG_REQUEST_SIGN) != 0) {
1595 if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) {
1596 exportedSessionKey = gen.getSecondaryKey();
1597 sessionKey = RC4(exportedSessionKey, userSessionKey);
1598 } else {
1599 sessionKey = userSessionKey;
1600 exportedSessionKey = sessionKey;
1601 }
1602 } else {
1603 if (computeMic) {
1604 throw new NTLMEngineException("Cannot sign/seal: no exported session key");
1605 }
1606 sessionKey = null;
1607 exportedSessionKey = null;
1608 }
1609 final Charset charset = getCharset(type2Flags);
1610 hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(charset) : null;
1611 domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1612 .toUpperCase(Locale.ROOT).getBytes(charset) : null;
1613 userBytes = user.getBytes(charset);
1614 }
1615
1616 public byte[] getEncryptedRandomSessionKey() {
1617 return sessionKey;
1618 }
1619
1620 public byte[] getExportedSessionKey() {
1621 return exportedSessionKey;
1622 }
1623
1624
1625 @Override
1626 void buildMessage() {
1627 final int ntRespLen = ntResp.length;
1628 final int lmRespLen = lmResp.length;
1629
1630 final int domainLen = domainBytes != null ? domainBytes.length : 0;
1631 final int hostLen = hostBytes != null ? hostBytes.length: 0;
1632 final int userLen = userBytes.length;
1633 final int sessionKeyLen;
1634 if (sessionKey != null) {
1635 sessionKeyLen = sessionKey.length;
1636 } else {
1637 sessionKeyLen = 0;
1638 }
1639
1640
1641 final int lmRespOffset = 72 +
1642 ( computeMic ? 16 : 0 );
1643 final int ntRespOffset = lmRespOffset + lmRespLen;
1644 final int domainOffset = ntRespOffset + ntRespLen;
1645 final int userOffset = domainOffset + domainLen;
1646 final int hostOffset = userOffset + userLen;
1647 final int sessionKeyOffset = hostOffset + hostLen;
1648 final int finalLength = sessionKeyOffset + sessionKeyLen;
1649
1650
1651 prepareResponse(finalLength, 3);
1652
1653
1654 addUShort(lmRespLen);
1655 addUShort(lmRespLen);
1656
1657
1658 addULong(lmRespOffset);
1659
1660
1661 addUShort(ntRespLen);
1662 addUShort(ntRespLen);
1663
1664
1665 addULong(ntRespOffset);
1666
1667
1668 addUShort(domainLen);
1669 addUShort(domainLen);
1670
1671
1672 addULong(domainOffset);
1673
1674
1675 addUShort(userLen);
1676 addUShort(userLen);
1677
1678
1679 addULong(userOffset);
1680
1681
1682 addUShort(hostLen);
1683 addUShort(hostLen);
1684
1685
1686 addULong(hostOffset);
1687
1688
1689 addUShort(sessionKeyLen);
1690 addUShort(sessionKeyLen);
1691
1692
1693 addULong(sessionKeyOffset);
1694
1695
1696 addULong(
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723 type2Flags
1724 );
1725
1726
1727 addUShort(0x0105);
1728
1729 addULong(2600);
1730
1731 addUShort(0x0f00);
1732
1733 int micPosition = -1;
1734 if ( computeMic ) {
1735 micPosition = currentOutputPosition;
1736 currentOutputPosition += 16;
1737 }
1738
1739
1740 addBytes(lmResp);
1741 addBytes(ntResp);
1742 addBytes(domainBytes);
1743 addBytes(userBytes);
1744 addBytes(hostBytes);
1745 if (sessionKey != null) {
1746 addBytes(sessionKey);
1747 }
1748
1749
1750
1751 if (computeMic) {
1752
1753 final HMACMD5 hmacMD5 = new HMACMD5( exportedSessionKey );
1754 hmacMD5.update( type1Message );
1755 hmacMD5.update( type2Message );
1756 hmacMD5.update( messageContents );
1757 final byte[] mic = hmacMD5.getOutput();
1758 System.arraycopy( mic, 0, messageContents, micPosition, mic.length );
1759 }
1760 }
1761
1762
1763
1764
1765
1766 private byte[] addGssMicAvsToTargetInfo( final byte[] originalTargetInfo,
1767 final Certificate peerServerCertificate ) throws NTLMEngineException
1768 {
1769 final byte[] newTargetInfo = new byte[originalTargetInfo.length + 8 + 20];
1770 final int appendLength = originalTargetInfo.length - 4;
1771 System.arraycopy( originalTargetInfo, 0, newTargetInfo, 0, appendLength );
1772 writeUShort( newTargetInfo, MSV_AV_FLAGS, appendLength );
1773 writeUShort( newTargetInfo, 4, appendLength + 2 );
1774 writeULong( newTargetInfo, MSV_AV_FLAGS_MIC, appendLength + 4 );
1775 writeUShort( newTargetInfo, MSV_AV_CHANNEL_BINDINGS, appendLength + 8 );
1776 writeUShort( newTargetInfo, 16, appendLength + 10 );
1777
1778 final byte[] channelBindingsHash;
1779 try
1780 {
1781 final byte[] certBytes = peerServerCertificate.getEncoded();
1782 final MessageDigest sha256 = MessageDigest.getInstance( "SHA-256" );
1783 final byte[] certHashBytes = sha256.digest( certBytes );
1784 final byte[] channelBindingStruct = new byte[16 + 4 + MAGIC_TLS_SERVER_ENDPOINT.length
1785 + certHashBytes.length];
1786 writeULong( channelBindingStruct, 0x00000035, 16 );
1787 System.arraycopy( MAGIC_TLS_SERVER_ENDPOINT, 0, channelBindingStruct, 20,
1788 MAGIC_TLS_SERVER_ENDPOINT.length );
1789 System.arraycopy( certHashBytes, 0, channelBindingStruct, 20 + MAGIC_TLS_SERVER_ENDPOINT.length,
1790 certHashBytes.length );
1791 final MessageDigest md5 = getMD5();
1792 channelBindingsHash = md5.digest( channelBindingStruct );
1793 }
1794 catch (final CertificateEncodingException | NoSuchAlgorithmException e )
1795 {
1796 throw new NTLMEngineException( e.getMessage(), e );
1797 }
1798
1799 System.arraycopy( channelBindingsHash, 0, newTargetInfo, appendLength + 12, 16 );
1800 return newTargetInfo;
1801 }
1802
1803 }
1804
1805 static void writeUShort(final byte[] buffer, final int value, final int offset) {
1806 buffer[offset] = ( byte ) ( value & 0xff );
1807 buffer[offset + 1] = ( byte ) ( value >> 8 & 0xff );
1808 }
1809
1810 static void writeULong(final byte[] buffer, final int value, final int offset) {
1811 buffer[offset] = (byte) (value & 0xff);
1812 buffer[offset + 1] = (byte) (value >> 8 & 0xff);
1813 buffer[offset + 2] = (byte) (value >> 16 & 0xff);
1814 buffer[offset + 3] = (byte) (value >> 24 & 0xff);
1815 }
1816
1817 static int F(final int x, final int y, final int z) {
1818 return ((x & y) | (~x & z));
1819 }
1820
1821 static int G(final int x, final int y, final int z) {
1822 return ((x & y) | (x & z) | (y & z));
1823 }
1824
1825 static int H(final int x, final int y, final int z) {
1826 return (x ^ y ^ z);
1827 }
1828
1829 static int rotintlft(final int val, final int numbits) {
1830 return ((val << numbits) | (val >>> (32 - numbits)));
1831 }
1832
1833 static MessageDigest getMD5() {
1834 try {
1835 return MessageDigest.getInstance("MD5");
1836 } catch (final NoSuchAlgorithmException ex) {
1837 throw new RuntimeException("MD5 message digest doesn't seem to exist - fatal error: "+ex.getMessage(), ex);
1838 }
1839 }
1840
1841
1842
1843
1844
1845
1846
1847
1848 static class MD4 {
1849 int A = 0x67452301;
1850 int B = 0xefcdab89;
1851 int C = 0x98badcfe;
1852 int D = 0x10325476;
1853 long count;
1854 final byte[] dataBuffer = new byte[64];
1855
1856 MD4() {
1857 }
1858
1859 void update(final byte[] input) {
1860
1861
1862
1863 int curBufferPos = (int) (count & 63L);
1864 int inputIndex = 0;
1865 while (input.length - inputIndex + curBufferPos >= dataBuffer.length) {
1866
1867
1868
1869 final int transferAmt = dataBuffer.length - curBufferPos;
1870 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1871 count += transferAmt;
1872 curBufferPos = 0;
1873 inputIndex += transferAmt;
1874 processBuffer();
1875 }
1876
1877
1878
1879 if (inputIndex < input.length) {
1880 final int transferAmt = input.length - inputIndex;
1881 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1882 count += transferAmt;
1883 curBufferPos += transferAmt;
1884 }
1885 }
1886
1887 byte[] getOutput() {
1888
1889
1890 final int bufferIndex = (int) (count & 63L);
1891 final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
1892 final byte[] postBytes = new byte[padLen + 8];
1893
1894
1895 postBytes[0] = (byte) 0x80;
1896
1897 for (int i = 0; i < 8; i++) {
1898 postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i));
1899 }
1900
1901
1902 update(postBytes);
1903
1904
1905 final byte[] result = new byte[16];
1906 writeULong(result, A, 0);
1907 writeULong(result, B, 4);
1908 writeULong(result, C, 8);
1909 writeULong(result, D, 12);
1910 return result;
1911 }
1912
1913 void processBuffer() {
1914
1915 final int[] d = new int[16];
1916
1917 for (int i = 0; i < 16; i++) {
1918 d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8)
1919 + ((dataBuffer[i * 4 + 2] & 0xff) << 16)
1920 + ((dataBuffer[i * 4 + 3] & 0xff) << 24);
1921 }
1922
1923
1924 final int AA = A;
1925 final int BB = B;
1926 final int CC = C;
1927 final int DD = D;
1928 round1(d);
1929 round2(d);
1930 round3(d);
1931 A += AA;
1932 B += BB;
1933 C += CC;
1934 D += DD;
1935
1936 }
1937
1938 void round1(final int[] d) {
1939 A = rotintlft((A + F(B, C, D) + d[0]), 3);
1940 D = rotintlft((D + F(A, B, C) + d[1]), 7);
1941 C = rotintlft((C + F(D, A, B) + d[2]), 11);
1942 B = rotintlft((B + F(C, D, A) + d[3]), 19);
1943
1944 A = rotintlft((A + F(B, C, D) + d[4]), 3);
1945 D = rotintlft((D + F(A, B, C) + d[5]), 7);
1946 C = rotintlft((C + F(D, A, B) + d[6]), 11);
1947 B = rotintlft((B + F(C, D, A) + d[7]), 19);
1948
1949 A = rotintlft((A + F(B, C, D) + d[8]), 3);
1950 D = rotintlft((D + F(A, B, C) + d[9]), 7);
1951 C = rotintlft((C + F(D, A, B) + d[10]), 11);
1952 B = rotintlft((B + F(C, D, A) + d[11]), 19);
1953
1954 A = rotintlft((A + F(B, C, D) + d[12]), 3);
1955 D = rotintlft((D + F(A, B, C) + d[13]), 7);
1956 C = rotintlft((C + F(D, A, B) + d[14]), 11);
1957 B = rotintlft((B + F(C, D, A) + d[15]), 19);
1958 }
1959
1960 void round2(final int[] d) {
1961 A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
1962 D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
1963 C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
1964 B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
1965
1966 A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
1967 D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
1968 C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
1969 B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
1970
1971 A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
1972 D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
1973 C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
1974 B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
1975
1976 A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
1977 D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
1978 C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
1979 B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
1980
1981 }
1982
1983 void round3(final int[] d) {
1984 A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
1985 D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
1986 C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
1987 B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
1988
1989 A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
1990 D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
1991 C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
1992 B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
1993
1994 A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
1995 D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
1996 C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
1997 B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
1998
1999 A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
2000 D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
2001 C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
2002 B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
2003
2004 }
2005
2006 }
2007
2008
2009
2010
2011
2012 static class HMACMD5 {
2013 final byte[] ipad;
2014 final byte[] opad;
2015 final MessageDigest md5;
2016
2017 HMACMD5(final byte[] input) {
2018 byte[] key = input;
2019 md5 = getMD5();
2020
2021
2022 ipad = new byte[64];
2023 opad = new byte[64];
2024
2025 int keyLength = key.length;
2026 if (keyLength > 64) {
2027
2028 md5.update(key);
2029 key = md5.digest();
2030 keyLength = key.length;
2031 }
2032 int i = 0;
2033 while (i < keyLength) {
2034 ipad[i] = (byte) (key[i] ^ (byte) 0x36);
2035 opad[i] = (byte) (key[i] ^ (byte) 0x5c);
2036 i++;
2037 }
2038 while (i < 64) {
2039 ipad[i] = (byte) 0x36;
2040 opad[i] = (byte) 0x5c;
2041 i++;
2042 }
2043
2044
2045 md5.reset();
2046 md5.update(ipad);
2047
2048 }
2049
2050
2051 byte[] getOutput() {
2052 final byte[] digest = md5.digest();
2053 md5.update(opad);
2054 return md5.digest(digest);
2055 }
2056
2057
2058 void update(final byte[] input) {
2059 md5.update(input);
2060 }
2061
2062
2063 void update(final byte[] input, final int offset, final int length) {
2064 md5.update(input, offset, length);
2065 }
2066
2067 }
2068
2069 @Override
2070 public String generateType1Msg(
2071 final String domain,
2072 final String workstation) throws NTLMEngineException {
2073 return getType1Message(workstation, domain);
2074 }
2075
2076 @Override
2077 public String generateType3Msg(
2078 final String username,
2079 final char[] password,
2080 final String domain,
2081 final String workstation,
2082 final String challenge) throws NTLMEngineException {
2083 final Type2Message t2m = new Type2Message(challenge);
2084 return getType3Message(
2085 username,
2086 password,
2087 workstation,
2088 domain,
2089 t2m.getChallenge(),
2090 t2m.getFlags(),
2091 t2m.getTarget(),
2092 t2m.getTargetInfo());
2093 }
2094
2095 }