1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.proxy.handlers.http.ntlm;
21
22 import java.security.Key;
23 import java.security.MessageDigest;
24
25 import javax.crypto.Cipher;
26 import javax.crypto.spec.SecretKeySpec;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public class NTLMResponses {
44
45 public static final byte[] LM_HASH_MAGIC_CONSTANT =
46 new byte[]{ 'K', 'G', 'S', '!', '@', '#', '$', '%' };
47
48 private NTLMResponses() {
49 }
50
51
52
53
54
55
56
57
58
59
60
61 public static byte[] getLMResponse(String password, byte[] challenge) throws Exception {
62 byte[] lmHash = lmHash(password);
63
64 return lmResponse(lmHash, challenge);
65 }
66
67
68
69
70
71
72
73
74
75
76
77 public static byte[] getNTLMResponse(String password, byte[] challenge) throws Exception {
78 byte[] ntlmHash = ntlmHash(password);
79
80 return lmResponse(ntlmHash, challenge);
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 public static byte[] getNTLMv2Response(String target, String user, String password, byte[] targetInformation,
100 byte[] challenge, byte[] clientNonce) throws Exception {
101
102 return getNTLMv2Response(target, user, password, targetInformation, challenge, clientNonce,
103 System.currentTimeMillis());
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 public static byte[] getNTLMv2Response(String target, String user, String password, byte[] targetInformation,
124 byte[] challenge, byte[] clientNonce, long time) throws Exception {
125 byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
126 byte[] blob = createBlob(targetInformation, clientNonce, time);
127
128 return lmv2Response(ntlmv2Hash, blob, challenge);
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public static byte[] getLMv2Response(String target, String user, String password, byte[] challenge,
146 byte[] clientNonce) throws Exception {
147 byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
148 return lmv2Response(ntlmv2Hash, clientNonce, challenge);
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 public static byte[] getNTLM2SessionResponse(String password, byte[] challenge, byte[] clientNonce)
165 throws Exception {
166 byte[] ntlmHash = ntlmHash(password);
167 MessageDigest md5 = MessageDigest.getInstance("MD5");
168 md5.update(challenge);
169 md5.update(clientNonce);
170 byte[] sessionHash = new byte[8];
171 System.arraycopy(md5.digest(), 0, sessionHash, 0, 8);
172 return lmResponse(ntlmHash, sessionHash);
173 }
174
175
176
177
178
179
180
181
182
183 private static byte[] lmHash(String password) throws Exception {
184 byte[] oemPassword = password.toUpperCase().getBytes("US-ASCII");
185 int length = Math.min(oemPassword.length, 14);
186 byte[] keyBytes = new byte[14];
187 System.arraycopy(oemPassword, 0, keyBytes, 0, length);
188 Key lowKey = createDESKey(keyBytes, 0);
189 Key highKey = createDESKey(keyBytes, 7);
190 Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
191 des.init(Cipher.ENCRYPT_MODE, lowKey);
192 byte[] lowHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
193 des.init(Cipher.ENCRYPT_MODE, highKey);
194 byte[] highHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
195 byte[] lmHash = new byte[16];
196 System.arraycopy(lowHash, 0, lmHash, 0, 8);
197 System.arraycopy(highHash, 0, lmHash, 8, 8);
198
199 return lmHash;
200 }
201
202
203
204
205
206
207
208
209
210 private static byte[] ntlmHash(String password) throws Exception {
211 byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
212 MessageDigest md4 = MessageDigest.getInstance("MD4");
213
214 return md4.digest(unicodePassword);
215 }
216
217
218
219
220
221
222
223
224
225
226
227 private static byte[] ntlmv2Hash(String target, String user, String password) throws Exception {
228 byte[] ntlmHash = ntlmHash(password);
229 String identity = user.toUpperCase() + target;
230
231 return hmacMD5(identity.getBytes("UnicodeLittleUnmarked"), ntlmHash);
232 }
233
234
235
236
237
238
239
240
241
242
243 private static byte[] lmResponse(byte[] hash, byte[] challenge) throws Exception {
244 byte[] keyBytes = new byte[21];
245 System.arraycopy(hash, 0, keyBytes, 0, 16);
246 Key lowKey = createDESKey(keyBytes, 0);
247 Key middleKey = createDESKey(keyBytes, 7);
248 Key highKey = createDESKey(keyBytes, 14);
249 Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
250 des.init(Cipher.ENCRYPT_MODE, lowKey);
251 byte[] lowResponse = des.doFinal(challenge);
252 des.init(Cipher.ENCRYPT_MODE, middleKey);
253 byte[] middleResponse = des.doFinal(challenge);
254 des.init(Cipher.ENCRYPT_MODE, highKey);
255 byte[] highResponse = des.doFinal(challenge);
256 byte[] lmResponse = new byte[24];
257 System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
258 System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
259 System.arraycopy(highResponse, 0, lmResponse, 16, 8);
260
261 return lmResponse;
262 }
263
264
265
266
267
268
269
270
271
272
273
274
275 private static byte[] lmv2Response(byte[] hash, byte[] clientData, byte[] challenge) throws Exception {
276 byte[] data = new byte[challenge.length + clientData.length];
277 System.arraycopy(challenge, 0, data, 0, challenge.length);
278 System.arraycopy(clientData, 0, data, challenge.length, clientData.length);
279 byte[] mac = hmacMD5(data, hash);
280 byte[] lmv2Response = new byte[mac.length + clientData.length];
281 System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
282 System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
283
284 return lmv2Response;
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298 private static byte[] createBlob(byte[] targetInformation, byte[] clientNonce, long time) {
299 byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
300 byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
301 byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
302 byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
303 time += 11644473600000l;
304 time *= 10000;
305
306 byte[] timestamp = new byte[8];
307
308 for (int i = 0; i < 8; i++) {
309 timestamp[i] = (byte) time;
310 time >>>= 8;
311 }
312
313 byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + clientNonce.length
314 + unknown1.length + targetInformation.length + unknown2.length];
315 int offset = 0;
316 System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
317 offset += blobSignature.length;
318 System.arraycopy(reserved, 0, blob, offset, reserved.length);
319 offset += reserved.length;
320 System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
321 offset += timestamp.length;
322 System.arraycopy(clientNonce, 0, blob, offset, clientNonce.length);
323 offset += clientNonce.length;
324 System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
325 offset += unknown1.length;
326 System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
327 offset += targetInformation.length;
328 System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
329
330 return blob;
331 }
332
333
334
335
336
337
338
339
340
341
342
343 public static byte[] hmacMD5(byte[] data, byte[] key) throws Exception {
344 byte[] ipad = new byte[64];
345 byte[] opad = new byte[64];
346
347
348 for (int i = 0; i < 64; i++) {
349 if (i < key.length) {
350 ipad[i] = (byte) (key[i] ^ 0x36);
351 opad[i] = (byte) (key[i] ^ 0x5c);
352 } else {
353 ipad[i] = 0x36;
354 opad[i] = 0x5c;
355 }
356 }
357
358 byte[] content = new byte[data.length + 64];
359 System.arraycopy(ipad, 0, content, 0, 64);
360 System.arraycopy(data, 0, content, 64, data.length);
361 MessageDigest md5 = MessageDigest.getInstance("MD5");
362 byte[] digestedData = md5.digest(content);
363 content = new byte[digestedData.length + 64];
364 System.arraycopy(opad, 0, content, 0, 64);
365 System.arraycopy(digestedData, 0, content, 64, digestedData.length);
366
367 return md5.digest(content);
368 }
369
370
371
372
373
374
375
376
377
378
379
380 private static Key createDESKey(byte[] bytes, int offset) {
381 byte[] keyBytes = new byte[7];
382 System.arraycopy(bytes, offset, keyBytes, 0, 7);
383 byte[] material = new byte[8];
384 material[0] = keyBytes[0];
385 material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
386 material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
387 material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
388 material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
389 material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
390 material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
391 material[7] = (byte) (keyBytes[6] << 1);
392 oddParity(material);
393
394 return new SecretKeySpec(material, "DES");
395 }
396
397
398
399
400
401
402
403 private static void oddParity(byte[] bytes) {
404 for (int i = 0; i < bytes.length; i++) {
405 byte b = bytes[i];
406 boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
407
408 if (needsParity) {
409 bytes[i] |= (byte) 0x01;
410 } else {
411 bytes[i] &= (byte) 0xfe;
412 }
413 }
414 }
415 }