1 /* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with 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, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 * 21 * This software consists of voluntary contributions made by many 22 * individuals on behalf of the Apache Software Foundation. For more 23 * information on the Apache Software Foundation, please see 24 * <http://www.apache.org/>. 25 * 26 */ 27 package ch.boye.httpclientandroidlib.impl.auth; 28 29 import java.io.UnsupportedEncodingException; 30 import java.security.Key; 31 import java.security.MessageDigest; 32 import java.util.Arrays; 33 import java.util.Locale; 34 35 import javax.crypto.Cipher; 36 import javax.crypto.spec.SecretKeySpec; 37 38 import org.mozilla.apache.commons.codec.binary.Base64; 39 40 import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; 41 import ch.boye.httpclientandroidlib.util.EncodingUtils; 42 43 /** 44 * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM 45 * authentication protocol. 46 * 47 * @since 4.1 48 */ 49 @NotThreadSafe 50 final class NTLMEngineImpl implements NTLMEngine { 51 52 // Flags we use; descriptions according to: 53 // http://davenport.sourceforge.net/ntlm.html 54 // and 55 // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx 56 protected static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001; // Unicode string encoding requested 57 protected static final int FLAG_REQUEST_TARGET = 0x00000004; // Requests target field 58 protected static final int FLAG_REQUEST_SIGN = 0x00000010; // Requests all messages have a signature attached, in NEGOTIATE message. 59 protected static final int FLAG_REQUEST_SEAL = 0x00000020; // Request key exchange for message confidentiality in NEGOTIATE message. MUST be used in conjunction with 56BIT. 60 protected static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080; // Request Lan Manager key instead of user session key 61 protected static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security. MUST be set in NEGOTIATE and CHALLENGE both 62 protected static final int FLAG_DOMAIN_PRESENT = 0x00001000; // Domain is present in message 63 protected static final int FLAG_WORKSTATION_PRESENT = 0x00002000; // Workstation is present in message 64 protected static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000; // Requests a signature block on all messages. Overridden by REQUEST_SIGN and REQUEST_SEAL. 65 protected static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security 66 protected static final int FLAG_REQUEST_VERSION = 0x02000000; // Request protocol version 67 protected static final int FLAG_TARGETINFO_PRESENT = 0x00800000; // From server in challenge message, indicating targetinfo is present 68 protected static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange 69 protected static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000; // Request explicit key exchange 70 protected static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000; // Must be used in conjunction with SEAL 71 72 73 /** Secure random generator */ 74 private static final java.security.SecureRandom RND_GEN; 75 static { 76 java.security.SecureRandom rnd = null; 77 try { 78 rnd = java.security.SecureRandom.getInstance("SHA1PRNG"); 79 } catch (final Exception ignore) { 80 } 81 RND_GEN = rnd; 82 } 83 84 /** Character encoding */ 85 static final String DEFAULT_CHARSET = "ASCII"; 86 87 /** The character set to use for encoding the credentials */ 88 private String credentialCharset = DEFAULT_CHARSET; 89 90 /** The signature string as bytes in the default encoding */ 91 private static final byte[] SIGNATURE; 92 93 static { 94 final byte[] bytesWithoutNull = EncodingUtils.getBytes("NTLMSSP", "ASCII"); 95 SIGNATURE = new byte[bytesWithoutNull.length + 1]; System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length)96 System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length); 97 SIGNATURE[bytesWithoutNull.length] = (byte) 0x00; 98 } 99 100 /** 101 * Returns the response for the given message. 102 * 103 * @param message 104 * the message that was received from the server. 105 * @param username 106 * the username to authenticate with. 107 * @param password 108 * the password to authenticate with. 109 * @param host 110 * The host. 111 * @param domain 112 * the NT domain to authenticate in. 113 * @return The response. 114 * @throws ch.boye.httpclientandroidlib.HttpException 115 * If the messages cannot be retrieved. 116 */ getResponseFor(final String message, final String username, final String password, final String host, final String domain)117 final String getResponseFor(final String message, final String username, final String password, 118 final String host, final String domain) throws NTLMEngineException { 119 120 final String response; 121 if (message == null || message.trim().equals("")) { 122 response = getType1Message(host, domain); 123 } else { 124 final Type2Message t2m = new Type2Message(message); 125 response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m 126 .getFlags(), t2m.getTarget(), t2m.getTargetInfo()); 127 } 128 return response; 129 } 130 131 /** 132 * Creates the first message (type 1 message) in the NTLM authentication 133 * sequence. This message includes the user name, domain and host for the 134 * authentication session. 135 * 136 * @param host 137 * the computer name of the host requesting authentication. 138 * @param domain 139 * The domain to authenticate with. 140 * @return String the message to add to the HTTP request header. 141 */ getType1Message(final String host, final String domain)142 String getType1Message(final String host, final String domain) throws NTLMEngineException { 143 return new Type1Message(domain, host).getResponse(); 144 } 145 146 /** 147 * Creates the type 3 message using the given server nonce. The type 3 148 * message includes all the information for authentication, host, domain, 149 * username and the result of encrypting the nonce sent by the server using 150 * the user's password as the key. 151 * 152 * @param user 153 * The user name. This should not include the domain name. 154 * @param password 155 * The password. 156 * @param host 157 * The host that is originating the authentication request. 158 * @param domain 159 * The domain to authenticate within. 160 * @param nonce 161 * the 8 byte array the server sent. 162 * @return The type 3 message. 163 * @throws NTLMEngineException 164 * If {@link #RC4(byte[],byte[])} fails. 165 */ getType3Message(final String user, final String password, final String host, final String domain, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation)166 String getType3Message(final String user, final String password, final String host, final String domain, 167 final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation) 168 throws NTLMEngineException { 169 return new Type3Message(domain, host, user, password, nonce, type2Flags, target, 170 targetInformation).getResponse(); 171 } 172 173 /** 174 * @return Returns the credentialCharset. 175 */ getCredentialCharset()176 String getCredentialCharset() { 177 return credentialCharset; 178 } 179 180 /** 181 * @param credentialCharset 182 * The credentialCharset to set. 183 */ setCredentialCharset(final String credentialCharset)184 void setCredentialCharset(final String credentialCharset) { 185 this.credentialCharset = credentialCharset; 186 } 187 188 /** Strip dot suffix from a name */ stripDotSuffix(final String value)189 private static String stripDotSuffix(final String value) { 190 if (value == null) { 191 return null; 192 } 193 final int index = value.indexOf("."); 194 if (index != -1) { 195 return value.substring(0, index); 196 } 197 return value; 198 } 199 200 /** Convert host to standard form */ convertHost(final String host)201 private static String convertHost(final String host) { 202 return stripDotSuffix(host); 203 } 204 205 /** Convert domain to standard form */ convertDomain(final String domain)206 private static String convertDomain(final String domain) { 207 return stripDotSuffix(domain); 208 } 209 readULong(final byte[] src, final int index)210 private static int readULong(final byte[] src, final int index) throws NTLMEngineException { 211 if (src.length < index + 4) { 212 throw new NTLMEngineException("NTLM authentication - buffer too small for DWORD"); 213 } 214 return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8) 215 | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24); 216 } 217 readUShort(final byte[] src, final int index)218 private static int readUShort(final byte[] src, final int index) throws NTLMEngineException { 219 if (src.length < index + 2) { 220 throw new NTLMEngineException("NTLM authentication - buffer too small for WORD"); 221 } 222 return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8); 223 } 224 readSecurityBuffer(final byte[] src, final int index)225 private static byte[] readSecurityBuffer(final byte[] src, final int index) throws NTLMEngineException { 226 final int length = readUShort(src, index); 227 final int offset = readULong(src, index + 4); 228 if (src.length < offset + length) { 229 throw new NTLMEngineException( 230 "NTLM authentication - buffer too small for data item"); 231 } 232 final byte[] buffer = new byte[length]; 233 System.arraycopy(src, offset, buffer, 0, length); 234 return buffer; 235 } 236 237 /** Calculate a challenge block */ makeRandomChallenge()238 private static byte[] makeRandomChallenge() throws NTLMEngineException { 239 if (RND_GEN == null) { 240 throw new NTLMEngineException("Random generator not available"); 241 } 242 final byte[] rval = new byte[8]; 243 synchronized (RND_GEN) { 244 RND_GEN.nextBytes(rval); 245 } 246 return rval; 247 } 248 249 /** Calculate a 16-byte secondary key */ makeSecondaryKey()250 private static byte[] makeSecondaryKey() throws NTLMEngineException { 251 if (RND_GEN == null) { 252 throw new NTLMEngineException("Random generator not available"); 253 } 254 final byte[] rval = new byte[16]; 255 synchronized (RND_GEN) { 256 RND_GEN.nextBytes(rval); 257 } 258 return rval; 259 } 260 261 protected static class CipherGen { 262 263 protected final String domain; 264 protected final String user; 265 protected final String password; 266 protected final byte[] challenge; 267 protected final String target; 268 protected final byte[] targetInformation; 269 270 // Information we can generate but may be passed in (for testing) 271 protected byte[] clientChallenge; 272 protected byte[] clientChallenge2; 273 protected byte[] secondaryKey; 274 protected byte[] timestamp; 275 276 // Stuff we always generate 277 protected byte[] lmHash = null; 278 protected byte[] lmResponse = null; 279 protected byte[] ntlmHash = null; 280 protected byte[] ntlmResponse = null; 281 protected byte[] ntlmv2Hash = null; 282 protected byte[] lmv2Hash = null; 283 protected byte[] lmv2Response = null; 284 protected byte[] ntlmv2Blob = null; 285 protected byte[] ntlmv2Response = null; 286 protected byte[] ntlm2SessionResponse = null; 287 protected byte[] lm2SessionResponse = null; 288 protected byte[] lmUserSessionKey = null; 289 protected byte[] ntlmUserSessionKey = null; 290 protected byte[] ntlmv2UserSessionKey = null; 291 protected byte[] ntlm2SessionResponseUserSessionKey = null; 292 protected byte[] lanManagerSessionKey = null; 293 CipherGen(final String domain, final String user, final String password, final byte[] challenge, final String target, final byte[] targetInformation, final byte[] clientChallenge, final byte[] clientChallenge2, final byte[] secondaryKey, final byte[] timestamp)294 public CipherGen(final String domain, final String user, final String password, 295 final byte[] challenge, final String target, final byte[] targetInformation, 296 final byte[] clientChallenge, final byte[] clientChallenge2, 297 final byte[] secondaryKey, final byte[] timestamp) { 298 this.domain = domain; 299 this.target = target; 300 this.user = user; 301 this.password = password; 302 this.challenge = challenge; 303 this.targetInformation = targetInformation; 304 this.clientChallenge = clientChallenge; 305 this.clientChallenge2 = clientChallenge2; 306 this.secondaryKey = secondaryKey; 307 this.timestamp = timestamp; 308 } 309 CipherGen(final String domain, final String user, final String password, final byte[] challenge, final String target, final byte[] targetInformation)310 public CipherGen(final String domain, final String user, final String password, 311 final byte[] challenge, final String target, final byte[] targetInformation) { 312 this(domain, user, password, challenge, target, targetInformation, null, null, null, null); 313 } 314 315 /** Calculate and return client challenge */ getClientChallenge()316 public byte[] getClientChallenge() 317 throws NTLMEngineException { 318 if (clientChallenge == null) { 319 clientChallenge = makeRandomChallenge(); 320 } 321 return clientChallenge; 322 } 323 324 /** Calculate and return second client challenge */ getClientChallenge2()325 public byte[] getClientChallenge2() 326 throws NTLMEngineException { 327 if (clientChallenge2 == null) { 328 clientChallenge2 = makeRandomChallenge(); 329 } 330 return clientChallenge2; 331 } 332 333 /** Calculate and return random secondary key */ getSecondaryKey()334 public byte[] getSecondaryKey() 335 throws NTLMEngineException { 336 if (secondaryKey == null) { 337 secondaryKey = makeSecondaryKey(); 338 } 339 return secondaryKey; 340 } 341 342 /** Calculate and return the LMHash */ getLMHash()343 public byte[] getLMHash() 344 throws NTLMEngineException { 345 if (lmHash == null) { 346 lmHash = lmHash(password); 347 } 348 return lmHash; 349 } 350 351 /** Calculate and return the LMResponse */ getLMResponse()352 public byte[] getLMResponse() 353 throws NTLMEngineException { 354 if (lmResponse == null) { 355 lmResponse = lmResponse(getLMHash(),challenge); 356 } 357 return lmResponse; 358 } 359 360 /** Calculate and return the NTLMHash */ getNTLMHash()361 public byte[] getNTLMHash() 362 throws NTLMEngineException { 363 if (ntlmHash == null) { 364 ntlmHash = ntlmHash(password); 365 } 366 return ntlmHash; 367 } 368 369 /** Calculate and return the NTLMResponse */ getNTLMResponse()370 public byte[] getNTLMResponse() 371 throws NTLMEngineException { 372 if (ntlmResponse == null) { 373 ntlmResponse = lmResponse(getNTLMHash(),challenge); 374 } 375 return ntlmResponse; 376 } 377 378 /** Calculate the LMv2 hash */ getLMv2Hash()379 public byte[] getLMv2Hash() 380 throws NTLMEngineException { 381 if (lmv2Hash == null) { 382 lmv2Hash = lmv2Hash(domain, user, getNTLMHash()); 383 } 384 return lmv2Hash; 385 } 386 387 /** Calculate the NTLMv2 hash */ getNTLMv2Hash()388 public byte[] getNTLMv2Hash() 389 throws NTLMEngineException { 390 if (ntlmv2Hash == null) { 391 ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash()); 392 } 393 return ntlmv2Hash; 394 } 395 396 /** Calculate a timestamp */ getTimestamp()397 public byte[] getTimestamp() { 398 if (timestamp == null) { 399 long time = System.currentTimeMillis(); 400 time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch. 401 time *= 10000; // tenths of a microsecond. 402 // convert to little-endian byte array. 403 timestamp = new byte[8]; 404 for (int i = 0; i < 8; i++) { 405 timestamp[i] = (byte) time; 406 time >>>= 8; 407 } 408 } 409 return timestamp; 410 } 411 412 /** Calculate the NTLMv2Blob */ getNTLMv2Blob()413 public byte[] getNTLMv2Blob() 414 throws NTLMEngineException { 415 if (ntlmv2Blob == null) { 416 ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp()); 417 } 418 return ntlmv2Blob; 419 } 420 421 /** Calculate the NTLMv2Response */ getNTLMv2Response()422 public byte[] getNTLMv2Response() 423 throws NTLMEngineException { 424 if (ntlmv2Response == null) { 425 ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob()); 426 } 427 return ntlmv2Response; 428 } 429 430 /** Calculate the LMv2Response */ getLMv2Response()431 public byte[] getLMv2Response() 432 throws NTLMEngineException { 433 if (lmv2Response == null) { 434 lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge()); 435 } 436 return lmv2Response; 437 } 438 439 /** Get NTLM2SessionResponse */ getNTLM2SessionResponse()440 public byte[] getNTLM2SessionResponse() 441 throws NTLMEngineException { 442 if (ntlm2SessionResponse == null) { 443 ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge()); 444 } 445 return ntlm2SessionResponse; 446 } 447 448 /** Calculate and return LM2 session response */ getLM2SessionResponse()449 public byte[] getLM2SessionResponse() 450 throws NTLMEngineException { 451 if (lm2SessionResponse == null) { 452 final byte[] clChallenge = getClientChallenge(); 453 lm2SessionResponse = new byte[24]; 454 System.arraycopy(clChallenge, 0, lm2SessionResponse, 0, clChallenge.length); 455 Arrays.fill(lm2SessionResponse, clChallenge.length, lm2SessionResponse.length, (byte) 0x00); 456 } 457 return lm2SessionResponse; 458 } 459 460 /** Get LMUserSessionKey */ getLMUserSessionKey()461 public byte[] getLMUserSessionKey() 462 throws NTLMEngineException { 463 if (lmUserSessionKey == null) { 464 lmUserSessionKey = new byte[16]; 465 System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8); 466 Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00); 467 } 468 return lmUserSessionKey; 469 } 470 471 /** Get NTLMUserSessionKey */ getNTLMUserSessionKey()472 public byte[] getNTLMUserSessionKey() 473 throws NTLMEngineException { 474 if (ntlmUserSessionKey == null) { 475 final MD4 md4 = new MD4(); 476 md4.update(getNTLMHash()); 477 ntlmUserSessionKey = md4.getOutput(); 478 } 479 return ntlmUserSessionKey; 480 } 481 482 /** GetNTLMv2UserSessionKey */ getNTLMv2UserSessionKey()483 public byte[] getNTLMv2UserSessionKey() 484 throws NTLMEngineException { 485 if (ntlmv2UserSessionKey == null) { 486 final byte[] ntlmv2hash = getNTLMv2Hash(); 487 final byte[] truncatedResponse = new byte[16]; 488 System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16); 489 ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash); 490 } 491 return ntlmv2UserSessionKey; 492 } 493 494 /** Get NTLM2SessionResponseUserSessionKey */ getNTLM2SessionResponseUserSessionKey()495 public byte[] getNTLM2SessionResponseUserSessionKey() 496 throws NTLMEngineException { 497 if (ntlm2SessionResponseUserSessionKey == null) { 498 final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse(); 499 final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length]; 500 System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length); 501 System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length); 502 ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,getNTLMUserSessionKey()); 503 } 504 return ntlm2SessionResponseUserSessionKey; 505 } 506 507 /** Get LAN Manager session key */ getLanManagerSessionKey()508 public byte[] getLanManagerSessionKey() 509 throws NTLMEngineException { 510 if (lanManagerSessionKey == null) { 511 try { 512 final byte[] keyBytes = new byte[14]; 513 System.arraycopy(getLMHash(), 0, keyBytes, 0, 8); 514 Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd); 515 final Key lowKey = createDESKey(keyBytes, 0); 516 final Key highKey = createDESKey(keyBytes, 7); 517 final byte[] truncatedResponse = new byte[8]; 518 System.arraycopy(getLMResponse(), 0, truncatedResponse, 0, truncatedResponse.length); 519 Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); 520 des.init(Cipher.ENCRYPT_MODE, lowKey); 521 final byte[] lowPart = des.doFinal(truncatedResponse); 522 des = Cipher.getInstance("DES/ECB/NoPadding"); 523 des.init(Cipher.ENCRYPT_MODE, highKey); 524 final byte[] highPart = des.doFinal(truncatedResponse); 525 lanManagerSessionKey = new byte[16]; 526 System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length); 527 System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length); 528 } catch (final Exception e) { 529 throw new NTLMEngineException(e.getMessage(), e); 530 } 531 } 532 return lanManagerSessionKey; 533 } 534 } 535 536 /** Calculates HMAC-MD5 */ hmacMD5(final byte[] value, final byte[] key)537 static byte[] hmacMD5(final byte[] value, final byte[] key) 538 throws NTLMEngineException { 539 final HMACMD5 hmacMD5 = new HMACMD5(key); 540 hmacMD5.update(value); 541 return hmacMD5.getOutput(); 542 } 543 544 /** Calculates RC4 */ RC4(final byte[] value, final byte[] key)545 static byte[] RC4(final byte[] value, final byte[] key) 546 throws NTLMEngineException { 547 try { 548 final Cipher rc4 = Cipher.getInstance("RC4"); 549 rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4")); 550 return rc4.doFinal(value); 551 } catch (final Exception e) { 552 throw new NTLMEngineException(e.getMessage(), e); 553 } 554 } 555 556 /** 557 * Calculates the NTLM2 Session Response for the given challenge, using the 558 * specified password and client challenge. 559 * 560 * @return The NTLM2 Session Response. This is placed in the NTLM response 561 * field of the Type 3 message; the LM response field contains the 562 * client challenge, null-padded to 24 bytes. 563 */ ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge, final byte[] clientChallenge)564 static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge, 565 final byte[] clientChallenge) throws NTLMEngineException { 566 try { 567 // Look up MD5 algorithm (was necessary on jdk 1.4.2) 568 // This used to be needed, but java 1.5.0_07 includes the MD5 569 // algorithm (finally) 570 // Class x = Class.forName("gnu.crypto.hash.MD5"); 571 // Method updateMethod = x.getMethod("update",new 572 // Class[]{byte[].class}); 573 // Method digestMethod = x.getMethod("digest",new Class[0]); 574 // Object mdInstance = x.newInstance(); 575 // updateMethod.invoke(mdInstance,new Object[]{challenge}); 576 // updateMethod.invoke(mdInstance,new Object[]{clientChallenge}); 577 // byte[] digest = (byte[])digestMethod.invoke(mdInstance,new 578 // Object[0]); 579 580 final MessageDigest md5 = MessageDigest.getInstance("MD5"); 581 md5.update(challenge); 582 md5.update(clientChallenge); 583 final byte[] digest = md5.digest(); 584 585 final byte[] sessionHash = new byte[8]; 586 System.arraycopy(digest, 0, sessionHash, 0, 8); 587 return lmResponse(ntlmHash, sessionHash); 588 } catch (final Exception e) { 589 if (e instanceof NTLMEngineException) { 590 throw (NTLMEngineException) e; 591 } 592 throw new NTLMEngineException(e.getMessage(), e); 593 } 594 } 595 596 /** 597 * Creates the LM Hash of the user's password. 598 * 599 * @param password 600 * The password. 601 * 602 * @return The LM Hash of the given password, used in the calculation of the 603 * LM Response. 604 */ lmHash(final String password)605 private static byte[] lmHash(final String password) throws NTLMEngineException { 606 try { 607 final byte[] oemPassword = password.toUpperCase(Locale.ENGLISH).getBytes("US-ASCII"); 608 final int length = Math.min(oemPassword.length, 14); 609 final byte[] keyBytes = new byte[14]; 610 System.arraycopy(oemPassword, 0, keyBytes, 0, length); 611 final Key lowKey = createDESKey(keyBytes, 0); 612 final Key highKey = createDESKey(keyBytes, 7); 613 final byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII"); 614 final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); 615 des.init(Cipher.ENCRYPT_MODE, lowKey); 616 final byte[] lowHash = des.doFinal(magicConstant); 617 des.init(Cipher.ENCRYPT_MODE, highKey); 618 final byte[] highHash = des.doFinal(magicConstant); 619 final byte[] lmHash = new byte[16]; 620 System.arraycopy(lowHash, 0, lmHash, 0, 8); 621 System.arraycopy(highHash, 0, lmHash, 8, 8); 622 return lmHash; 623 } catch (final Exception e) { 624 throw new NTLMEngineException(e.getMessage(), e); 625 } 626 } 627 628 /** 629 * Creates the NTLM Hash of the user's password. 630 * 631 * @param password 632 * The password. 633 * 634 * @return The NTLM Hash of the given password, used in the calculation of 635 * the NTLM Response and the NTLMv2 and LMv2 Hashes. 636 */ ntlmHash(final String password)637 private static byte[] ntlmHash(final String password) throws NTLMEngineException { 638 try { 639 final byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked"); 640 final MD4 md4 = new MD4(); 641 md4.update(unicodePassword); 642 return md4.getOutput(); 643 } catch (final UnsupportedEncodingException e) { 644 throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); 645 } 646 } 647 648 /** 649 * Creates the LMv2 Hash of the user's password. 650 * 651 * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2 652 * Responses. 653 */ lmv2Hash(final String domain, final String user, final byte[] ntlmHash)654 private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash) 655 throws NTLMEngineException { 656 try { 657 final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); 658 // Upper case username, upper case domain! 659 hmacMD5.update(user.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); 660 if (domain != null) { 661 hmacMD5.update(domain.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); 662 } 663 return hmacMD5.getOutput(); 664 } catch (final UnsupportedEncodingException e) { 665 throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e); 666 } 667 } 668 669 /** 670 * Creates the NTLMv2 Hash of the user's password. 671 * 672 * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2 673 * Responses. 674 */ ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash)675 private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash) 676 throws NTLMEngineException { 677 try { 678 final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); 679 // Upper case username, mixed case target!! 680 hmacMD5.update(user.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); 681 if (domain != null) { 682 hmacMD5.update(domain.getBytes("UnicodeLittleUnmarked")); 683 } 684 return hmacMD5.getOutput(); 685 } catch (final UnsupportedEncodingException e) { 686 throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e); 687 } 688 } 689 690 /** 691 * Creates the LM Response from the given hash and Type 2 challenge. 692 * 693 * @param hash 694 * The LM or NTLM Hash. 695 * @param challenge 696 * The server challenge from the Type 2 message. 697 * 698 * @return The response (either LM or NTLM, depending on the provided hash). 699 */ lmResponse(final byte[] hash, final byte[] challenge)700 private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException { 701 try { 702 final byte[] keyBytes = new byte[21]; 703 System.arraycopy(hash, 0, keyBytes, 0, 16); 704 final Key lowKey = createDESKey(keyBytes, 0); 705 final Key middleKey = createDESKey(keyBytes, 7); 706 final Key highKey = createDESKey(keyBytes, 14); 707 final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); 708 des.init(Cipher.ENCRYPT_MODE, lowKey); 709 final byte[] lowResponse = des.doFinal(challenge); 710 des.init(Cipher.ENCRYPT_MODE, middleKey); 711 final byte[] middleResponse = des.doFinal(challenge); 712 des.init(Cipher.ENCRYPT_MODE, highKey); 713 final byte[] highResponse = des.doFinal(challenge); 714 final byte[] lmResponse = new byte[24]; 715 System.arraycopy(lowResponse, 0, lmResponse, 0, 8); 716 System.arraycopy(middleResponse, 0, lmResponse, 8, 8); 717 System.arraycopy(highResponse, 0, lmResponse, 16, 8); 718 return lmResponse; 719 } catch (final Exception e) { 720 throw new NTLMEngineException(e.getMessage(), e); 721 } 722 } 723 724 /** 725 * Creates the LMv2 Response from the given hash, client data, and Type 2 726 * challenge. 727 * 728 * @param hash 729 * The NTLMv2 Hash. 730 * @param clientData 731 * The client data (blob or client challenge). 732 * @param challenge 733 * The server challenge from the Type 2 message. 734 * 735 * @return The response (either NTLMv2 or LMv2, depending on the client 736 * data). 737 */ lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData)738 private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData) 739 throws NTLMEngineException { 740 final HMACMD5 hmacMD5 = new HMACMD5(hash); 741 hmacMD5.update(challenge); 742 hmacMD5.update(clientData); 743 final byte[] mac = hmacMD5.getOutput(); 744 final byte[] lmv2Response = new byte[mac.length + clientData.length]; 745 System.arraycopy(mac, 0, lmv2Response, 0, mac.length); 746 System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length); 747 return lmv2Response; 748 } 749 750 /** 751 * Creates the NTLMv2 blob from the given target information block and 752 * client challenge. 753 * 754 * @param targetInformation 755 * The target information block from the Type 2 message. 756 * @param clientChallenge 757 * The random 8-byte client challenge. 758 * 759 * @return The blob, used in the calculation of the NTLMv2 Response. 760 */ createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp)761 private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) { 762 final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 }; 763 final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 764 final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 765 final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 766 final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8 767 + unknown1.length + targetInformation.length + unknown2.length]; 768 int offset = 0; 769 System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length); 770 offset += blobSignature.length; 771 System.arraycopy(reserved, 0, blob, offset, reserved.length); 772 offset += reserved.length; 773 System.arraycopy(timestamp, 0, blob, offset, timestamp.length); 774 offset += timestamp.length; 775 System.arraycopy(clientChallenge, 0, blob, offset, 8); 776 offset += 8; 777 System.arraycopy(unknown1, 0, blob, offset, unknown1.length); 778 offset += unknown1.length; 779 System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length); 780 offset += targetInformation.length; 781 System.arraycopy(unknown2, 0, blob, offset, unknown2.length); 782 offset += unknown2.length; 783 return blob; 784 } 785 786 /** 787 * Creates a DES encryption key from the given key material. 788 * 789 * @param bytes 790 * A byte array containing the DES key material. 791 * @param offset 792 * The offset in the given byte array at which the 7-byte key 793 * material starts. 794 * 795 * @return A DES encryption key created from the key material starting at 796 * the specified offset in the given byte array. 797 */ createDESKey(final byte[] bytes, final int offset)798 private static Key createDESKey(final byte[] bytes, final int offset) { 799 final byte[] keyBytes = new byte[7]; 800 System.arraycopy(bytes, offset, keyBytes, 0, 7); 801 final byte[] material = new byte[8]; 802 material[0] = keyBytes[0]; 803 material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1); 804 material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2); 805 material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3); 806 material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4); 807 material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5); 808 material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6); 809 material[7] = (byte) (keyBytes[6] << 1); 810 oddParity(material); 811 return new SecretKeySpec(material, "DES"); 812 } 813 814 /** 815 * Applies odd parity to the given byte array. 816 * 817 * @param bytes 818 * The data whose parity bits are to be adjusted for odd parity. 819 */ oddParity(final byte[] bytes)820 private static void oddParity(final byte[] bytes) { 821 for (int i = 0; i < bytes.length; i++) { 822 final byte b = bytes[i]; 823 final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) 824 ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0; 825 if (needsParity) { 826 bytes[i] |= (byte) 0x01; 827 } else { 828 bytes[i] &= (byte) 0xfe; 829 } 830 } 831 } 832 833 /** NTLM message generation, base class */ 834 static class NTLMMessage { 835 /** The current response */ 836 private byte[] messageContents = null; 837 838 /** The current output position */ 839 private int currentOutputPosition = 0; 840 841 /** Constructor to use when message contents are not yet known */ NTLMMessage()842 NTLMMessage() { 843 } 844 845 /** Constructor to use when message contents are known */ NTLMMessage(final String messageBody, final int expectedType)846 NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException { 847 messageContents = Base64.decodeBase64(EncodingUtils.getBytes(messageBody, 848 DEFAULT_CHARSET)); 849 // Look for NTLM message 850 if (messageContents.length < SIGNATURE.length) { 851 throw new NTLMEngineException("NTLM message decoding error - packet too short"); 852 } 853 int i = 0; 854 while (i < SIGNATURE.length) { 855 if (messageContents[i] != SIGNATURE[i]) { 856 throw new NTLMEngineException( 857 "NTLM message expected - instead got unrecognized bytes"); 858 } 859 i++; 860 } 861 862 // Check to be sure there's a type 2 message indicator next 863 final int type = readULong(SIGNATURE.length); 864 if (type != expectedType) { 865 throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType) 866 + " message expected - instead got type " + Integer.toString(type)); 867 } 868 869 currentOutputPosition = messageContents.length; 870 } 871 872 /** 873 * Get the length of the signature and flags, so calculations can adjust 874 * offsets accordingly. 875 */ getPreambleLength()876 protected int getPreambleLength() { 877 return SIGNATURE.length + 4; 878 } 879 880 /** Get the message length */ getMessageLength()881 protected int getMessageLength() { 882 return currentOutputPosition; 883 } 884 885 /** Read a byte from a position within the message buffer */ readByte(final int position)886 protected byte readByte(final int position) throws NTLMEngineException { 887 if (messageContents.length < position + 1) { 888 throw new NTLMEngineException("NTLM: Message too short"); 889 } 890 return messageContents[position]; 891 } 892 893 /** Read a bunch of bytes from a position in the message buffer */ readBytes(final byte[] buffer, final int position)894 protected void readBytes(final byte[] buffer, final int position) throws NTLMEngineException { 895 if (messageContents.length < position + buffer.length) { 896 throw new NTLMEngineException("NTLM: Message too short"); 897 } 898 System.arraycopy(messageContents, position, buffer, 0, buffer.length); 899 } 900 901 /** Read a ushort from a position within the message buffer */ readUShort(final int position)902 protected int readUShort(final int position) throws NTLMEngineException { 903 return NTLMEngineImpl.readUShort(messageContents, position); 904 } 905 906 /** Read a ulong from a position within the message buffer */ readULong(final int position)907 protected int readULong(final int position) throws NTLMEngineException { 908 return NTLMEngineImpl.readULong(messageContents, position); 909 } 910 911 /** Read a security buffer from a position within the message buffer */ readSecurityBuffer(final int position)912 protected byte[] readSecurityBuffer(final int position) throws NTLMEngineException { 913 return NTLMEngineImpl.readSecurityBuffer(messageContents, position); 914 } 915 916 /** 917 * Prepares the object to create a response of the given length. 918 * 919 * @param maxlength 920 * the maximum length of the response to prepare, not 921 * including the type and the signature (which this method 922 * adds). 923 */ prepareResponse(final int maxlength, final int messageType)924 protected void prepareResponse(final int maxlength, final int messageType) { 925 messageContents = new byte[maxlength]; 926 currentOutputPosition = 0; 927 addBytes(SIGNATURE); 928 addULong(messageType); 929 } 930 931 /** 932 * Adds the given byte to the response. 933 * 934 * @param b 935 * the byte to add. 936 */ addByte(final byte b)937 protected void addByte(final byte b) { 938 messageContents[currentOutputPosition] = b; 939 currentOutputPosition++; 940 } 941 942 /** 943 * Adds the given bytes to the response. 944 * 945 * @param bytes 946 * the bytes to add. 947 */ addBytes(final byte[] bytes)948 protected void addBytes(final byte[] bytes) { 949 if (bytes == null) { 950 return; 951 } 952 for (final byte b : bytes) { 953 messageContents[currentOutputPosition] = b; 954 currentOutputPosition++; 955 } 956 } 957 958 /** Adds a USHORT to the response */ addUShort(final int value)959 protected void addUShort(final int value) { 960 addByte((byte) (value & 0xff)); 961 addByte((byte) (value >> 8 & 0xff)); 962 } 963 964 /** Adds a ULong to the response */ addULong(final int value)965 protected void addULong(final int value) { 966 addByte((byte) (value & 0xff)); 967 addByte((byte) (value >> 8 & 0xff)); 968 addByte((byte) (value >> 16 & 0xff)); 969 addByte((byte) (value >> 24 & 0xff)); 970 } 971 972 /** 973 * Returns the response that has been generated after shrinking the 974 * array if required and base64 encodes the response. 975 * 976 * @return The response as above. 977 */ getResponse()978 String getResponse() { 979 final byte[] resp; 980 if (messageContents.length > currentOutputPosition) { 981 final byte[] tmp = new byte[currentOutputPosition]; 982 System.arraycopy(messageContents, 0, tmp, 0, currentOutputPosition); 983 resp = tmp; 984 } else { 985 resp = messageContents; 986 } 987 return EncodingUtils.getAsciiString(Base64.encodeBase64(resp)); 988 } 989 990 } 991 992 /** Type 1 message assembly class */ 993 static class Type1Message extends NTLMMessage { 994 protected byte[] hostBytes; 995 protected byte[] domainBytes; 996 997 /** Constructor. Include the arguments the message will need */ Type1Message(final String domain, final String host)998 Type1Message(final String domain, final String host) throws NTLMEngineException { 999 super(); 1000 try { 1001 // Strip off domain name from the host! 1002 final String unqualifiedHost = convertHost(host); 1003 // Use only the base domain name! 1004 final String unqualifiedDomain = convertDomain(domain); 1005 1006 hostBytes = unqualifiedHost != null? unqualifiedHost.getBytes("ASCII") : null; 1007 domainBytes = unqualifiedDomain != null ? unqualifiedDomain 1008 .toUpperCase(Locale.ENGLISH).getBytes("ASCII") : null; 1009 } catch (final UnsupportedEncodingException e) { 1010 throw new NTLMEngineException("Unicode unsupported: " + e.getMessage(), e); 1011 } 1012 } 1013 1014 /** 1015 * Getting the response involves building the message before returning 1016 * it 1017 */ 1018 @Override getResponse()1019 String getResponse() { 1020 // Now, build the message. Calculate its length first, including 1021 // signature or type. 1022 final int finalLength = 32 + 8 /*+ hostBytes.length + domainBytes.length */; 1023 1024 // Set up the response. This will initialize the signature, message 1025 // type, and flags. 1026 prepareResponse(finalLength, 1); 1027 1028 // Flags. These are the complete set of flags we support. 1029 addULong( 1030 //FLAG_WORKSTATION_PRESENT | 1031 //FLAG_DOMAIN_PRESENT | 1032 1033 // Required flags 1034 //FLAG_REQUEST_LAN_MANAGER_KEY | 1035 FLAG_REQUEST_NTLMv1 | 1036 FLAG_REQUEST_NTLM2_SESSION | 1037 1038 // Protocol version request 1039 FLAG_REQUEST_VERSION | 1040 1041 // Recommended privacy settings 1042 FLAG_REQUEST_ALWAYS_SIGN | 1043 //FLAG_REQUEST_SEAL | 1044 //FLAG_REQUEST_SIGN | 1045 1046 // These must be set according to documentation, based on use of SEAL above 1047 FLAG_REQUEST_128BIT_KEY_EXCH | 1048 FLAG_REQUEST_56BIT_ENCRYPTION | 1049 //FLAG_REQUEST_EXPLICIT_KEY_EXCH | 1050 1051 FLAG_REQUEST_UNICODE_ENCODING); 1052 1053 // Domain length (two times). 1054 addUShort(/*domainBytes.length*/0); 1055 addUShort(/*domainBytes.length*/0); 1056 1057 // Domain offset. 1058 addULong(/*hostBytes.length +*/ 32 + 8); 1059 1060 // Host length (two times). 1061 addUShort(/*hostBytes.length*/0); 1062 addUShort(/*hostBytes.length*/0); 1063 1064 // Host offset (always 32 + 8). 1065 addULong(32 + 8); 1066 1067 // Version 1068 addUShort(0x0105); 1069 // Build 1070 addULong(2600); 1071 // NTLM revision 1072 addUShort(0x0f00); 1073 1074 1075 // Host (workstation) String. 1076 //addBytes(hostBytes); 1077 1078 // Domain String. 1079 //addBytes(domainBytes); 1080 1081 1082 return super.getResponse(); 1083 } 1084 1085 } 1086 1087 /** Type 2 message class */ 1088 static class Type2Message extends NTLMMessage { 1089 protected byte[] challenge; 1090 protected String target; 1091 protected byte[] targetInfo; 1092 protected int flags; 1093 Type2Message(final String message)1094 Type2Message(final String message) throws NTLMEngineException { 1095 super(message, 2); 1096 1097 // Type 2 message is laid out as follows: 1098 // First 8 bytes: NTLMSSP[0] 1099 // Next 4 bytes: Ulong, value 2 1100 // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset) 1101 // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235 1102 // Next 8 bytes, starting at offset 24: Challenge 1103 // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros) 1104 // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset) 1105 // Next 2 bytes, major/minor version number (e.g. 0x05 0x02) 1106 // Next 8 bytes, build number 1107 // Next 2 bytes, protocol version number (e.g. 0x00 0x0f) 1108 // Next, various text fields, and a ushort of value 0 at the end 1109 1110 // Parse out the rest of the info we need from the message 1111 // The nonce is the 8 bytes starting from the byte in position 24. 1112 challenge = new byte[8]; 1113 readBytes(challenge, 24); 1114 1115 flags = readULong(20); 1116 1117 if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) { 1118 throw new NTLMEngineException( 1119 "NTLM type 2 message has flags that make no sense: " 1120 + Integer.toString(flags)); 1121 } 1122 1123 // Do the target! 1124 target = null; 1125 // The TARGET_DESIRED flag is said to not have understood semantics 1126 // in Type2 messages, so use the length of the packet to decide 1127 // how to proceed instead 1128 if (getMessageLength() >= 12 + 8) { 1129 final byte[] bytes = readSecurityBuffer(12); 1130 if (bytes.length != 0) { 1131 try { 1132 target = new String(bytes, "UnicodeLittleUnmarked"); 1133 } catch (final UnsupportedEncodingException e) { 1134 throw new NTLMEngineException(e.getMessage(), e); 1135 } 1136 } 1137 } 1138 1139 // Do the target info! 1140 targetInfo = null; 1141 // TARGET_DESIRED flag cannot be relied on, so use packet length 1142 if (getMessageLength() >= 40 + 8) { 1143 final byte[] bytes = readSecurityBuffer(40); 1144 if (bytes.length != 0) { 1145 targetInfo = bytes; 1146 } 1147 } 1148 } 1149 1150 /** Retrieve the challenge */ getChallenge()1151 byte[] getChallenge() { 1152 return challenge; 1153 } 1154 1155 /** Retrieve the target */ getTarget()1156 String getTarget() { 1157 return target; 1158 } 1159 1160 /** Retrieve the target info */ getTargetInfo()1161 byte[] getTargetInfo() { 1162 return targetInfo; 1163 } 1164 1165 /** Retrieve the response flags */ getFlags()1166 int getFlags() { 1167 return flags; 1168 } 1169 1170 } 1171 1172 /** Type 3 message assembly class */ 1173 static class Type3Message extends NTLMMessage { 1174 // Response flags from the type2 message 1175 protected int type2Flags; 1176 1177 protected byte[] domainBytes; 1178 protected byte[] hostBytes; 1179 protected byte[] userBytes; 1180 1181 protected byte[] lmResp; 1182 protected byte[] ntResp; 1183 protected byte[] sessionKey; 1184 1185 1186 /** Constructor. Pass the arguments we will need */ Type3Message(final String domain, final String host, final String user, final String password, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation)1187 Type3Message(final String domain, final String host, final String user, final String password, final byte[] nonce, 1188 final int type2Flags, final String target, final byte[] targetInformation) 1189 throws NTLMEngineException { 1190 // Save the flags 1191 this.type2Flags = type2Flags; 1192 1193 // Strip off domain name from the host! 1194 final String unqualifiedHost = convertHost(host); 1195 // Use only the base domain name! 1196 final String unqualifiedDomain = convertDomain(domain); 1197 1198 // Create a cipher generator class. Use domain BEFORE it gets modified! 1199 final CipherGen gen = new CipherGen(unqualifiedDomain, user, password, nonce, target, targetInformation); 1200 1201 // Use the new code to calculate the responses, including v2 if that 1202 // seems warranted. 1203 byte[] userSessionKey; 1204 try { 1205 // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet 1206 // been tested 1207 if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) && 1208 targetInformation != null && target != null) { 1209 // NTLMv2 1210 ntResp = gen.getNTLMv2Response(); 1211 lmResp = gen.getLMv2Response(); 1212 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { 1213 userSessionKey = gen.getLanManagerSessionKey(); 1214 } else { 1215 userSessionKey = gen.getNTLMv2UserSessionKey(); 1216 } 1217 } else { 1218 // NTLMv1 1219 if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) { 1220 // NTLM2 session stuff is requested 1221 ntResp = gen.getNTLM2SessionResponse(); 1222 lmResp = gen.getLM2SessionResponse(); 1223 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { 1224 userSessionKey = gen.getLanManagerSessionKey(); 1225 } else { 1226 userSessionKey = gen.getNTLM2SessionResponseUserSessionKey(); 1227 } 1228 } else { 1229 ntResp = gen.getNTLMResponse(); 1230 lmResp = gen.getLMResponse(); 1231 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { 1232 userSessionKey = gen.getLanManagerSessionKey(); 1233 } else { 1234 userSessionKey = gen.getNTLMUserSessionKey(); 1235 } 1236 } 1237 } 1238 } catch (final NTLMEngineException e) { 1239 // This likely means we couldn't find the MD4 hash algorithm - 1240 // fail back to just using LM 1241 ntResp = new byte[0]; 1242 lmResp = gen.getLMResponse(); 1243 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { 1244 userSessionKey = gen.getLanManagerSessionKey(); 1245 } else { 1246 userSessionKey = gen.getLMUserSessionKey(); 1247 } 1248 } 1249 1250 if ((type2Flags & FLAG_REQUEST_SIGN) != 0) { 1251 if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) { 1252 sessionKey = RC4(gen.getSecondaryKey(), userSessionKey); 1253 } else { 1254 sessionKey = userSessionKey; 1255 } 1256 } else { 1257 sessionKey = null; 1258 } 1259 1260 try { 1261 hostBytes = unqualifiedHost != null ? unqualifiedHost 1262 .getBytes("UnicodeLittleUnmarked") : null; 1263 domainBytes = unqualifiedDomain != null ? unqualifiedDomain 1264 .toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked") : null; 1265 userBytes = user.getBytes("UnicodeLittleUnmarked"); 1266 } catch (final UnsupportedEncodingException e) { 1267 throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); 1268 } 1269 } 1270 1271 /** Assemble the response */ 1272 @Override getResponse()1273 String getResponse() { 1274 final int ntRespLen = ntResp.length; 1275 final int lmRespLen = lmResp.length; 1276 1277 final int domainLen = domainBytes != null ? domainBytes.length : 0; 1278 final int hostLen = hostBytes != null ? hostBytes.length: 0; 1279 final int userLen = userBytes.length; 1280 final int sessionKeyLen; 1281 if (sessionKey != null) { 1282 sessionKeyLen = sessionKey.length; 1283 } else { 1284 sessionKeyLen = 0; 1285 } 1286 1287 // Calculate the layout within the packet 1288 final int lmRespOffset = 72; // allocate space for the version 1289 final int ntRespOffset = lmRespOffset + lmRespLen; 1290 final int domainOffset = ntRespOffset + ntRespLen; 1291 final int userOffset = domainOffset + domainLen; 1292 final int hostOffset = userOffset + userLen; 1293 final int sessionKeyOffset = hostOffset + hostLen; 1294 final int finalLength = sessionKeyOffset + sessionKeyLen; 1295 1296 // Start the response. Length includes signature and type 1297 prepareResponse(finalLength, 3); 1298 1299 // LM Resp Length (twice) 1300 addUShort(lmRespLen); 1301 addUShort(lmRespLen); 1302 1303 // LM Resp Offset 1304 addULong(lmRespOffset); 1305 1306 // NT Resp Length (twice) 1307 addUShort(ntRespLen); 1308 addUShort(ntRespLen); 1309 1310 // NT Resp Offset 1311 addULong(ntRespOffset); 1312 1313 // Domain length (twice) 1314 addUShort(domainLen); 1315 addUShort(domainLen); 1316 1317 // Domain offset. 1318 addULong(domainOffset); 1319 1320 // User Length (twice) 1321 addUShort(userLen); 1322 addUShort(userLen); 1323 1324 // User offset 1325 addULong(userOffset); 1326 1327 // Host length (twice) 1328 addUShort(hostLen); 1329 addUShort(hostLen); 1330 1331 // Host offset 1332 addULong(hostOffset); 1333 1334 // Session key length (twice) 1335 addUShort(sessionKeyLen); 1336 addUShort(sessionKeyLen); 1337 1338 // Session key offset 1339 addULong(sessionKeyOffset); 1340 1341 // Flags. 1342 addULong( 1343 //FLAG_WORKSTATION_PRESENT | 1344 //FLAG_DOMAIN_PRESENT | 1345 1346 // Required flags 1347 (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) | 1348 (type2Flags & FLAG_REQUEST_NTLMv1) | 1349 (type2Flags & FLAG_REQUEST_NTLM2_SESSION) | 1350 1351 // Protocol version request 1352 FLAG_REQUEST_VERSION | 1353 1354 // Recommended privacy settings 1355 (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) | 1356 (type2Flags & FLAG_REQUEST_SEAL) | 1357 (type2Flags & FLAG_REQUEST_SIGN) | 1358 1359 // These must be set according to documentation, based on use of SEAL above 1360 (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) | 1361 (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) | 1362 (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) | 1363 1364 (type2Flags & FLAG_TARGETINFO_PRESENT) | 1365 (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) | 1366 (type2Flags & FLAG_REQUEST_TARGET) 1367 ); 1368 1369 // Version 1370 addUShort(0x0105); 1371 // Build 1372 addULong(2600); 1373 // NTLM revision 1374 addUShort(0x0f00); 1375 1376 // Add the actual data 1377 addBytes(lmResp); 1378 addBytes(ntResp); 1379 addBytes(domainBytes); 1380 addBytes(userBytes); 1381 addBytes(hostBytes); 1382 if (sessionKey != null) { 1383 addBytes(sessionKey); 1384 } 1385 1386 return super.getResponse(); 1387 } 1388 } 1389 writeULong(final byte[] buffer, final int value, final int offset)1390 static void writeULong(final byte[] buffer, final int value, final int offset) { 1391 buffer[offset] = (byte) (value & 0xff); 1392 buffer[offset + 1] = (byte) (value >> 8 & 0xff); 1393 buffer[offset + 2] = (byte) (value >> 16 & 0xff); 1394 buffer[offset + 3] = (byte) (value >> 24 & 0xff); 1395 } 1396 F(final int x, final int y, final int z)1397 static int F(final int x, final int y, final int z) { 1398 return ((x & y) | (~x & z)); 1399 } 1400 G(final int x, final int y, final int z)1401 static int G(final int x, final int y, final int z) { 1402 return ((x & y) | (x & z) | (y & z)); 1403 } 1404 H(final int x, final int y, final int z)1405 static int H(final int x, final int y, final int z) { 1406 return (x ^ y ^ z); 1407 } 1408 rotintlft(final int val, final int numbits)1409 static int rotintlft(final int val, final int numbits) { 1410 return ((val << numbits) | (val >>> (32 - numbits))); 1411 } 1412 1413 /** 1414 * Cryptography support - MD4. The following class was based loosely on the 1415 * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java. 1416 * Code correctness was verified by looking at MD4.java from the jcifs 1417 * library (http://jcifs.samba.org). It was massaged extensively to the 1418 * final form found here by Karl Wright (kwright@metacarta.com). 1419 */ 1420 static class MD4 { 1421 protected int A = 0x67452301; 1422 protected int B = 0xefcdab89; 1423 protected int C = 0x98badcfe; 1424 protected int D = 0x10325476; 1425 protected long count = 0L; 1426 protected byte[] dataBuffer = new byte[64]; 1427 MD4()1428 MD4() { 1429 } 1430 update(final byte[] input)1431 void update(final byte[] input) { 1432 // We always deal with 512 bits at a time. Correspondingly, there is 1433 // a buffer 64 bytes long that we write data into until it gets 1434 // full. 1435 int curBufferPos = (int) (count & 63L); 1436 int inputIndex = 0; 1437 while (input.length - inputIndex + curBufferPos >= dataBuffer.length) { 1438 // We have enough data to do the next step. Do a partial copy 1439 // and a transform, updating inputIndex and curBufferPos 1440 // accordingly 1441 final int transferAmt = dataBuffer.length - curBufferPos; 1442 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); 1443 count += transferAmt; 1444 curBufferPos = 0; 1445 inputIndex += transferAmt; 1446 processBuffer(); 1447 } 1448 1449 // If there's anything left, copy it into the buffer and leave it. 1450 // We know there's not enough left to process. 1451 if (inputIndex < input.length) { 1452 final int transferAmt = input.length - inputIndex; 1453 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); 1454 count += transferAmt; 1455 curBufferPos += transferAmt; 1456 } 1457 } 1458 getOutput()1459 byte[] getOutput() { 1460 // Feed pad/length data into engine. This must round out the input 1461 // to a multiple of 512 bits. 1462 final int bufferIndex = (int) (count & 63L); 1463 final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex); 1464 final byte[] postBytes = new byte[padLen + 8]; 1465 // Leading 0x80, specified amount of zero padding, then length in 1466 // bits. 1467 postBytes[0] = (byte) 0x80; 1468 // Fill out the last 8 bytes with the length 1469 for (int i = 0; i < 8; i++) { 1470 postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i)); 1471 } 1472 1473 // Update the engine 1474 update(postBytes); 1475 1476 // Calculate final result 1477 final byte[] result = new byte[16]; 1478 writeULong(result, A, 0); 1479 writeULong(result, B, 4); 1480 writeULong(result, C, 8); 1481 writeULong(result, D, 12); 1482 return result; 1483 } 1484 processBuffer()1485 protected void processBuffer() { 1486 // Convert current buffer to 16 ulongs 1487 final int[] d = new int[16]; 1488 1489 for (int i = 0; i < 16; i++) { 1490 d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8) 1491 + ((dataBuffer[i * 4 + 2] & 0xff) << 16) 1492 + ((dataBuffer[i * 4 + 3] & 0xff) << 24); 1493 } 1494 1495 // Do a round of processing 1496 final int AA = A; 1497 final int BB = B; 1498 final int CC = C; 1499 final int DD = D; 1500 round1(d); 1501 round2(d); 1502 round3(d); 1503 A += AA; 1504 B += BB; 1505 C += CC; 1506 D += DD; 1507 1508 } 1509 round1(final int[] d)1510 protected void round1(final int[] d) { 1511 A = rotintlft((A + F(B, C, D) + d[0]), 3); 1512 D = rotintlft((D + F(A, B, C) + d[1]), 7); 1513 C = rotintlft((C + F(D, A, B) + d[2]), 11); 1514 B = rotintlft((B + F(C, D, A) + d[3]), 19); 1515 1516 A = rotintlft((A + F(B, C, D) + d[4]), 3); 1517 D = rotintlft((D + F(A, B, C) + d[5]), 7); 1518 C = rotintlft((C + F(D, A, B) + d[6]), 11); 1519 B = rotintlft((B + F(C, D, A) + d[7]), 19); 1520 1521 A = rotintlft((A + F(B, C, D) + d[8]), 3); 1522 D = rotintlft((D + F(A, B, C) + d[9]), 7); 1523 C = rotintlft((C + F(D, A, B) + d[10]), 11); 1524 B = rotintlft((B + F(C, D, A) + d[11]), 19); 1525 1526 A = rotintlft((A + F(B, C, D) + d[12]), 3); 1527 D = rotintlft((D + F(A, B, C) + d[13]), 7); 1528 C = rotintlft((C + F(D, A, B) + d[14]), 11); 1529 B = rotintlft((B + F(C, D, A) + d[15]), 19); 1530 } 1531 round2(final int[] d)1532 protected void round2(final int[] d) { 1533 A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3); 1534 D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5); 1535 C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9); 1536 B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13); 1537 1538 A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3); 1539 D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5); 1540 C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9); 1541 B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13); 1542 1543 A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3); 1544 D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5); 1545 C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9); 1546 B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13); 1547 1548 A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3); 1549 D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5); 1550 C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9); 1551 B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13); 1552 1553 } 1554 round3(final int[] d)1555 protected void round3(final int[] d) { 1556 A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3); 1557 D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9); 1558 C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11); 1559 B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15); 1560 1561 A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3); 1562 D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9); 1563 C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11); 1564 B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15); 1565 1566 A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3); 1567 D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9); 1568 C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11); 1569 B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15); 1570 1571 A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3); 1572 D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9); 1573 C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11); 1574 B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15); 1575 1576 } 1577 1578 } 1579 1580 /** 1581 * Cryptography support - HMACMD5 - algorithmically based on various web 1582 * resources by Karl Wright 1583 */ 1584 static class HMACMD5 { 1585 protected byte[] ipad; 1586 protected byte[] opad; 1587 protected MessageDigest md5; 1588 HMACMD5(final byte[] input)1589 HMACMD5(final byte[] input) throws NTLMEngineException { 1590 byte[] key = input; 1591 try { 1592 md5 = MessageDigest.getInstance("MD5"); 1593 } catch (final Exception ex) { 1594 // Umm, the algorithm doesn't exist - throw an 1595 // NTLMEngineException! 1596 throw new NTLMEngineException( 1597 "Error getting md5 message digest implementation: " + ex.getMessage(), ex); 1598 } 1599 1600 // Initialize the pad buffers with the key 1601 ipad = new byte[64]; 1602 opad = new byte[64]; 1603 1604 int keyLength = key.length; 1605 if (keyLength > 64) { 1606 // Use MD5 of the key instead, as described in RFC 2104 1607 md5.update(key); 1608 key = md5.digest(); 1609 keyLength = key.length; 1610 } 1611 int i = 0; 1612 while (i < keyLength) { 1613 ipad[i] = (byte) (key[i] ^ (byte) 0x36); 1614 opad[i] = (byte) (key[i] ^ (byte) 0x5c); 1615 i++; 1616 } 1617 while (i < 64) { 1618 ipad[i] = (byte) 0x36; 1619 opad[i] = (byte) 0x5c; 1620 i++; 1621 } 1622 1623 // Very important: update the digest with the ipad buffer 1624 md5.reset(); 1625 md5.update(ipad); 1626 1627 } 1628 1629 /** Grab the current digest. This is the "answer". */ getOutput()1630 byte[] getOutput() { 1631 final byte[] digest = md5.digest(); 1632 md5.update(opad); 1633 return md5.digest(digest); 1634 } 1635 1636 /** Update by adding a complete array */ update(final byte[] input)1637 void update(final byte[] input) { 1638 md5.update(input); 1639 } 1640 1641 /** Update the algorithm */ update(final byte[] input, final int offset, final int length)1642 void update(final byte[] input, final int offset, final int length) { 1643 md5.update(input, offset, length); 1644 } 1645 1646 } 1647 generateType1Msg( final String domain, final String workstation)1648 public String generateType1Msg( 1649 final String domain, 1650 final String workstation) throws NTLMEngineException { 1651 return getType1Message(workstation, domain); 1652 } 1653 generateType3Msg( final String username, final String password, final String domain, final String workstation, final String challenge)1654 public String generateType3Msg( 1655 final String username, 1656 final String password, 1657 final String domain, 1658 final String workstation, 1659 final String challenge) throws NTLMEngineException { 1660 final Type2Message t2m = new Type2Message(challenge); 1661 return getType3Message( 1662 username, 1663 password, 1664 workstation, 1665 domain, 1666 t2m.getChallenge(), 1667 t2m.getFlags(), 1668 t2m.getTarget(), 1669 t2m.getTargetInfo()); 1670 } 1671 1672 } 1673