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