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