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