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