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