Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /*
2 * ====================================================================
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ====================================================================
19 *
20 * This software consists of voluntary contributions made by many
21 * individuals on behalf of the Apache Software Foundation. For more
22 * information on the Apache Software Foundation, please see
23 * <http://www.apache.org/>.
24 *
25 */
27 package ch.boye.httpclientandroidlib.impl.auth;
29 import java.security.Key;
30 import java.security.MessageDigest;
31 import java.util.Arrays;
33 import javax.crypto.Cipher;
34 import javax.crypto.spec.SecretKeySpec;
36 import org.mozilla.apache.commons.codec.binary.Base64;
37 import ch.boye.httpclientandroidlib.util.EncodingUtils;
39 /**
40 * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM
41 * authentication protocol.
42 *
43 * @since 4.1
44 */
45 final class NTLMEngineImpl implements NTLMEngine {
47 // Flags we use
48 protected final static int FLAG_UNICODE_ENCODING = 0x00000001;
49 protected final static int FLAG_TARGET_DESIRED = 0x00000004;
50 protected final static int FLAG_NEGOTIATE_SIGN = 0x00000010;
51 protected final static int FLAG_NEGOTIATE_SEAL = 0x00000020;
52 protected final static int FLAG_NEGOTIATE_NTLM = 0x00000200;
53 protected final static int FLAG_NEGOTIATE_ALWAYS_SIGN = 0x00008000;
54 protected final static int FLAG_NEGOTIATE_NTLM2 = 0x00080000;
55 protected final static int FLAG_NEGOTIATE_128 = 0x20000000;
56 protected final static int FLAG_NEGOTIATE_KEY_EXCH = 0x40000000;
58 /** Secure random generator */
59 private static final java.security.SecureRandom RND_GEN;
60 static {
61 java.security.SecureRandom rnd = null;
62 try {
63 rnd = java.security.SecureRandom.getInstance("SHA1PRNG");
64 } catch (Exception e) {
65 }
66 RND_GEN = rnd;
67 }
69 /** Character encoding */
70 static final String DEFAULT_CHARSET = "ASCII";
72 /** The character set to use for encoding the credentials */
73 private String credentialCharset = DEFAULT_CHARSET;
75 /** The signature string as bytes in the default encoding */
76 private static byte[] SIGNATURE;
78 static {
79 byte[] bytesWithoutNull = EncodingUtils.getBytes("NTLMSSP", "ASCII");
80 SIGNATURE = new byte[bytesWithoutNull.length + 1];
81 System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length);
82 SIGNATURE[bytesWithoutNull.length] = (byte) 0x00;
83 }
85 /**
86 * Returns the response for the given message.
87 *
88 * @param message
89 * the message that was received from the server.
90 * @param username
91 * the username to authenticate with.
92 * @param password
93 * the password to authenticate with.
94 * @param host
95 * The host.
96 * @param domain
97 * the NT domain to authenticate in.
98 * @return The response.
99 * @throws HttpException
100 * If the messages cannot be retrieved.
101 */
102 final String getResponseFor(String message, String username, String password,
103 String host, String domain) throws NTLMEngineException {
105 final String response;
106 if (message == null || message.trim().equals("")) {
107 response = getType1Message(host, domain);
108 } else {
109 Type2Message t2m = new Type2Message(message);
110 response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m
111 .getFlags(), t2m.getTarget(), t2m.getTargetInfo());
112 }
113 return response;
114 }
116 /**
117 * Creates the first message (type 1 message) in the NTLM authentication
118 * sequence. This message includes the user name, domain and host for the
119 * authentication session.
120 *
121 * @param host
122 * the computer name of the host requesting authentication.
123 * @param domain
124 * The domain to authenticate with.
125 * @return String the message to add to the HTTP request header.
126 */
127 String getType1Message(String host, String domain) throws NTLMEngineException {
128 return new Type1Message(domain, host).getResponse();
129 }
131 /**
132 * Creates the type 3 message using the given server nonce. The type 3
133 * message includes all the information for authentication, host, domain,
134 * username and the result of encrypting the nonce sent by the server using
135 * the user's password as the key.
136 *
137 * @param user
138 * The user name. This should not include the domain name.
139 * @param password
140 * The password.
141 * @param host
142 * The host that is originating the authentication request.
143 * @param domain
144 * The domain to authenticate within.
145 * @param nonce
146 * the 8 byte array the server sent.
147 * @return The type 3 message.
148 * @throws NTLMEngineException
149 * If {@encrypt(byte[],byte[])} fails.
150 */
151 String getType3Message(String user, String password, String host, String domain,
152 byte[] nonce, int type2Flags, String target, byte[] targetInformation)
153 throws NTLMEngineException {
154 return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
155 targetInformation).getResponse();
156 }
158 /**
159 * @return Returns the credentialCharset.
160 */
161 String getCredentialCharset() {
162 return credentialCharset;
163 }
165 /**
166 * @param credentialCharset
167 * The credentialCharset to set.
168 */
169 void setCredentialCharset(String credentialCharset) {
170 this.credentialCharset = credentialCharset;
171 }
173 /** Strip dot suffix from a name */
174 private static String stripDotSuffix(String value) {
175 int index = value.indexOf(".");
176 if (index != -1)
177 return value.substring(0, index);
178 return value;
179 }
181 /** Convert host to standard form */
182 private static String convertHost(String host) {
183 return stripDotSuffix(host);
184 }
186 /** Convert domain to standard form */
187 private static String convertDomain(String domain) {
188 return stripDotSuffix(domain);
189 }
191 private static int readULong(byte[] src, int index) throws NTLMEngineException {
192 if (src.length < index + 4)
193 throw new NTLMEngineException("NTLM authentication - buffer too small for DWORD");
194 return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8)
195 | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24);
196 }
198 private static int readUShort(byte[] src, int index) throws NTLMEngineException {
199 if (src.length < index + 2)
200 throw new NTLMEngineException("NTLM authentication - buffer too small for WORD");
201 return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);
202 }
204 private static byte[] readSecurityBuffer(byte[] src, int index) throws NTLMEngineException {
205 int length = readUShort(src, index);
206 int offset = readULong(src, index + 4);
207 if (src.length < offset + length)
208 throw new NTLMEngineException(
209 "NTLM authentication - buffer too small for data item");
210 byte[] buffer = new byte[length];
211 System.arraycopy(src, offset, buffer, 0, length);
212 return buffer;
213 }
215 /** Calculate a challenge block */
216 private static byte[] makeRandomChallenge() throws NTLMEngineException {
217 if (RND_GEN == null) {
218 throw new NTLMEngineException("Random generator not available");
219 }
220 byte[] rval = new byte[8];
221 synchronized (RND_GEN) {
222 RND_GEN.nextBytes(rval);
223 }
224 return rval;
225 }
227 /** Calculate an NTLM2 challenge block */
228 private static byte[] makeNTLM2RandomChallenge() throws NTLMEngineException {
229 if (RND_GEN == null) {
230 throw new NTLMEngineException("Random generator not available");
231 }
232 byte[] rval = new byte[24];
233 synchronized (RND_GEN) {
234 RND_GEN.nextBytes(rval);
235 }
236 // 8-byte challenge, padded with zeros to 24 bytes.
237 Arrays.fill(rval, 8, 24, (byte) 0x00);
238 return rval;
239 }
241 /**
242 * Calculates the LM Response for the given challenge, using the specified
243 * password.
244 *
245 * @param password
246 * The user's password.
247 * @param challenge
248 * The Type 2 challenge from the server.
249 *
250 * @return The LM Response.
251 */
252 static byte[] getLMResponse(String password, byte[] challenge)
253 throws NTLMEngineException {
254 byte[] lmHash = lmHash(password);
255 return lmResponse(lmHash, challenge);
256 }
258 /**
259 * Calculates the NTLM Response for the given challenge, using the specified
260 * password.
261 *
262 * @param password
263 * The user's password.
264 * @param challenge
265 * The Type 2 challenge from the server.
266 *
267 * @return The NTLM Response.
268 */
269 static byte[] getNTLMResponse(String password, byte[] challenge)
270 throws NTLMEngineException {
271 byte[] ntlmHash = ntlmHash(password);
272 return lmResponse(ntlmHash, challenge);
273 }
275 /**
276 * Calculates the NTLMv2 Response for the given challenge, using the
277 * specified authentication target, username, password, target information
278 * block, and client challenge.
279 *
280 * @param target
281 * The authentication target (i.e., domain).
282 * @param user
283 * The username.
284 * @param password
285 * The user's password.
286 * @param targetInformation
287 * The target information block from the Type 2 message.
288 * @param challenge
289 * The Type 2 challenge from the server.
290 * @param clientChallenge
291 * The random 8-byte client challenge.
292 *
293 * @return The NTLMv2 Response.
294 */
295 static byte[] getNTLMv2Response(String target, String user, String password,
296 byte[] challenge, byte[] clientChallenge, byte[] targetInformation)
297 throws NTLMEngineException {
298 byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
299 byte[] blob = createBlob(clientChallenge, targetInformation);
300 return lmv2Response(ntlmv2Hash, challenge, blob);
301 }
303 /**
304 * Calculates the LMv2 Response for the given challenge, using the specified
305 * authentication target, username, password, and client challenge.
306 *
307 * @param target
308 * The authentication target (i.e., domain).
309 * @param user
310 * The username.
311 * @param password
312 * The user's password.
313 * @param challenge
314 * The Type 2 challenge from the server.
315 * @param clientChallenge
316 * The random 8-byte client challenge.
317 *
318 * @return The LMv2 Response.
319 */
320 static byte[] getLMv2Response(String target, String user, String password,
321 byte[] challenge, byte[] clientChallenge) throws NTLMEngineException {
322 byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
323 return lmv2Response(ntlmv2Hash, challenge, clientChallenge);
324 }
326 /**
327 * Calculates the NTLM2 Session Response for the given challenge, using the
328 * specified password and client challenge.
329 *
330 * @param password
331 * The user's password.
332 * @param challenge
333 * The Type 2 challenge from the server.
334 * @param clientChallenge
335 * The random 8-byte client challenge.
336 *
337 * @return The NTLM2 Session Response. This is placed in the NTLM response
338 * field of the Type 3 message; the LM response field contains the
339 * client challenge, null-padded to 24 bytes.
340 */
341 static byte[] getNTLM2SessionResponse(String password, byte[] challenge,
342 byte[] clientChallenge) throws NTLMEngineException {
343 try {
344 byte[] ntlmHash = ntlmHash(password);
346 // Look up MD5 algorithm (was necessary on jdk 1.4.2)
347 // This used to be needed, but java 1.5.0_07 includes the MD5
348 // algorithm (finally)
349 // Class x = Class.forName("gnu.crypto.hash.MD5");
350 // Method updateMethod = x.getMethod("update",new
351 // Class[]{byte[].class});
352 // Method digestMethod = x.getMethod("digest",new Class[0]);
353 // Object mdInstance = x.newInstance();
354 // updateMethod.invoke(mdInstance,new Object[]{challenge});
355 // updateMethod.invoke(mdInstance,new Object[]{clientChallenge});
356 // byte[] digest = (byte[])digestMethod.invoke(mdInstance,new
357 // Object[0]);
359 MessageDigest md5 = MessageDigest.getInstance("MD5");
360 md5.update(challenge);
361 md5.update(clientChallenge);
362 byte[] digest = md5.digest();
364 byte[] sessionHash = new byte[8];
365 System.arraycopy(digest, 0, sessionHash, 0, 8);
366 return lmResponse(ntlmHash, sessionHash);
367 } catch (Exception e) {
368 if (e instanceof NTLMEngineException)
369 throw (NTLMEngineException) e;
370 throw new NTLMEngineException(e.getMessage(), e);
371 }
372 }
374 /**
375 * Creates the LM Hash of the user's password.
376 *
377 * @param password
378 * The password.
379 *
380 * @return The LM Hash of the given password, used in the calculation of the
381 * LM Response.
382 */
383 private static byte[] lmHash(String password) throws NTLMEngineException {
384 try {
385 byte[] oemPassword = password.toUpperCase().getBytes("US-ASCII");
386 int length = Math.min(oemPassword.length, 14);
387 byte[] keyBytes = new byte[14];
388 System.arraycopy(oemPassword, 0, keyBytes, 0, length);
389 Key lowKey = createDESKey(keyBytes, 0);
390 Key highKey = createDESKey(keyBytes, 7);
391 byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII");
392 Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
393 des.init(Cipher.ENCRYPT_MODE, lowKey);
394 byte[] lowHash = des.doFinal(magicConstant);
395 des.init(Cipher.ENCRYPT_MODE, highKey);
396 byte[] highHash = des.doFinal(magicConstant);
397 byte[] lmHash = new byte[16];
398 System.arraycopy(lowHash, 0, lmHash, 0, 8);
399 System.arraycopy(highHash, 0, lmHash, 8, 8);
400 return lmHash;
401 } catch (Exception e) {
402 throw new NTLMEngineException(e.getMessage(), e);
403 }
404 }
406 /**
407 * Creates the NTLM Hash of the user's password.
408 *
409 * @param password
410 * The password.
411 *
412 * @return The NTLM Hash of the given password, used in the calculation of
413 * the NTLM Response and the NTLMv2 and LMv2 Hashes.
414 */
415 private static byte[] ntlmHash(String password) throws NTLMEngineException {
416 try {
417 byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
418 MD4 md4 = new MD4();
419 md4.update(unicodePassword);
420 return md4.getOutput();
421 } catch (java.io.UnsupportedEncodingException e) {
422 throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e);
423 }
424 }
426 /**
427 * Creates the NTLMv2 Hash of the user's password.
428 *
429 * @param target
430 * The authentication target (i.e., domain).
431 * @param user
432 * The username.
433 * @param password
434 * The password.
435 *
436 * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2
437 * Responses.
438 */
439 private static byte[] ntlmv2Hash(String target, String user, String password)
440 throws NTLMEngineException {
441 try {
442 byte[] ntlmHash = ntlmHash(password);
443 HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
444 // Upper case username, mixed case target!!
445 hmacMD5.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked"));
446 hmacMD5.update(target.getBytes("UnicodeLittleUnmarked"));
447 return hmacMD5.getOutput();
448 } catch (java.io.UnsupportedEncodingException e) {
449 throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e);
450 }
451 }
453 /**
454 * Creates the LM Response from the given hash and Type 2 challenge.
455 *
456 * @param hash
457 * The LM or NTLM Hash.
458 * @param challenge
459 * The server challenge from the Type 2 message.
460 *
461 * @return The response (either LM or NTLM, depending on the provided hash).
462 */
463 private static byte[] lmResponse(byte[] hash, byte[] challenge) throws NTLMEngineException {
464 try {
465 byte[] keyBytes = new byte[21];
466 System.arraycopy(hash, 0, keyBytes, 0, 16);
467 Key lowKey = createDESKey(keyBytes, 0);
468 Key middleKey = createDESKey(keyBytes, 7);
469 Key highKey = createDESKey(keyBytes, 14);
470 Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
471 des.init(Cipher.ENCRYPT_MODE, lowKey);
472 byte[] lowResponse = des.doFinal(challenge);
473 des.init(Cipher.ENCRYPT_MODE, middleKey);
474 byte[] middleResponse = des.doFinal(challenge);
475 des.init(Cipher.ENCRYPT_MODE, highKey);
476 byte[] highResponse = des.doFinal(challenge);
477 byte[] lmResponse = new byte[24];
478 System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
479 System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
480 System.arraycopy(highResponse, 0, lmResponse, 16, 8);
481 return lmResponse;
482 } catch (Exception e) {
483 throw new NTLMEngineException(e.getMessage(), e);
484 }
485 }
487 /**
488 * Creates the LMv2 Response from the given hash, client data, and Type 2
489 * challenge.
490 *
491 * @param hash
492 * The NTLMv2 Hash.
493 * @param clientData
494 * The client data (blob or client challenge).
495 * @param challenge
496 * The server challenge from the Type 2 message.
497 *
498 * @return The response (either NTLMv2 or LMv2, depending on the client
499 * data).
500 */
501 private static byte[] lmv2Response(byte[] hash, byte[] challenge, byte[] clientData)
502 throws NTLMEngineException {
503 HMACMD5 hmacMD5 = new HMACMD5(hash);
504 hmacMD5.update(challenge);
505 hmacMD5.update(clientData);
506 byte[] mac = hmacMD5.getOutput();
507 byte[] lmv2Response = new byte[mac.length + clientData.length];
508 System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
509 System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
510 return lmv2Response;
511 }
513 /**
514 * Creates the NTLMv2 blob from the given target information block and
515 * client challenge.
516 *
517 * @param targetInformation
518 * The target information block from the Type 2 message.
519 * @param clientChallenge
520 * The random 8-byte client challenge.
521 *
522 * @return The blob, used in the calculation of the NTLMv2 Response.
523 */
524 private static byte[] createBlob(byte[] clientChallenge, byte[] targetInformation) {
525 byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
526 byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
527 byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
528 long time = System.currentTimeMillis();
529 time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch.
530 time *= 10000; // tenths of a microsecond.
531 // convert to little-endian byte array.
532 byte[] timestamp = new byte[8];
533 for (int i = 0; i < 8; i++) {
534 timestamp[i] = (byte) time;
535 time >>>= 8;
536 }
537 byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8
538 + unknown1.length + targetInformation.length];
539 int offset = 0;
540 System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
541 offset += blobSignature.length;
542 System.arraycopy(reserved, 0, blob, offset, reserved.length);
543 offset += reserved.length;
544 System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
545 offset += timestamp.length;
546 System.arraycopy(clientChallenge, 0, blob, offset, 8);
547 offset += 8;
548 System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
549 offset += unknown1.length;
550 System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
551 return blob;
552 }
554 /**
555 * Creates a DES encryption key from the given key material.
556 *
557 * @param bytes
558 * A byte array containing the DES key material.
559 * @param offset
560 * The offset in the given byte array at which the 7-byte key
561 * material starts.
562 *
563 * @return A DES encryption key created from the key material starting at
564 * the specified offset in the given byte array.
565 */
566 private static Key createDESKey(byte[] bytes, int offset) {
567 byte[] keyBytes = new byte[7];
568 System.arraycopy(bytes, offset, keyBytes, 0, 7);
569 byte[] material = new byte[8];
570 material[0] = keyBytes[0];
571 material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
572 material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
573 material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
574 material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
575 material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
576 material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
577 material[7] = (byte) (keyBytes[6] << 1);
578 oddParity(material);
579 return new SecretKeySpec(material, "DES");
580 }
582 /**
583 * Applies odd parity to the given byte array.
584 *
585 * @param bytes
586 * The data whose parity bits are to be adjusted for odd parity.
587 */
588 private static void oddParity(byte[] bytes) {
589 for (int i = 0; i < bytes.length; i++) {
590 byte b = bytes[i];
591 boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3)
592 ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
593 if (needsParity) {
594 bytes[i] |= (byte) 0x01;
595 } else {
596 bytes[i] &= (byte) 0xfe;
597 }
598 }
599 }
601 /** NTLM message generation, base class */
602 static class NTLMMessage {
603 /** The current response */
604 private byte[] messageContents = null;
606 /** The current output position */
607 private int currentOutputPosition = 0;
609 /** Constructor to use when message contents are not yet known */
610 NTLMMessage() {
611 }
613 /** Constructor to use when message contents are known */
614 NTLMMessage(String messageBody, int expectedType) throws NTLMEngineException {
615 messageContents = Base64.decodeBase64(EncodingUtils.getBytes(messageBody,
616 DEFAULT_CHARSET));
617 // Look for NTLM message
618 if (messageContents.length < SIGNATURE.length)
619 throw new NTLMEngineException("NTLM message decoding error - packet too short");
620 int i = 0;
621 while (i < SIGNATURE.length) {
622 if (messageContents[i] != SIGNATURE[i])
623 throw new NTLMEngineException(
624 "NTLM message expected - instead got unrecognized bytes");
625 i++;
626 }
628 // Check to be sure there's a type 2 message indicator next
629 int type = readULong(SIGNATURE.length);
630 if (type != expectedType)
631 throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType)
632 + " message expected - instead got type " + Integer.toString(type));
634 currentOutputPosition = messageContents.length;
635 }
637 /**
638 * Get the length of the signature and flags, so calculations can adjust
639 * offsets accordingly.
640 */
641 protected int getPreambleLength() {
642 return SIGNATURE.length + 4;
643 }
645 /** Get the message length */
646 protected int getMessageLength() {
647 return currentOutputPosition;
648 }
650 /** Read a byte from a position within the message buffer */
651 protected byte readByte(int position) throws NTLMEngineException {
652 if (messageContents.length < position + 1)
653 throw new NTLMEngineException("NTLM: Message too short");
654 return messageContents[position];
655 }
657 /** Read a bunch of bytes from a position in the message buffer */
658 protected void readBytes(byte[] buffer, int position) throws NTLMEngineException {
659 if (messageContents.length < position + buffer.length)
660 throw new NTLMEngineException("NTLM: Message too short");
661 System.arraycopy(messageContents, position, buffer, 0, buffer.length);
662 }
664 /** Read a ushort from a position within the message buffer */
665 protected int readUShort(int position) throws NTLMEngineException {
666 return NTLMEngineImpl.readUShort(messageContents, position);
667 }
669 /** Read a ulong from a position within the message buffer */
670 protected int readULong(int position) throws NTLMEngineException {
671 return NTLMEngineImpl.readULong(messageContents, position);
672 }
674 /** Read a security buffer from a position within the message buffer */
675 protected byte[] readSecurityBuffer(int position) throws NTLMEngineException {
676 return NTLMEngineImpl.readSecurityBuffer(messageContents, position);
677 }
679 /**
680 * Prepares the object to create a response of the given length.
681 *
682 * @param length
683 * the maximum length of the response to prepare, not
684 * including the type and the signature (which this method
685 * adds).
686 */
687 protected void prepareResponse(int maxlength, int messageType) {
688 messageContents = new byte[maxlength];
689 currentOutputPosition = 0;
690 addBytes(SIGNATURE);
691 addULong(messageType);
692 }
694 /**
695 * Adds the given byte to the response.
696 *
697 * @param b
698 * the byte to add.
699 */
700 protected void addByte(byte b) {
701 messageContents[currentOutputPosition] = b;
702 currentOutputPosition++;
703 }
705 /**
706 * Adds the given bytes to the response.
707 *
708 * @param bytes
709 * the bytes to add.
710 */
711 protected void addBytes(byte[] bytes) {
712 for (int i = 0; i < bytes.length; i++) {
713 messageContents[currentOutputPosition] = bytes[i];
714 currentOutputPosition++;
715 }
716 }
718 /** Adds a USHORT to the response */
719 protected void addUShort(int value) {
720 addByte((byte) (value & 0xff));
721 addByte((byte) (value >> 8 & 0xff));
722 }
724 /** Adds a ULong to the response */
725 protected void addULong(int value) {
726 addByte((byte) (value & 0xff));
727 addByte((byte) (value >> 8 & 0xff));
728 addByte((byte) (value >> 16 & 0xff));
729 addByte((byte) (value >> 24 & 0xff));
730 }
732 /**
733 * Returns the response that has been generated after shrinking the
734 * array if required and base64 encodes the response.
735 *
736 * @return The response as above.
737 */
738 String getResponse() {
739 byte[] resp;
740 if (messageContents.length > currentOutputPosition) {
741 byte[] tmp = new byte[currentOutputPosition];
742 for (int i = 0; i < currentOutputPosition; i++) {
743 tmp[i] = messageContents[i];
744 }
745 resp = tmp;
746 } else {
747 resp = messageContents;
748 }
749 return EncodingUtils.getAsciiString(Base64.encodeBase64(resp));
750 }
752 }
754 /** Type 1 message assembly class */
755 static class Type1Message extends NTLMMessage {
756 protected byte[] hostBytes;
757 protected byte[] domainBytes;
759 /** Constructor. Include the arguments the message will need */
760 Type1Message(String domain, String host) throws NTLMEngineException {
761 super();
762 try {
763 // Strip off domain name from the host!
764 host = convertHost(host);
765 // Use only the base domain name!
766 domain = convertDomain(domain);
768 hostBytes = host.getBytes("UnicodeLittleUnmarked");
769 domainBytes = domain.toUpperCase().getBytes("UnicodeLittleUnmarked");
770 } catch (java.io.UnsupportedEncodingException e) {
771 throw new NTLMEngineException("Unicode unsupported: " + e.getMessage(), e);
772 }
773 }
775 /**
776 * Getting the response involves building the message before returning
777 * it
778 */
779 @Override
780 String getResponse() {
781 // Now, build the message. Calculate its length first, including
782 // signature or type.
783 int finalLength = 32 + hostBytes.length + domainBytes.length;
785 // Set up the response. This will initialize the signature, message
786 // type, and flags.
787 prepareResponse(finalLength, 1);
789 // Flags. These are the complete set of flags we support.
790 addULong(FLAG_NEGOTIATE_NTLM | FLAG_NEGOTIATE_NTLM2 | FLAG_NEGOTIATE_SIGN
791 | FLAG_NEGOTIATE_SEAL |
792 /*
793 * FLAG_NEGOTIATE_ALWAYS_SIGN | FLAG_NEGOTIATE_KEY_EXCH |
794 */
795 FLAG_UNICODE_ENCODING | FLAG_TARGET_DESIRED | FLAG_NEGOTIATE_128);
797 // Domain length (two times).
798 addUShort(domainBytes.length);
799 addUShort(domainBytes.length);
801 // Domain offset.
802 addULong(hostBytes.length + 32);
804 // Host length (two times).
805 addUShort(hostBytes.length);
806 addUShort(hostBytes.length);
808 // Host offset (always 32).
809 addULong(32);
811 // Host String.
812 addBytes(hostBytes);
814 // Domain String.
815 addBytes(domainBytes);
817 return super.getResponse();
818 }
820 }
822 /** Type 2 message class */
823 static class Type2Message extends NTLMMessage {
824 protected byte[] challenge;
825 protected String target;
826 protected byte[] targetInfo;
827 protected int flags;
829 Type2Message(String message) throws NTLMEngineException {
830 super(message, 2);
832 // Parse out the rest of the info we need from the message
833 // The nonce is the 8 bytes starting from the byte in position 24.
834 challenge = new byte[8];
835 readBytes(challenge, 24);
837 flags = readULong(20);
838 if ((flags & FLAG_UNICODE_ENCODING) == 0)
839 throw new NTLMEngineException(
840 "NTLM type 2 message has flags that make no sense: "
841 + Integer.toString(flags));
842 // Do the target!
843 target = null;
844 // The TARGET_DESIRED flag is said to not have understood semantics
845 // in Type2 messages, so use the length of the packet to decide
846 // how to proceed instead
847 if (getMessageLength() >= 12 + 8) {
848 byte[] bytes = readSecurityBuffer(12);
849 if (bytes.length != 0) {
850 try {
851 target = new String(bytes, "UnicodeLittleUnmarked");
852 } catch (java.io.UnsupportedEncodingException e) {
853 throw new NTLMEngineException(e.getMessage(), e);
854 }
855 }
856 }
858 // Do the target info!
859 targetInfo = null;
860 // TARGET_DESIRED flag cannot be relied on, so use packet length
861 if (getMessageLength() >= 40 + 8) {
862 byte[] bytes = readSecurityBuffer(40);
863 if (bytes.length != 0) {
864 targetInfo = bytes;
865 }
866 }
867 }
869 /** Retrieve the challenge */
870 byte[] getChallenge() {
871 return challenge;
872 }
874 /** Retrieve the target */
875 String getTarget() {
876 return target;
877 }
879 /** Retrieve the target info */
880 byte[] getTargetInfo() {
881 return targetInfo;
882 }
884 /** Retrieve the response flags */
885 int getFlags() {
886 return flags;
887 }
889 }
891 /** Type 3 message assembly class */
892 static class Type3Message extends NTLMMessage {
893 // Response flags from the type2 message
894 protected int type2Flags;
896 protected byte[] domainBytes;
897 protected byte[] hostBytes;
898 protected byte[] userBytes;
900 protected byte[] lmResp;
901 protected byte[] ntResp;
903 /** Constructor. Pass the arguments we will need */
904 Type3Message(String domain, String host, String user, String password, byte[] nonce,
905 int type2Flags, String target, byte[] targetInformation)
906 throws NTLMEngineException {
907 // Save the flags
908 this.type2Flags = type2Flags;
910 // Strip off domain name from the host!
911 host = convertHost(host);
912 // Use only the base domain name!
913 domain = convertDomain(domain);
915 // Use the new code to calculate the responses, including v2 if that
916 // seems warranted.
917 try {
918 if (targetInformation != null && target != null) {
919 byte[] clientChallenge = makeRandomChallenge();
920 ntResp = getNTLMv2Response(target, user, password, nonce, clientChallenge,
921 targetInformation);
922 lmResp = getLMv2Response(target, user, password, nonce, clientChallenge);
923 } else {
924 if ((type2Flags & FLAG_NEGOTIATE_NTLM2) != 0) {
925 // NTLM2 session stuff is requested
926 byte[] clientChallenge = makeNTLM2RandomChallenge();
928 ntResp = getNTLM2SessionResponse(password, nonce, clientChallenge);
929 lmResp = clientChallenge;
931 // All the other flags we send (signing, sealing, key
932 // exchange) are supported, but they don't do anything
933 // at all in an
934 // NTLM2 context! So we're done at this point.
935 } else {
936 ntResp = getNTLMResponse(password, nonce);
937 lmResp = getLMResponse(password, nonce);
938 }
939 }
940 } catch (NTLMEngineException e) {
941 // This likely means we couldn't find the MD4 hash algorithm -
942 // fail back to just using LM
943 ntResp = new byte[0];
944 lmResp = getLMResponse(password, nonce);
945 }
947 try {
948 domainBytes = domain.toUpperCase().getBytes("UnicodeLittleUnmarked");
949 hostBytes = host.getBytes("UnicodeLittleUnmarked");
950 userBytes = user.getBytes("UnicodeLittleUnmarked");
951 } catch (java.io.UnsupportedEncodingException e) {
952 throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e);
953 }
954 }
956 /** Assemble the response */
957 @Override
958 String getResponse() {
959 int ntRespLen = ntResp.length;
960 int lmRespLen = lmResp.length;
962 int domainLen = domainBytes.length;
963 int hostLen = hostBytes.length;
964 int userLen = userBytes.length;
966 // Calculate the layout within the packet
967 int lmRespOffset = 64;
968 int ntRespOffset = lmRespOffset + lmRespLen;
969 int domainOffset = ntRespOffset + ntRespLen;
970 int userOffset = domainOffset + domainLen;
971 int hostOffset = userOffset + userLen;
972 int sessionKeyOffset = hostOffset + hostLen;
973 int finalLength = sessionKeyOffset + 0;
975 // Start the response. Length includes signature and type
976 prepareResponse(finalLength, 3);
978 // LM Resp Length (twice)
979 addUShort(lmRespLen);
980 addUShort(lmRespLen);
982 // LM Resp Offset
983 addULong(lmRespOffset);
985 // NT Resp Length (twice)
986 addUShort(ntRespLen);
987 addUShort(ntRespLen);
989 // NT Resp Offset
990 addULong(ntRespOffset);
992 // Domain length (twice)
993 addUShort(domainLen);
994 addUShort(domainLen);
996 // Domain offset.
997 addULong(domainOffset);
999 // User Length (twice)
1000 addUShort(userLen);
1001 addUShort(userLen);
1003 // User offset
1004 addULong(userOffset);
1006 // Host length (twice)
1007 addUShort(hostLen);
1008 addUShort(hostLen);
1010 // Host offset
1011 addULong(hostOffset);
1013 // 4 bytes of zeros - not sure what this is
1014 addULong(0);
1016 // Message length
1017 addULong(finalLength);
1019 // Flags. Currently: NEGOTIATE_NTLM + UNICODE_ENCODING +
1020 // TARGET_DESIRED + NEGOTIATE_128
1021 addULong(FLAG_NEGOTIATE_NTLM | FLAG_UNICODE_ENCODING | FLAG_TARGET_DESIRED
1022 | FLAG_NEGOTIATE_128 | (type2Flags & FLAG_NEGOTIATE_NTLM2)
1023 | (type2Flags & FLAG_NEGOTIATE_SIGN) | (type2Flags & FLAG_NEGOTIATE_SEAL)
1024 | (type2Flags & FLAG_NEGOTIATE_KEY_EXCH)
1025 | (type2Flags & FLAG_NEGOTIATE_ALWAYS_SIGN));
1027 // Add the actual data
1028 addBytes(lmResp);
1029 addBytes(ntResp);
1030 addBytes(domainBytes);
1031 addBytes(userBytes);
1032 addBytes(hostBytes);
1034 return super.getResponse();
1035 }
1036 }
1038 static void writeULong(byte[] buffer, int value, int offset) {
1039 buffer[offset] = (byte) (value & 0xff);
1040 buffer[offset + 1] = (byte) (value >> 8 & 0xff);
1041 buffer[offset + 2] = (byte) (value >> 16 & 0xff);
1042 buffer[offset + 3] = (byte) (value >> 24 & 0xff);
1043 }
1045 static int F(int x, int y, int z) {
1046 return ((x & y) | (~x & z));
1047 }
1049 static int G(int x, int y, int z) {
1050 return ((x & y) | (x & z) | (y & z));
1051 }
1053 static int H(int x, int y, int z) {
1054 return (x ^ y ^ z);
1055 }
1057 static int rotintlft(int val, int numbits) {
1058 return ((val << numbits) | (val >>> (32 - numbits)));
1059 }
1061 /**
1062 * Cryptography support - MD4. The following class was based loosely on the
1063 * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java.
1064 * Code correctness was verified by looking at MD4.java from the jcifs
1065 * library (http://jcifs.samba.org). It was massaged extensively to the
1066 * final form found here by Karl Wright (kwright@metacarta.com).
1067 */
1068 static class MD4 {
1069 protected int A = 0x67452301;
1070 protected int B = 0xefcdab89;
1071 protected int C = 0x98badcfe;
1072 protected int D = 0x10325476;
1073 protected long count = 0L;
1074 protected byte[] dataBuffer = new byte[64];
1076 MD4() {
1077 }
1079 void update(byte[] input) {
1080 // We always deal with 512 bits at a time. Correspondingly, there is
1081 // a buffer 64 bytes long that we write data into until it gets
1082 // full.
1083 int curBufferPos = (int) (count & 63L);
1084 int inputIndex = 0;
1085 while (input.length - inputIndex + curBufferPos >= dataBuffer.length) {
1086 // We have enough data to do the next step. Do a partial copy
1087 // and a transform, updating inputIndex and curBufferPos
1088 // accordingly
1089 int transferAmt = dataBuffer.length - curBufferPos;
1090 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1091 count += transferAmt;
1092 curBufferPos = 0;
1093 inputIndex += transferAmt;
1094 processBuffer();
1095 }
1097 // If there's anything left, copy it into the buffer and leave it.
1098 // We know there's not enough left to process.
1099 if (inputIndex < input.length) {
1100 int transferAmt = input.length - inputIndex;
1101 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1102 count += transferAmt;
1103 curBufferPos += transferAmt;
1104 }
1105 }
1107 byte[] getOutput() {
1108 // Feed pad/length data into engine. This must round out the input
1109 // to a multiple of 512 bits.
1110 int bufferIndex = (int) (count & 63L);
1111 int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
1112 byte[] postBytes = new byte[padLen + 8];
1113 // Leading 0x80, specified amount of zero padding, then length in
1114 // bits.
1115 postBytes[0] = (byte) 0x80;
1116 // Fill out the last 8 bytes with the length
1117 for (int i = 0; i < 8; i++) {
1118 postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i));
1119 }
1121 // Update the engine
1122 update(postBytes);
1124 // Calculate final result
1125 byte[] result = new byte[16];
1126 writeULong(result, A, 0);
1127 writeULong(result, B, 4);
1128 writeULong(result, C, 8);
1129 writeULong(result, D, 12);
1130 return result;
1131 }
1133 protected void processBuffer() {
1134 // Convert current buffer to 16 ulongs
1135 int[] d = new int[16];
1137 for (int i = 0; i < 16; i++) {
1138 d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8)
1139 + ((dataBuffer[i * 4 + 2] & 0xff) << 16)
1140 + ((dataBuffer[i * 4 + 3] & 0xff) << 24);
1141 }
1143 // Do a round of processing
1144 int AA = A;
1145 int BB = B;
1146 int CC = C;
1147 int DD = D;
1148 round1(d);
1149 round2(d);
1150 round3(d);
1151 A += AA;
1152 B += BB;
1153 C += CC;
1154 D += DD;
1156 }
1158 protected void round1(int[] d) {
1159 A = rotintlft((A + F(B, C, D) + d[0]), 3);
1160 D = rotintlft((D + F(A, B, C) + d[1]), 7);
1161 C = rotintlft((C + F(D, A, B) + d[2]), 11);
1162 B = rotintlft((B + F(C, D, A) + d[3]), 19);
1164 A = rotintlft((A + F(B, C, D) + d[4]), 3);
1165 D = rotintlft((D + F(A, B, C) + d[5]), 7);
1166 C = rotintlft((C + F(D, A, B) + d[6]), 11);
1167 B = rotintlft((B + F(C, D, A) + d[7]), 19);
1169 A = rotintlft((A + F(B, C, D) + d[8]), 3);
1170 D = rotintlft((D + F(A, B, C) + d[9]), 7);
1171 C = rotintlft((C + F(D, A, B) + d[10]), 11);
1172 B = rotintlft((B + F(C, D, A) + d[11]), 19);
1174 A = rotintlft((A + F(B, C, D) + d[12]), 3);
1175 D = rotintlft((D + F(A, B, C) + d[13]), 7);
1176 C = rotintlft((C + F(D, A, B) + d[14]), 11);
1177 B = rotintlft((B + F(C, D, A) + d[15]), 19);
1178 }
1180 protected void round2(int[] d) {
1181 A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
1182 D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
1183 C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
1184 B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
1186 A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
1187 D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
1188 C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
1189 B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
1191 A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
1192 D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
1193 C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
1194 B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
1196 A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
1197 D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
1198 C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
1199 B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
1201 }
1203 protected void round3(int[] d) {
1204 A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
1205 D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
1206 C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
1207 B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
1209 A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
1210 D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
1211 C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
1212 B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
1214 A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
1215 D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
1216 C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
1217 B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
1219 A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
1220 D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
1221 C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
1222 B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
1224 }
1226 }
1228 /**
1229 * Cryptography support - HMACMD5 - algorithmically based on various web
1230 * resources by Karl Wright
1231 */
1232 static class HMACMD5 {
1233 protected byte[] ipad;
1234 protected byte[] opad;
1235 protected MessageDigest md5;
1237 HMACMD5(byte[] key) throws NTLMEngineException {
1238 try {
1239 md5 = MessageDigest.getInstance("MD5");
1240 } catch (Exception ex) {
1241 // Umm, the algorithm doesn't exist - throw an
1242 // NTLMEngineException!
1243 throw new NTLMEngineException(
1244 "Error getting md5 message digest implementation: " + ex.getMessage(), ex);
1245 }
1247 // Initialize the pad buffers with the key
1248 ipad = new byte[64];
1249 opad = new byte[64];
1251 int keyLength = key.length;
1252 if (keyLength > 64) {
1253 // Use MD5 of the key instead, as described in RFC 2104
1254 md5.update(key);
1255 key = md5.digest();
1256 keyLength = key.length;
1257 }
1258 int i = 0;
1259 while (i < keyLength) {
1260 ipad[i] = (byte) (key[i] ^ (byte) 0x36);
1261 opad[i] = (byte) (key[i] ^ (byte) 0x5c);
1262 i++;
1263 }
1264 while (i < 64) {
1265 ipad[i] = (byte) 0x36;
1266 opad[i] = (byte) 0x5c;
1267 i++;
1268 }
1270 // Very important: update the digest with the ipad buffer
1271 md5.reset();
1272 md5.update(ipad);
1274 }
1276 /** Grab the current digest. This is the "answer". */
1277 byte[] getOutput() {
1278 byte[] digest = md5.digest();
1279 md5.update(opad);
1280 return md5.digest(digest);
1281 }
1283 /** Update by adding a complete array */
1284 void update(byte[] input) {
1285 md5.update(input);
1286 }
1288 /** Update the algorithm */
1289 void update(byte[] input, int offset, int length) {
1290 md5.update(input, offset, length);
1291 }
1293 }
1295 public String generateType1Msg(
1296 final String domain,
1297 final String workstation) throws NTLMEngineException {
1298 return getType1Message(workstation, domain);
1299 }
1301 public String generateType3Msg(
1302 final String username,
1303 final String password,
1304 final String domain,
1305 final String workstation,
1306 final String challenge) throws NTLMEngineException {
1307 Type2Message t2m = new Type2Message(challenge);
1308 return getType3Message(
1309 username,
1310 password,
1311 workstation,
1312 domain,
1313 t2m.getChallenge(),
1314 t2m.getFlags(),
1315 t2m.getTarget(),
1316 t2m.getTargetInfo());
1317 }
1319 }