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) throws Exception {
66 byte[] lmHash = lmHash(password);
67 return lmResponse(lmHash, challenge);
68 }
69
70
71
72
73
74
75
76
77
78
79 public static byte[] getNTLMResponse(String password, byte[] challenge) throws Exception {
80 byte[] ntlmHash = ntlmHash(password);
81 return lmResponse(ntlmHash, challenge);
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 public static byte[] getNTLMv2Response(String target, String user, String password, byte[] targetInformation,
123 byte[] challenge, byte[] clientNonce, long time) throws Exception {
124 byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
125 byte[] blob = createBlob(targetInformation, clientNonce, time);
126 return lmv2Response(ntlmv2Hash, blob, challenge);
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 public static byte[] getLMv2Response(String target, String user, String password, byte[] challenge,
143 byte[] clientNonce) throws Exception {
144 byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
145 return lmv2Response(ntlmv2Hash, clientNonce, challenge);
146 }
147
148
149
150
151
152
153
154
155
156
157
158
159
160 public static byte[] getNTLM2SessionResponse(String password, byte[] challenge, byte[] clientNonce)
161 throws Exception {
162 byte[] ntlmHash = ntlmHash(password);
163 MessageDigest md5 = MessageDigest.getInstance("MD5");
164 md5.update(challenge);
165 md5.update(clientNonce);
166 byte[] sessionHash = new byte[8];
167 System.arraycopy(md5.digest(), 0, sessionHash, 0, 8);
168 return lmResponse(ntlmHash, sessionHash);
169 }
170
171
172
173
174
175
176
177
178
179 private static byte[] lmHash(String password) throws Exception {
180 byte[] oemPassword = password.toUpperCase().getBytes("US-ASCII");
181 int length = Math.min(oemPassword.length, 14);
182 byte[] keyBytes = new byte[14];
183 System.arraycopy(oemPassword, 0, keyBytes, 0, length);
184 Key lowKey = createDESKey(keyBytes, 0);
185 Key highKey = createDESKey(keyBytes, 7);
186 Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
187 des.init(Cipher.ENCRYPT_MODE, lowKey);
188 byte[] lowHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
189 des.init(Cipher.ENCRYPT_MODE, highKey);
190 byte[] highHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
191 byte[] lmHash = new byte[16];
192 System.arraycopy(lowHash, 0, lmHash, 0, 8);
193 System.arraycopy(highHash, 0, lmHash, 8, 8);
194 return lmHash;
195 }
196
197
198
199
200
201
202
203
204
205 private static byte[] ntlmHash(String password) throws Exception {
206 byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
207 MessageDigest md4 = MessageDigest.getInstance("MD4");
208 return md4.digest(unicodePassword);
209 }
210
211
212
213
214
215
216
217
218
219
220
221 private static byte[] ntlmv2Hash(String target, String user, String password) throws Exception {
222 byte[] ntlmHash = ntlmHash(password);
223 String identity = user.toUpperCase() + target;
224 return hmacMD5(identity.getBytes("UnicodeLittleUnmarked"), ntlmHash);
225 }
226
227
228
229
230
231
232
233
234
235
236 private static byte[] lmResponse(byte[] hash, byte[] challenge) throws Exception {
237 byte[] keyBytes = new byte[21];
238 System.arraycopy(hash, 0, keyBytes, 0, 16);
239 Key lowKey = createDESKey(keyBytes, 0);
240 Key middleKey = createDESKey(keyBytes, 7);
241 Key highKey = createDESKey(keyBytes, 14);
242 Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
243 des.init(Cipher.ENCRYPT_MODE, lowKey);
244 byte[] lowResponse = des.doFinal(challenge);
245 des.init(Cipher.ENCRYPT_MODE, middleKey);
246 byte[] middleResponse = des.doFinal(challenge);
247 des.init(Cipher.ENCRYPT_MODE, highKey);
248 byte[] highResponse = des.doFinal(challenge);
249 byte[] lmResponse = new byte[24];
250 System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
251 System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
252 System.arraycopy(highResponse, 0, lmResponse, 16, 8);
253 return lmResponse;
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267 private static byte[] lmv2Response(byte[] hash, byte[] clientData, byte[] challenge) throws Exception {
268 byte[] data = new byte[challenge.length + clientData.length];
269 System.arraycopy(challenge, 0, data, 0, challenge.length);
270 System.arraycopy(clientData, 0, data, challenge.length, clientData.length);
271 byte[] mac = hmacMD5(data, hash);
272 byte[] lmv2Response = new byte[mac.length + clientData.length];
273 System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
274 System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
275 return lmv2Response;
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289 private static byte[] createBlob(byte[] targetInformation, byte[] clientNonce, long time) {
290 byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
291 byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
292 byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
293 byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
294 time += 11644473600000l;
295 time *= 10000;
296
297 byte[] timestamp = new byte[8];
298 for (int i = 0; i < 8; i++) {
299 timestamp[i] = (byte) time;
300 time >>>= 8;
301 }
302 byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + clientNonce.length
303 + unknown1.length + targetInformation.length + unknown2.length];
304 int offset = 0;
305 System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
306 offset += blobSignature.length;
307 System.arraycopy(reserved, 0, blob, offset, reserved.length);
308 offset += reserved.length;
309 System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
310 offset += timestamp.length;
311 System.arraycopy(clientNonce, 0, blob, offset, clientNonce.length);
312 offset += clientNonce.length;
313 System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
314 offset += unknown1.length;
315 System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
316 offset += targetInformation.length;
317 System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
318 return blob;
319 }
320
321
322
323
324
325
326
327
328
329
330 public static byte[] hmacMD5(byte[] data, byte[] key) throws Exception {
331 byte[] ipad = new byte[64];
332 byte[] opad = new byte[64];
333
334
335 for (int i = 0; i < 64; i++) {
336 if (i < key.length) {
337 ipad[i] = (byte) (key[i] ^ 0x36);
338 opad[i] = (byte) (key[i] ^ 0x5c);
339 } else {
340 ipad[i] = 0x36;
341 opad[i] = 0x5c;
342 }
343 }
344
345 byte[] content = new byte[data.length + 64];
346 System.arraycopy(ipad, 0, content, 0, 64);
347 System.arraycopy(data, 0, content, 64, data.length);
348 MessageDigest md5 = MessageDigest.getInstance("MD5");
349 data = md5.digest(content);
350 content = new byte[data.length + 64];
351 System.arraycopy(opad, 0, content, 0, 64);
352 System.arraycopy(data, 0, content, 64, data.length);
353 return md5.digest(content);
354 }
355
356
357
358
359
360
361
362
363
364
365
366 private static Key createDESKey(byte[] bytes, int offset) {
367 byte[] keyBytes = new byte[7];
368 System.arraycopy(bytes, offset, keyBytes, 0, 7);
369 byte[] material = new byte[8];
370 material[0] = keyBytes[0];
371 material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
372 material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
373 material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
374 material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
375 material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
376 material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
377 material[7] = (byte) (keyBytes[6] << 1);
378 oddParity(material);
379 return new SecretKeySpec(material, "DES");
380 }
381
382
383
384
385
386
387
388 private static void oddParity(byte[] bytes) {
389 for (int i = 0; i < bytes.length; i++) {
390 byte b = bytes[i];
391 boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
392 if (needsParity) {
393 bytes[i] |= (byte) 0x01;
394 } else {
395 bytes[i] &= (byte) 0xfe;
396 }
397 }
398 }
399 }