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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package edu.internet2.middleware.grouperClientExt.org.apache.commons.httpclient.auth;
47
48 import java.security.InvalidKeyException;
49 import java.security.NoSuchAlgorithmException;
50
51 import javax.crypto.BadPaddingException;
52 import javax.crypto.Cipher;
53 import javax.crypto.IllegalBlockSizeException;
54 import javax.crypto.NoSuchPaddingException;
55 import javax.crypto.spec.SecretKeySpec;
56
57 import edu.internet2.middleware.grouperClientExt.org.apache.commons.codec.binary.Base64;
58 import edu.internet2.middleware.grouperClientExt.org.apache.commons.httpclient.HttpException;
59 import edu.internet2.middleware.grouperClientExt.org.apache.commons.httpclient.util.EncodingUtil;
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 final class NTLM {
84
85
86 public static final String DEFAULT_CHARSET = "ASCII";
87
88
89 private byte[] currentResponse;
90
91
92 private int currentPosition = 0;
93
94
95 private String credentialCharset = DEFAULT_CHARSET;
96
97
98
99
100
101
102
103
104
105
106
107
108 public final String getResponseFor(String message,
109 String username, String password, String host, String domain)
110 throws AuthenticationException {
111
112 final String response;
113 if (message == null || message.trim().equals("")) {
114 response = getType1Message(host, domain);
115 } else {
116 response = getType3Message(username, password, host, domain,
117 parseType2Message(message));
118 }
119 return response;
120 }
121
122
123
124
125
126
127
128 private Cipher getCipher(byte[] key) throws AuthenticationException {
129 try {
130 final Cipher ecipher = Cipher.getInstance("DES/ECB/NoPadding");
131 key = setupKey(key);
132 ecipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "DES"));
133 return ecipher;
134 } catch (NoSuchAlgorithmException e) {
135 throw new AuthenticationException("DES encryption is not available.", e);
136 } catch (InvalidKeyException e) {
137 throw new AuthenticationException("Invalid key for DES encryption.", e);
138 } catch (NoSuchPaddingException e) {
139 throw new AuthenticationException(
140 "NoPadding option for DES is not available.", e);
141 }
142 }
143
144
145
146
147
148
149 private byte[] setupKey(byte[] key56) {
150 byte[] key = new byte[8];
151 key[0] = (byte) ((key56[0] >> 1) & 0xff);
152 key[1] = (byte) ((((key56[0] & 0x01) << 6)
153 | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff);
154 key[2] = (byte) ((((key56[1] & 0x03) << 5)
155 | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff);
156 key[3] = (byte) ((((key56[2] & 0x07) << 4)
157 | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff);
158 key[4] = (byte) ((((key56[3] & 0x0f) << 3)
159 | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff);
160 key[5] = (byte) ((((key56[4] & 0x1f) << 2)
161 | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff);
162 key[6] = (byte) ((((key56[5] & 0x3f) << 1)
163 | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff);
164 key[7] = (byte) (key56[6] & 0x7f);
165
166 for (int i = 0; i < key.length; i++) {
167 key[i] = (byte) (key[i] << 1);
168 }
169 return key;
170 }
171
172
173
174
175
176
177
178
179 private byte[] encrypt(byte[] key, byte[] bytes)
180 throws AuthenticationException {
181 Cipher ecipher = getCipher(key);
182 try {
183 byte[] enc = ecipher.doFinal(bytes);
184 return enc;
185 } catch (IllegalBlockSizeException e) {
186 throw new AuthenticationException("Invalid block size for DES encryption.", e);
187 } catch (BadPaddingException e) {
188 throw new AuthenticationException("Data not padded correctly for DES encryption.", e);
189 }
190 }
191
192
193
194
195
196 private void prepareResponse(int length) {
197 currentResponse = new byte[length];
198 currentPosition = 0;
199 }
200
201
202
203
204
205 private void addByte(byte b) {
206 currentResponse[currentPosition] = b;
207 currentPosition++;
208 }
209
210
211
212
213
214 private void addBytes(byte[] bytes) {
215 for (int i = 0; i < bytes.length; i++) {
216 currentResponse[currentPosition] = bytes[i];
217 currentPosition++;
218 }
219 }
220
221
222
223
224
225
226 private String getResponse() {
227 byte[] resp;
228 if (currentResponse.length > currentPosition) {
229 byte[] tmp = new byte[currentPosition];
230 for (int i = 0; i < currentPosition; i++) {
231 tmp[i] = currentResponse[i];
232 }
233 resp = tmp;
234 } else {
235 resp = currentResponse;
236 }
237 return EncodingUtil.getAsciiString(Base64.encodeBase64(resp));
238 }
239
240
241
242
243
244
245
246
247
248 public String getType1Message(String host, String domain) {
249 host = host.toUpperCase();
250 domain = domain.toUpperCase();
251 byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET);
252 byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET);
253
254 int finalLength = 32 + hostBytes.length + domainBytes.length;
255 prepareResponse(finalLength);
256
257
258 byte[] protocol = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET);
259 addBytes(protocol);
260 addByte((byte) 0);
261
262
263 addByte((byte) 1);
264 addByte((byte) 0);
265 addByte((byte) 0);
266 addByte((byte) 0);
267
268
269 addByte((byte) 6);
270 addByte((byte) 82);
271 addByte((byte) 0);
272 addByte((byte) 0);
273
274
275 int iDomLen = domainBytes.length;
276 byte[] domLen = convertShort(iDomLen);
277 addByte(domLen[0]);
278 addByte(domLen[1]);
279
280
281 addByte(domLen[0]);
282 addByte(domLen[1]);
283
284
285 byte[] domOff = convertShort(hostBytes.length + 32);
286 addByte(domOff[0]);
287 addByte(domOff[1]);
288 addByte((byte) 0);
289 addByte((byte) 0);
290
291
292 byte[] hostLen = convertShort(hostBytes.length);
293 addByte(hostLen[0]);
294 addByte(hostLen[1]);
295
296
297 addByte(hostLen[0]);
298 addByte(hostLen[1]);
299
300
301 byte[] hostOff = convertShort(32);
302 addByte(hostOff[0]);
303 addByte(hostOff[1]);
304 addByte((byte) 0);
305 addByte((byte) 0);
306
307
308 addBytes(hostBytes);
309
310
311 addBytes(domainBytes);
312
313 return getResponse();
314 }
315
316
317
318
319
320
321
322
323 public byte[] parseType2Message(String message) {
324
325 byte[] msg = Base64.decodeBase64(EncodingUtil.getBytes(message, DEFAULT_CHARSET));
326 byte[] nonce = new byte[8];
327
328 for (int i = 0; i < 8; i++) {
329 nonce[i] = msg[i + 24];
330 }
331 return nonce;
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347 public String getType3Message(String user, String password,
348 String host, String domain, byte[] nonce)
349 throws AuthenticationException {
350
351 int ntRespLen = 0;
352 int lmRespLen = 24;
353 domain = domain.toUpperCase();
354 host = host.toUpperCase();
355 user = user.toUpperCase();
356 byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET);
357 byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET);
358 byte[] userBytes = EncodingUtil.getBytes(user, credentialCharset);
359 int domainLen = domainBytes.length;
360 int hostLen = hostBytes.length;
361 int userLen = userBytes.length;
362 int finalLength = 64 + ntRespLen + lmRespLen + domainLen
363 + userLen + hostLen;
364 prepareResponse(finalLength);
365 byte[] ntlmssp = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET);
366 addBytes(ntlmssp);
367 addByte((byte) 0);
368 addByte((byte) 3);
369 addByte((byte) 0);
370 addByte((byte) 0);
371 addByte((byte) 0);
372
373
374 addBytes(convertShort(24));
375 addBytes(convertShort(24));
376
377
378 addBytes(convertShort(finalLength - 24));
379 addByte((byte) 0);
380 addByte((byte) 0);
381
382
383 addBytes(convertShort(0));
384 addBytes(convertShort(0));
385
386
387 addBytes(convertShort(finalLength));
388 addByte((byte) 0);
389 addByte((byte) 0);
390
391
392 addBytes(convertShort(domainLen));
393 addBytes(convertShort(domainLen));
394
395
396 addBytes(convertShort(64));
397 addByte((byte) 0);
398 addByte((byte) 0);
399
400
401 addBytes(convertShort(userLen));
402 addBytes(convertShort(userLen));
403
404
405 addBytes(convertShort(64 + domainLen));
406 addByte((byte) 0);
407 addByte((byte) 0);
408
409
410 addBytes(convertShort(hostLen));
411 addBytes(convertShort(hostLen));
412
413
414 addBytes(convertShort(64 + domainLen + userLen));
415
416 for (int i = 0; i < 6; i++) {
417 addByte((byte) 0);
418 }
419
420
421 addBytes(convertShort(finalLength));
422 addByte((byte) 0);
423 addByte((byte) 0);
424
425
426 addByte((byte) 6);
427 addByte((byte) 82);
428 addByte((byte) 0);
429 addByte((byte) 0);
430
431 addBytes(domainBytes);
432 addBytes(userBytes);
433 addBytes(hostBytes);
434 addBytes(hashPassword(password, nonce));
435 return getResponse();
436 }
437
438
439
440
441
442
443
444
445
446 private byte[] hashPassword(String password, byte[] nonce)
447 throws AuthenticationException {
448 byte[] passw = EncodingUtil.getBytes(password.toUpperCase(), credentialCharset);
449 byte[] lmPw1 = new byte[7];
450 byte[] lmPw2 = new byte[7];
451
452 int len = passw.length;
453 if (len > 7) {
454 len = 7;
455 }
456
457 int idx;
458 for (idx = 0; idx < len; idx++) {
459 lmPw1[idx] = passw[idx];
460 }
461 for (; idx < 7; idx++) {
462 lmPw1[idx] = (byte) 0;
463 }
464
465 len = passw.length;
466 if (len > 14) {
467 len = 14;
468 }
469 for (idx = 7; idx < len; idx++) {
470 lmPw2[idx - 7] = passw[idx];
471 }
472 for (; idx < 14; idx++) {
473 lmPw2[idx - 7] = (byte) 0;
474 }
475
476
477 byte[] magic = {
478 (byte) 0x4B, (byte) 0x47, (byte) 0x53, (byte) 0x21,
479 (byte) 0x40, (byte) 0x23, (byte) 0x24, (byte) 0x25
480 };
481
482 byte[] lmHpw1;
483 lmHpw1 = encrypt(lmPw1, magic);
484
485 byte[] lmHpw2 = encrypt(lmPw2, magic);
486
487 byte[] lmHpw = new byte[21];
488 for (int i = 0; i < lmHpw1.length; i++) {
489 lmHpw[i] = lmHpw1[i];
490 }
491 for (int i = 0; i < lmHpw2.length; i++) {
492 lmHpw[i + 8] = lmHpw2[i];
493 }
494 for (int i = 0; i < 5; i++) {
495 lmHpw[i + 16] = (byte) 0;
496 }
497
498
499 byte[] lmResp = new byte[24];
500 calcResp(lmHpw, nonce, lmResp);
501
502 return lmResp;
503 }
504
505
506
507
508
509
510
511
512
513
514
515 private void calcResp(byte[] keys, byte[] plaintext, byte[] results)
516 throws AuthenticationException {
517 byte[] keys1 = new byte[7];
518 byte[] keys2 = new byte[7];
519 byte[] keys3 = new byte[7];
520 for (int i = 0; i < 7; i++) {
521 keys1[i] = keys[i];
522 }
523
524 for (int i = 0; i < 7; i++) {
525 keys2[i] = keys[i + 7];
526 }
527
528 for (int i = 0; i < 7; i++) {
529 keys3[i] = keys[i + 14];
530 }
531 byte[] results1 = encrypt(keys1, plaintext);
532
533 byte[] results2 = encrypt(keys2, plaintext);
534
535 byte[] results3 = encrypt(keys3, plaintext);
536
537 for (int i = 0; i < 8; i++) {
538 results[i] = results1[i];
539 }
540 for (int i = 0; i < 8; i++) {
541 results[i + 8] = results2[i];
542 }
543 for (int i = 0; i < 8; i++) {
544 results[i + 16] = results3[i];
545 }
546 }
547
548
549
550
551
552
553 private byte[] convertShort(int num) {
554 byte[] val = new byte[2];
555 String hex = Integer.toString(num, 16);
556 while (hex.length() < 4) {
557 hex = "0" + hex;
558 }
559 String low = hex.substring(2, 4);
560 String high = hex.substring(0, 2);
561
562 val[0] = (byte) Integer.parseInt(low, 16);
563 val[1] = (byte) Integer.parseInt(high, 16);
564 return val;
565 }
566
567
568
569
570 public String getCredentialCharset() {
571 return credentialCharset;
572 }
573
574
575
576
577 public void setCredentialCharset(String credentialCharset) {
578 this.credentialCharset = credentialCharset;
579 }
580
581 }