1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1319 @@ 1.4 +/* 1.5 + * ==================================================================== 1.6 + * 1.7 + * Licensed to the Apache Software Foundation (ASF) under one or more 1.8 + * contributor license agreements. See the NOTICE file distributed with 1.9 + * this work for additional information regarding copyright ownership. 1.10 + * The ASF licenses this file to You under the Apache License, Version 2.0 1.11 + * (the "License"); you may not use this file except in compliance with 1.12 + * the License. You may obtain a copy of the License at 1.13 + * 1.14 + * http://www.apache.org/licenses/LICENSE-2.0 1.15 + * 1.16 + * Unless required by applicable law or agreed to in writing, software 1.17 + * distributed under the License is distributed on an "AS IS" BASIS, 1.18 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.19 + * See the License for the specific language governing permissions and 1.20 + * limitations under the License. 1.21 + * ==================================================================== 1.22 + * 1.23 + * This software consists of voluntary contributions made by many 1.24 + * individuals on behalf of the Apache Software Foundation. For more 1.25 + * information on the Apache Software Foundation, please see 1.26 + * <http://www.apache.org/>. 1.27 + * 1.28 + */ 1.29 + 1.30 +package ch.boye.httpclientandroidlib.impl.auth; 1.31 + 1.32 +import java.security.Key; 1.33 +import java.security.MessageDigest; 1.34 +import java.util.Arrays; 1.35 + 1.36 +import javax.crypto.Cipher; 1.37 +import javax.crypto.spec.SecretKeySpec; 1.38 + 1.39 +import org.mozilla.apache.commons.codec.binary.Base64; 1.40 +import ch.boye.httpclientandroidlib.util.EncodingUtils; 1.41 + 1.42 +/** 1.43 + * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM 1.44 + * authentication protocol. 1.45 + * 1.46 + * @since 4.1 1.47 + */ 1.48 +final class NTLMEngineImpl implements NTLMEngine { 1.49 + 1.50 + // Flags we use 1.51 + protected final static int FLAG_UNICODE_ENCODING = 0x00000001; 1.52 + protected final static int FLAG_TARGET_DESIRED = 0x00000004; 1.53 + protected final static int FLAG_NEGOTIATE_SIGN = 0x00000010; 1.54 + protected final static int FLAG_NEGOTIATE_SEAL = 0x00000020; 1.55 + protected final static int FLAG_NEGOTIATE_NTLM = 0x00000200; 1.56 + protected final static int FLAG_NEGOTIATE_ALWAYS_SIGN = 0x00008000; 1.57 + protected final static int FLAG_NEGOTIATE_NTLM2 = 0x00080000; 1.58 + protected final static int FLAG_NEGOTIATE_128 = 0x20000000; 1.59 + protected final static int FLAG_NEGOTIATE_KEY_EXCH = 0x40000000; 1.60 + 1.61 + /** Secure random generator */ 1.62 + private static final java.security.SecureRandom RND_GEN; 1.63 + static { 1.64 + java.security.SecureRandom rnd = null; 1.65 + try { 1.66 + rnd = java.security.SecureRandom.getInstance("SHA1PRNG"); 1.67 + } catch (Exception e) { 1.68 + } 1.69 + RND_GEN = rnd; 1.70 + } 1.71 + 1.72 + /** Character encoding */ 1.73 + static final String DEFAULT_CHARSET = "ASCII"; 1.74 + 1.75 + /** The character set to use for encoding the credentials */ 1.76 + private String credentialCharset = DEFAULT_CHARSET; 1.77 + 1.78 + /** The signature string as bytes in the default encoding */ 1.79 + private static byte[] SIGNATURE; 1.80 + 1.81 + static { 1.82 + byte[] bytesWithoutNull = EncodingUtils.getBytes("NTLMSSP", "ASCII"); 1.83 + SIGNATURE = new byte[bytesWithoutNull.length + 1]; 1.84 + System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length); 1.85 + SIGNATURE[bytesWithoutNull.length] = (byte) 0x00; 1.86 + } 1.87 + 1.88 + /** 1.89 + * Returns the response for the given message. 1.90 + * 1.91 + * @param message 1.92 + * the message that was received from the server. 1.93 + * @param username 1.94 + * the username to authenticate with. 1.95 + * @param password 1.96 + * the password to authenticate with. 1.97 + * @param host 1.98 + * The host. 1.99 + * @param domain 1.100 + * the NT domain to authenticate in. 1.101 + * @return The response. 1.102 + * @throws HttpException 1.103 + * If the messages cannot be retrieved. 1.104 + */ 1.105 + final String getResponseFor(String message, String username, String password, 1.106 + String host, String domain) throws NTLMEngineException { 1.107 + 1.108 + final String response; 1.109 + if (message == null || message.trim().equals("")) { 1.110 + response = getType1Message(host, domain); 1.111 + } else { 1.112 + Type2Message t2m = new Type2Message(message); 1.113 + response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m 1.114 + .getFlags(), t2m.getTarget(), t2m.getTargetInfo()); 1.115 + } 1.116 + return response; 1.117 + } 1.118 + 1.119 + /** 1.120 + * Creates the first message (type 1 message) in the NTLM authentication 1.121 + * sequence. This message includes the user name, domain and host for the 1.122 + * authentication session. 1.123 + * 1.124 + * @param host 1.125 + * the computer name of the host requesting authentication. 1.126 + * @param domain 1.127 + * The domain to authenticate with. 1.128 + * @return String the message to add to the HTTP request header. 1.129 + */ 1.130 + String getType1Message(String host, String domain) throws NTLMEngineException { 1.131 + return new Type1Message(domain, host).getResponse(); 1.132 + } 1.133 + 1.134 + /** 1.135 + * Creates the type 3 message using the given server nonce. The type 3 1.136 + * message includes all the information for authentication, host, domain, 1.137 + * username and the result of encrypting the nonce sent by the server using 1.138 + * the user's password as the key. 1.139 + * 1.140 + * @param user 1.141 + * The user name. This should not include the domain name. 1.142 + * @param password 1.143 + * The password. 1.144 + * @param host 1.145 + * The host that is originating the authentication request. 1.146 + * @param domain 1.147 + * The domain to authenticate within. 1.148 + * @param nonce 1.149 + * the 8 byte array the server sent. 1.150 + * @return The type 3 message. 1.151 + * @throws NTLMEngineException 1.152 + * If {@encrypt(byte[],byte[])} fails. 1.153 + */ 1.154 + String getType3Message(String user, String password, String host, String domain, 1.155 + byte[] nonce, int type2Flags, String target, byte[] targetInformation) 1.156 + throws NTLMEngineException { 1.157 + return new Type3Message(domain, host, user, password, nonce, type2Flags, target, 1.158 + targetInformation).getResponse(); 1.159 + } 1.160 + 1.161 + /** 1.162 + * @return Returns the credentialCharset. 1.163 + */ 1.164 + String getCredentialCharset() { 1.165 + return credentialCharset; 1.166 + } 1.167 + 1.168 + /** 1.169 + * @param credentialCharset 1.170 + * The credentialCharset to set. 1.171 + */ 1.172 + void setCredentialCharset(String credentialCharset) { 1.173 + this.credentialCharset = credentialCharset; 1.174 + } 1.175 + 1.176 + /** Strip dot suffix from a name */ 1.177 + private static String stripDotSuffix(String value) { 1.178 + int index = value.indexOf("."); 1.179 + if (index != -1) 1.180 + return value.substring(0, index); 1.181 + return value; 1.182 + } 1.183 + 1.184 + /** Convert host to standard form */ 1.185 + private static String convertHost(String host) { 1.186 + return stripDotSuffix(host); 1.187 + } 1.188 + 1.189 + /** Convert domain to standard form */ 1.190 + private static String convertDomain(String domain) { 1.191 + return stripDotSuffix(domain); 1.192 + } 1.193 + 1.194 + private static int readULong(byte[] src, int index) throws NTLMEngineException { 1.195 + if (src.length < index + 4) 1.196 + throw new NTLMEngineException("NTLM authentication - buffer too small for DWORD"); 1.197 + return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8) 1.198 + | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24); 1.199 + } 1.200 + 1.201 + private static int readUShort(byte[] src, int index) throws NTLMEngineException { 1.202 + if (src.length < index + 2) 1.203 + throw new NTLMEngineException("NTLM authentication - buffer too small for WORD"); 1.204 + return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8); 1.205 + } 1.206 + 1.207 + private static byte[] readSecurityBuffer(byte[] src, int index) throws NTLMEngineException { 1.208 + int length = readUShort(src, index); 1.209 + int offset = readULong(src, index + 4); 1.210 + if (src.length < offset + length) 1.211 + throw new NTLMEngineException( 1.212 + "NTLM authentication - buffer too small for data item"); 1.213 + byte[] buffer = new byte[length]; 1.214 + System.arraycopy(src, offset, buffer, 0, length); 1.215 + return buffer; 1.216 + } 1.217 + 1.218 + /** Calculate a challenge block */ 1.219 + private static byte[] makeRandomChallenge() throws NTLMEngineException { 1.220 + if (RND_GEN == null) { 1.221 + throw new NTLMEngineException("Random generator not available"); 1.222 + } 1.223 + byte[] rval = new byte[8]; 1.224 + synchronized (RND_GEN) { 1.225 + RND_GEN.nextBytes(rval); 1.226 + } 1.227 + return rval; 1.228 + } 1.229 + 1.230 + /** Calculate an NTLM2 challenge block */ 1.231 + private static byte[] makeNTLM2RandomChallenge() throws NTLMEngineException { 1.232 + if (RND_GEN == null) { 1.233 + throw new NTLMEngineException("Random generator not available"); 1.234 + } 1.235 + byte[] rval = new byte[24]; 1.236 + synchronized (RND_GEN) { 1.237 + RND_GEN.nextBytes(rval); 1.238 + } 1.239 + // 8-byte challenge, padded with zeros to 24 bytes. 1.240 + Arrays.fill(rval, 8, 24, (byte) 0x00); 1.241 + return rval; 1.242 + } 1.243 + 1.244 + /** 1.245 + * Calculates the LM Response for the given challenge, using the specified 1.246 + * password. 1.247 + * 1.248 + * @param password 1.249 + * The user's password. 1.250 + * @param challenge 1.251 + * The Type 2 challenge from the server. 1.252 + * 1.253 + * @return The LM Response. 1.254 + */ 1.255 + static byte[] getLMResponse(String password, byte[] challenge) 1.256 + throws NTLMEngineException { 1.257 + byte[] lmHash = lmHash(password); 1.258 + return lmResponse(lmHash, challenge); 1.259 + } 1.260 + 1.261 + /** 1.262 + * Calculates the NTLM Response for the given challenge, using the specified 1.263 + * password. 1.264 + * 1.265 + * @param password 1.266 + * The user's password. 1.267 + * @param challenge 1.268 + * The Type 2 challenge from the server. 1.269 + * 1.270 + * @return The NTLM Response. 1.271 + */ 1.272 + static byte[] getNTLMResponse(String password, byte[] challenge) 1.273 + throws NTLMEngineException { 1.274 + byte[] ntlmHash = ntlmHash(password); 1.275 + return lmResponse(ntlmHash, challenge); 1.276 + } 1.277 + 1.278 + /** 1.279 + * Calculates the NTLMv2 Response for the given challenge, using the 1.280 + * specified authentication target, username, password, target information 1.281 + * block, and client challenge. 1.282 + * 1.283 + * @param target 1.284 + * The authentication target (i.e., domain). 1.285 + * @param user 1.286 + * The username. 1.287 + * @param password 1.288 + * The user's password. 1.289 + * @param targetInformation 1.290 + * The target information block from the Type 2 message. 1.291 + * @param challenge 1.292 + * The Type 2 challenge from the server. 1.293 + * @param clientChallenge 1.294 + * The random 8-byte client challenge. 1.295 + * 1.296 + * @return The NTLMv2 Response. 1.297 + */ 1.298 + static byte[] getNTLMv2Response(String target, String user, String password, 1.299 + byte[] challenge, byte[] clientChallenge, byte[] targetInformation) 1.300 + throws NTLMEngineException { 1.301 + byte[] ntlmv2Hash = ntlmv2Hash(target, user, password); 1.302 + byte[] blob = createBlob(clientChallenge, targetInformation); 1.303 + return lmv2Response(ntlmv2Hash, challenge, blob); 1.304 + } 1.305 + 1.306 + /** 1.307 + * Calculates the LMv2 Response for the given challenge, using the specified 1.308 + * authentication target, username, password, and client challenge. 1.309 + * 1.310 + * @param target 1.311 + * The authentication target (i.e., domain). 1.312 + * @param user 1.313 + * The username. 1.314 + * @param password 1.315 + * The user's password. 1.316 + * @param challenge 1.317 + * The Type 2 challenge from the server. 1.318 + * @param clientChallenge 1.319 + * The random 8-byte client challenge. 1.320 + * 1.321 + * @return The LMv2 Response. 1.322 + */ 1.323 + static byte[] getLMv2Response(String target, String user, String password, 1.324 + byte[] challenge, byte[] clientChallenge) throws NTLMEngineException { 1.325 + byte[] ntlmv2Hash = ntlmv2Hash(target, user, password); 1.326 + return lmv2Response(ntlmv2Hash, challenge, clientChallenge); 1.327 + } 1.328 + 1.329 + /** 1.330 + * Calculates the NTLM2 Session Response for the given challenge, using the 1.331 + * specified password and client challenge. 1.332 + * 1.333 + * @param password 1.334 + * The user's password. 1.335 + * @param challenge 1.336 + * The Type 2 challenge from the server. 1.337 + * @param clientChallenge 1.338 + * The random 8-byte client challenge. 1.339 + * 1.340 + * @return The NTLM2 Session Response. This is placed in the NTLM response 1.341 + * field of the Type 3 message; the LM response field contains the 1.342 + * client challenge, null-padded to 24 bytes. 1.343 + */ 1.344 + static byte[] getNTLM2SessionResponse(String password, byte[] challenge, 1.345 + byte[] clientChallenge) throws NTLMEngineException { 1.346 + try { 1.347 + byte[] ntlmHash = ntlmHash(password); 1.348 + 1.349 + // Look up MD5 algorithm (was necessary on jdk 1.4.2) 1.350 + // This used to be needed, but java 1.5.0_07 includes the MD5 1.351 + // algorithm (finally) 1.352 + // Class x = Class.forName("gnu.crypto.hash.MD5"); 1.353 + // Method updateMethod = x.getMethod("update",new 1.354 + // Class[]{byte[].class}); 1.355 + // Method digestMethod = x.getMethod("digest",new Class[0]); 1.356 + // Object mdInstance = x.newInstance(); 1.357 + // updateMethod.invoke(mdInstance,new Object[]{challenge}); 1.358 + // updateMethod.invoke(mdInstance,new Object[]{clientChallenge}); 1.359 + // byte[] digest = (byte[])digestMethod.invoke(mdInstance,new 1.360 + // Object[0]); 1.361 + 1.362 + MessageDigest md5 = MessageDigest.getInstance("MD5"); 1.363 + md5.update(challenge); 1.364 + md5.update(clientChallenge); 1.365 + byte[] digest = md5.digest(); 1.366 + 1.367 + byte[] sessionHash = new byte[8]; 1.368 + System.arraycopy(digest, 0, sessionHash, 0, 8); 1.369 + return lmResponse(ntlmHash, sessionHash); 1.370 + } catch (Exception e) { 1.371 + if (e instanceof NTLMEngineException) 1.372 + throw (NTLMEngineException) e; 1.373 + throw new NTLMEngineException(e.getMessage(), e); 1.374 + } 1.375 + } 1.376 + 1.377 + /** 1.378 + * Creates the LM Hash of the user's password. 1.379 + * 1.380 + * @param password 1.381 + * The password. 1.382 + * 1.383 + * @return The LM Hash of the given password, used in the calculation of the 1.384 + * LM Response. 1.385 + */ 1.386 + private static byte[] lmHash(String password) throws NTLMEngineException { 1.387 + try { 1.388 + byte[] oemPassword = password.toUpperCase().getBytes("US-ASCII"); 1.389 + int length = Math.min(oemPassword.length, 14); 1.390 + byte[] keyBytes = new byte[14]; 1.391 + System.arraycopy(oemPassword, 0, keyBytes, 0, length); 1.392 + Key lowKey = createDESKey(keyBytes, 0); 1.393 + Key highKey = createDESKey(keyBytes, 7); 1.394 + byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII"); 1.395 + Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); 1.396 + des.init(Cipher.ENCRYPT_MODE, lowKey); 1.397 + byte[] lowHash = des.doFinal(magicConstant); 1.398 + des.init(Cipher.ENCRYPT_MODE, highKey); 1.399 + byte[] highHash = des.doFinal(magicConstant); 1.400 + byte[] lmHash = new byte[16]; 1.401 + System.arraycopy(lowHash, 0, lmHash, 0, 8); 1.402 + System.arraycopy(highHash, 0, lmHash, 8, 8); 1.403 + return lmHash; 1.404 + } catch (Exception e) { 1.405 + throw new NTLMEngineException(e.getMessage(), e); 1.406 + } 1.407 + } 1.408 + 1.409 + /** 1.410 + * Creates the NTLM Hash of the user's password. 1.411 + * 1.412 + * @param password 1.413 + * The password. 1.414 + * 1.415 + * @return The NTLM Hash of the given password, used in the calculation of 1.416 + * the NTLM Response and the NTLMv2 and LMv2 Hashes. 1.417 + */ 1.418 + private static byte[] ntlmHash(String password) throws NTLMEngineException { 1.419 + try { 1.420 + byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked"); 1.421 + MD4 md4 = new MD4(); 1.422 + md4.update(unicodePassword); 1.423 + return md4.getOutput(); 1.424 + } catch (java.io.UnsupportedEncodingException e) { 1.425 + throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); 1.426 + } 1.427 + } 1.428 + 1.429 + /** 1.430 + * Creates the NTLMv2 Hash of the user's password. 1.431 + * 1.432 + * @param target 1.433 + * The authentication target (i.e., domain). 1.434 + * @param user 1.435 + * The username. 1.436 + * @param password 1.437 + * The password. 1.438 + * 1.439 + * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2 1.440 + * Responses. 1.441 + */ 1.442 + private static byte[] ntlmv2Hash(String target, String user, String password) 1.443 + throws NTLMEngineException { 1.444 + try { 1.445 + byte[] ntlmHash = ntlmHash(password); 1.446 + HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); 1.447 + // Upper case username, mixed case target!! 1.448 + hmacMD5.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked")); 1.449 + hmacMD5.update(target.getBytes("UnicodeLittleUnmarked")); 1.450 + return hmacMD5.getOutput(); 1.451 + } catch (java.io.UnsupportedEncodingException e) { 1.452 + throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e); 1.453 + } 1.454 + } 1.455 + 1.456 + /** 1.457 + * Creates the LM Response from the given hash and Type 2 challenge. 1.458 + * 1.459 + * @param hash 1.460 + * The LM or NTLM Hash. 1.461 + * @param challenge 1.462 + * The server challenge from the Type 2 message. 1.463 + * 1.464 + * @return The response (either LM or NTLM, depending on the provided hash). 1.465 + */ 1.466 + private static byte[] lmResponse(byte[] hash, byte[] challenge) throws NTLMEngineException { 1.467 + try { 1.468 + byte[] keyBytes = new byte[21]; 1.469 + System.arraycopy(hash, 0, keyBytes, 0, 16); 1.470 + Key lowKey = createDESKey(keyBytes, 0); 1.471 + Key middleKey = createDESKey(keyBytes, 7); 1.472 + Key highKey = createDESKey(keyBytes, 14); 1.473 + Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); 1.474 + des.init(Cipher.ENCRYPT_MODE, lowKey); 1.475 + byte[] lowResponse = des.doFinal(challenge); 1.476 + des.init(Cipher.ENCRYPT_MODE, middleKey); 1.477 + byte[] middleResponse = des.doFinal(challenge); 1.478 + des.init(Cipher.ENCRYPT_MODE, highKey); 1.479 + byte[] highResponse = des.doFinal(challenge); 1.480 + byte[] lmResponse = new byte[24]; 1.481 + System.arraycopy(lowResponse, 0, lmResponse, 0, 8); 1.482 + System.arraycopy(middleResponse, 0, lmResponse, 8, 8); 1.483 + System.arraycopy(highResponse, 0, lmResponse, 16, 8); 1.484 + return lmResponse; 1.485 + } catch (Exception e) { 1.486 + throw new NTLMEngineException(e.getMessage(), e); 1.487 + } 1.488 + } 1.489 + 1.490 + /** 1.491 + * Creates the LMv2 Response from the given hash, client data, and Type 2 1.492 + * challenge. 1.493 + * 1.494 + * @param hash 1.495 + * The NTLMv2 Hash. 1.496 + * @param clientData 1.497 + * The client data (blob or client challenge). 1.498 + * @param challenge 1.499 + * The server challenge from the Type 2 message. 1.500 + * 1.501 + * @return The response (either NTLMv2 or LMv2, depending on the client 1.502 + * data). 1.503 + */ 1.504 + private static byte[] lmv2Response(byte[] hash, byte[] challenge, byte[] clientData) 1.505 + throws NTLMEngineException { 1.506 + HMACMD5 hmacMD5 = new HMACMD5(hash); 1.507 + hmacMD5.update(challenge); 1.508 + hmacMD5.update(clientData); 1.509 + byte[] mac = hmacMD5.getOutput(); 1.510 + byte[] lmv2Response = new byte[mac.length + clientData.length]; 1.511 + System.arraycopy(mac, 0, lmv2Response, 0, mac.length); 1.512 + System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length); 1.513 + return lmv2Response; 1.514 + } 1.515 + 1.516 + /** 1.517 + * Creates the NTLMv2 blob from the given target information block and 1.518 + * client challenge. 1.519 + * 1.520 + * @param targetInformation 1.521 + * The target information block from the Type 2 message. 1.522 + * @param clientChallenge 1.523 + * The random 8-byte client challenge. 1.524 + * 1.525 + * @return The blob, used in the calculation of the NTLMv2 Response. 1.526 + */ 1.527 + private static byte[] createBlob(byte[] clientChallenge, byte[] targetInformation) { 1.528 + byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 }; 1.529 + byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 1.530 + byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 1.531 + long time = System.currentTimeMillis(); 1.532 + time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch. 1.533 + time *= 10000; // tenths of a microsecond. 1.534 + // convert to little-endian byte array. 1.535 + byte[] timestamp = new byte[8]; 1.536 + for (int i = 0; i < 8; i++) { 1.537 + timestamp[i] = (byte) time; 1.538 + time >>>= 8; 1.539 + } 1.540 + byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8 1.541 + + unknown1.length + targetInformation.length]; 1.542 + int offset = 0; 1.543 + System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length); 1.544 + offset += blobSignature.length; 1.545 + System.arraycopy(reserved, 0, blob, offset, reserved.length); 1.546 + offset += reserved.length; 1.547 + System.arraycopy(timestamp, 0, blob, offset, timestamp.length); 1.548 + offset += timestamp.length; 1.549 + System.arraycopy(clientChallenge, 0, blob, offset, 8); 1.550 + offset += 8; 1.551 + System.arraycopy(unknown1, 0, blob, offset, unknown1.length); 1.552 + offset += unknown1.length; 1.553 + System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length); 1.554 + return blob; 1.555 + } 1.556 + 1.557 + /** 1.558 + * Creates a DES encryption key from the given key material. 1.559 + * 1.560 + * @param bytes 1.561 + * A byte array containing the DES key material. 1.562 + * @param offset 1.563 + * The offset in the given byte array at which the 7-byte key 1.564 + * material starts. 1.565 + * 1.566 + * @return A DES encryption key created from the key material starting at 1.567 + * the specified offset in the given byte array. 1.568 + */ 1.569 + private static Key createDESKey(byte[] bytes, int offset) { 1.570 + byte[] keyBytes = new byte[7]; 1.571 + System.arraycopy(bytes, offset, keyBytes, 0, 7); 1.572 + byte[] material = new byte[8]; 1.573 + material[0] = keyBytes[0]; 1.574 + material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1); 1.575 + material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2); 1.576 + material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3); 1.577 + material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4); 1.578 + material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5); 1.579 + material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6); 1.580 + material[7] = (byte) (keyBytes[6] << 1); 1.581 + oddParity(material); 1.582 + return new SecretKeySpec(material, "DES"); 1.583 + } 1.584 + 1.585 + /** 1.586 + * Applies odd parity to the given byte array. 1.587 + * 1.588 + * @param bytes 1.589 + * The data whose parity bits are to be adjusted for odd parity. 1.590 + */ 1.591 + private static void oddParity(byte[] bytes) { 1.592 + for (int i = 0; i < bytes.length; i++) { 1.593 + byte b = bytes[i]; 1.594 + boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) 1.595 + ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0; 1.596 + if (needsParity) { 1.597 + bytes[i] |= (byte) 0x01; 1.598 + } else { 1.599 + bytes[i] &= (byte) 0xfe; 1.600 + } 1.601 + } 1.602 + } 1.603 + 1.604 + /** NTLM message generation, base class */ 1.605 + static class NTLMMessage { 1.606 + /** The current response */ 1.607 + private byte[] messageContents = null; 1.608 + 1.609 + /** The current output position */ 1.610 + private int currentOutputPosition = 0; 1.611 + 1.612 + /** Constructor to use when message contents are not yet known */ 1.613 + NTLMMessage() { 1.614 + } 1.615 + 1.616 + /** Constructor to use when message contents are known */ 1.617 + NTLMMessage(String messageBody, int expectedType) throws NTLMEngineException { 1.618 + messageContents = Base64.decodeBase64(EncodingUtils.getBytes(messageBody, 1.619 + DEFAULT_CHARSET)); 1.620 + // Look for NTLM message 1.621 + if (messageContents.length < SIGNATURE.length) 1.622 + throw new NTLMEngineException("NTLM message decoding error - packet too short"); 1.623 + int i = 0; 1.624 + while (i < SIGNATURE.length) { 1.625 + if (messageContents[i] != SIGNATURE[i]) 1.626 + throw new NTLMEngineException( 1.627 + "NTLM message expected - instead got unrecognized bytes"); 1.628 + i++; 1.629 + } 1.630 + 1.631 + // Check to be sure there's a type 2 message indicator next 1.632 + int type = readULong(SIGNATURE.length); 1.633 + if (type != expectedType) 1.634 + throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType) 1.635 + + " message expected - instead got type " + Integer.toString(type)); 1.636 + 1.637 + currentOutputPosition = messageContents.length; 1.638 + } 1.639 + 1.640 + /** 1.641 + * Get the length of the signature and flags, so calculations can adjust 1.642 + * offsets accordingly. 1.643 + */ 1.644 + protected int getPreambleLength() { 1.645 + return SIGNATURE.length + 4; 1.646 + } 1.647 + 1.648 + /** Get the message length */ 1.649 + protected int getMessageLength() { 1.650 + return currentOutputPosition; 1.651 + } 1.652 + 1.653 + /** Read a byte from a position within the message buffer */ 1.654 + protected byte readByte(int position) throws NTLMEngineException { 1.655 + if (messageContents.length < position + 1) 1.656 + throw new NTLMEngineException("NTLM: Message too short"); 1.657 + return messageContents[position]; 1.658 + } 1.659 + 1.660 + /** Read a bunch of bytes from a position in the message buffer */ 1.661 + protected void readBytes(byte[] buffer, int position) throws NTLMEngineException { 1.662 + if (messageContents.length < position + buffer.length) 1.663 + throw new NTLMEngineException("NTLM: Message too short"); 1.664 + System.arraycopy(messageContents, position, buffer, 0, buffer.length); 1.665 + } 1.666 + 1.667 + /** Read a ushort from a position within the message buffer */ 1.668 + protected int readUShort(int position) throws NTLMEngineException { 1.669 + return NTLMEngineImpl.readUShort(messageContents, position); 1.670 + } 1.671 + 1.672 + /** Read a ulong from a position within the message buffer */ 1.673 + protected int readULong(int position) throws NTLMEngineException { 1.674 + return NTLMEngineImpl.readULong(messageContents, position); 1.675 + } 1.676 + 1.677 + /** Read a security buffer from a position within the message buffer */ 1.678 + protected byte[] readSecurityBuffer(int position) throws NTLMEngineException { 1.679 + return NTLMEngineImpl.readSecurityBuffer(messageContents, position); 1.680 + } 1.681 + 1.682 + /** 1.683 + * Prepares the object to create a response of the given length. 1.684 + * 1.685 + * @param length 1.686 + * the maximum length of the response to prepare, not 1.687 + * including the type and the signature (which this method 1.688 + * adds). 1.689 + */ 1.690 + protected void prepareResponse(int maxlength, int messageType) { 1.691 + messageContents = new byte[maxlength]; 1.692 + currentOutputPosition = 0; 1.693 + addBytes(SIGNATURE); 1.694 + addULong(messageType); 1.695 + } 1.696 + 1.697 + /** 1.698 + * Adds the given byte to the response. 1.699 + * 1.700 + * @param b 1.701 + * the byte to add. 1.702 + */ 1.703 + protected void addByte(byte b) { 1.704 + messageContents[currentOutputPosition] = b; 1.705 + currentOutputPosition++; 1.706 + } 1.707 + 1.708 + /** 1.709 + * Adds the given bytes to the response. 1.710 + * 1.711 + * @param bytes 1.712 + * the bytes to add. 1.713 + */ 1.714 + protected void addBytes(byte[] bytes) { 1.715 + for (int i = 0; i < bytes.length; i++) { 1.716 + messageContents[currentOutputPosition] = bytes[i]; 1.717 + currentOutputPosition++; 1.718 + } 1.719 + } 1.720 + 1.721 + /** Adds a USHORT to the response */ 1.722 + protected void addUShort(int value) { 1.723 + addByte((byte) (value & 0xff)); 1.724 + addByte((byte) (value >> 8 & 0xff)); 1.725 + } 1.726 + 1.727 + /** Adds a ULong to the response */ 1.728 + protected void addULong(int value) { 1.729 + addByte((byte) (value & 0xff)); 1.730 + addByte((byte) (value >> 8 & 0xff)); 1.731 + addByte((byte) (value >> 16 & 0xff)); 1.732 + addByte((byte) (value >> 24 & 0xff)); 1.733 + } 1.734 + 1.735 + /** 1.736 + * Returns the response that has been generated after shrinking the 1.737 + * array if required and base64 encodes the response. 1.738 + * 1.739 + * @return The response as above. 1.740 + */ 1.741 + String getResponse() { 1.742 + byte[] resp; 1.743 + if (messageContents.length > currentOutputPosition) { 1.744 + byte[] tmp = new byte[currentOutputPosition]; 1.745 + for (int i = 0; i < currentOutputPosition; i++) { 1.746 + tmp[i] = messageContents[i]; 1.747 + } 1.748 + resp = tmp; 1.749 + } else { 1.750 + resp = messageContents; 1.751 + } 1.752 + return EncodingUtils.getAsciiString(Base64.encodeBase64(resp)); 1.753 + } 1.754 + 1.755 + } 1.756 + 1.757 + /** Type 1 message assembly class */ 1.758 + static class Type1Message extends NTLMMessage { 1.759 + protected byte[] hostBytes; 1.760 + protected byte[] domainBytes; 1.761 + 1.762 + /** Constructor. Include the arguments the message will need */ 1.763 + Type1Message(String domain, String host) throws NTLMEngineException { 1.764 + super(); 1.765 + try { 1.766 + // Strip off domain name from the host! 1.767 + host = convertHost(host); 1.768 + // Use only the base domain name! 1.769 + domain = convertDomain(domain); 1.770 + 1.771 + hostBytes = host.getBytes("UnicodeLittleUnmarked"); 1.772 + domainBytes = domain.toUpperCase().getBytes("UnicodeLittleUnmarked"); 1.773 + } catch (java.io.UnsupportedEncodingException e) { 1.774 + throw new NTLMEngineException("Unicode unsupported: " + e.getMessage(), e); 1.775 + } 1.776 + } 1.777 + 1.778 + /** 1.779 + * Getting the response involves building the message before returning 1.780 + * it 1.781 + */ 1.782 + @Override 1.783 + String getResponse() { 1.784 + // Now, build the message. Calculate its length first, including 1.785 + // signature or type. 1.786 + int finalLength = 32 + hostBytes.length + domainBytes.length; 1.787 + 1.788 + // Set up the response. This will initialize the signature, message 1.789 + // type, and flags. 1.790 + prepareResponse(finalLength, 1); 1.791 + 1.792 + // Flags. These are the complete set of flags we support. 1.793 + addULong(FLAG_NEGOTIATE_NTLM | FLAG_NEGOTIATE_NTLM2 | FLAG_NEGOTIATE_SIGN 1.794 + | FLAG_NEGOTIATE_SEAL | 1.795 + /* 1.796 + * FLAG_NEGOTIATE_ALWAYS_SIGN | FLAG_NEGOTIATE_KEY_EXCH | 1.797 + */ 1.798 + FLAG_UNICODE_ENCODING | FLAG_TARGET_DESIRED | FLAG_NEGOTIATE_128); 1.799 + 1.800 + // Domain length (two times). 1.801 + addUShort(domainBytes.length); 1.802 + addUShort(domainBytes.length); 1.803 + 1.804 + // Domain offset. 1.805 + addULong(hostBytes.length + 32); 1.806 + 1.807 + // Host length (two times). 1.808 + addUShort(hostBytes.length); 1.809 + addUShort(hostBytes.length); 1.810 + 1.811 + // Host offset (always 32). 1.812 + addULong(32); 1.813 + 1.814 + // Host String. 1.815 + addBytes(hostBytes); 1.816 + 1.817 + // Domain String. 1.818 + addBytes(domainBytes); 1.819 + 1.820 + return super.getResponse(); 1.821 + } 1.822 + 1.823 + } 1.824 + 1.825 + /** Type 2 message class */ 1.826 + static class Type2Message extends NTLMMessage { 1.827 + protected byte[] challenge; 1.828 + protected String target; 1.829 + protected byte[] targetInfo; 1.830 + protected int flags; 1.831 + 1.832 + Type2Message(String message) throws NTLMEngineException { 1.833 + super(message, 2); 1.834 + 1.835 + // Parse out the rest of the info we need from the message 1.836 + // The nonce is the 8 bytes starting from the byte in position 24. 1.837 + challenge = new byte[8]; 1.838 + readBytes(challenge, 24); 1.839 + 1.840 + flags = readULong(20); 1.841 + if ((flags & FLAG_UNICODE_ENCODING) == 0) 1.842 + throw new NTLMEngineException( 1.843 + "NTLM type 2 message has flags that make no sense: " 1.844 + + Integer.toString(flags)); 1.845 + // Do the target! 1.846 + target = null; 1.847 + // The TARGET_DESIRED flag is said to not have understood semantics 1.848 + // in Type2 messages, so use the length of the packet to decide 1.849 + // how to proceed instead 1.850 + if (getMessageLength() >= 12 + 8) { 1.851 + byte[] bytes = readSecurityBuffer(12); 1.852 + if (bytes.length != 0) { 1.853 + try { 1.854 + target = new String(bytes, "UnicodeLittleUnmarked"); 1.855 + } catch (java.io.UnsupportedEncodingException e) { 1.856 + throw new NTLMEngineException(e.getMessage(), e); 1.857 + } 1.858 + } 1.859 + } 1.860 + 1.861 + // Do the target info! 1.862 + targetInfo = null; 1.863 + // TARGET_DESIRED flag cannot be relied on, so use packet length 1.864 + if (getMessageLength() >= 40 + 8) { 1.865 + byte[] bytes = readSecurityBuffer(40); 1.866 + if (bytes.length != 0) { 1.867 + targetInfo = bytes; 1.868 + } 1.869 + } 1.870 + } 1.871 + 1.872 + /** Retrieve the challenge */ 1.873 + byte[] getChallenge() { 1.874 + return challenge; 1.875 + } 1.876 + 1.877 + /** Retrieve the target */ 1.878 + String getTarget() { 1.879 + return target; 1.880 + } 1.881 + 1.882 + /** Retrieve the target info */ 1.883 + byte[] getTargetInfo() { 1.884 + return targetInfo; 1.885 + } 1.886 + 1.887 + /** Retrieve the response flags */ 1.888 + int getFlags() { 1.889 + return flags; 1.890 + } 1.891 + 1.892 + } 1.893 + 1.894 + /** Type 3 message assembly class */ 1.895 + static class Type3Message extends NTLMMessage { 1.896 + // Response flags from the type2 message 1.897 + protected int type2Flags; 1.898 + 1.899 + protected byte[] domainBytes; 1.900 + protected byte[] hostBytes; 1.901 + protected byte[] userBytes; 1.902 + 1.903 + protected byte[] lmResp; 1.904 + protected byte[] ntResp; 1.905 + 1.906 + /** Constructor. Pass the arguments we will need */ 1.907 + Type3Message(String domain, String host, String user, String password, byte[] nonce, 1.908 + int type2Flags, String target, byte[] targetInformation) 1.909 + throws NTLMEngineException { 1.910 + // Save the flags 1.911 + this.type2Flags = type2Flags; 1.912 + 1.913 + // Strip off domain name from the host! 1.914 + host = convertHost(host); 1.915 + // Use only the base domain name! 1.916 + domain = convertDomain(domain); 1.917 + 1.918 + // Use the new code to calculate the responses, including v2 if that 1.919 + // seems warranted. 1.920 + try { 1.921 + if (targetInformation != null && target != null) { 1.922 + byte[] clientChallenge = makeRandomChallenge(); 1.923 + ntResp = getNTLMv2Response(target, user, password, nonce, clientChallenge, 1.924 + targetInformation); 1.925 + lmResp = getLMv2Response(target, user, password, nonce, clientChallenge); 1.926 + } else { 1.927 + if ((type2Flags & FLAG_NEGOTIATE_NTLM2) != 0) { 1.928 + // NTLM2 session stuff is requested 1.929 + byte[] clientChallenge = makeNTLM2RandomChallenge(); 1.930 + 1.931 + ntResp = getNTLM2SessionResponse(password, nonce, clientChallenge); 1.932 + lmResp = clientChallenge; 1.933 + 1.934 + // All the other flags we send (signing, sealing, key 1.935 + // exchange) are supported, but they don't do anything 1.936 + // at all in an 1.937 + // NTLM2 context! So we're done at this point. 1.938 + } else { 1.939 + ntResp = getNTLMResponse(password, nonce); 1.940 + lmResp = getLMResponse(password, nonce); 1.941 + } 1.942 + } 1.943 + } catch (NTLMEngineException e) { 1.944 + // This likely means we couldn't find the MD4 hash algorithm - 1.945 + // fail back to just using LM 1.946 + ntResp = new byte[0]; 1.947 + lmResp = getLMResponse(password, nonce); 1.948 + } 1.949 + 1.950 + try { 1.951 + domainBytes = domain.toUpperCase().getBytes("UnicodeLittleUnmarked"); 1.952 + hostBytes = host.getBytes("UnicodeLittleUnmarked"); 1.953 + userBytes = user.getBytes("UnicodeLittleUnmarked"); 1.954 + } catch (java.io.UnsupportedEncodingException e) { 1.955 + throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); 1.956 + } 1.957 + } 1.958 + 1.959 + /** Assemble the response */ 1.960 + @Override 1.961 + String getResponse() { 1.962 + int ntRespLen = ntResp.length; 1.963 + int lmRespLen = lmResp.length; 1.964 + 1.965 + int domainLen = domainBytes.length; 1.966 + int hostLen = hostBytes.length; 1.967 + int userLen = userBytes.length; 1.968 + 1.969 + // Calculate the layout within the packet 1.970 + int lmRespOffset = 64; 1.971 + int ntRespOffset = lmRespOffset + lmRespLen; 1.972 + int domainOffset = ntRespOffset + ntRespLen; 1.973 + int userOffset = domainOffset + domainLen; 1.974 + int hostOffset = userOffset + userLen; 1.975 + int sessionKeyOffset = hostOffset + hostLen; 1.976 + int finalLength = sessionKeyOffset + 0; 1.977 + 1.978 + // Start the response. Length includes signature and type 1.979 + prepareResponse(finalLength, 3); 1.980 + 1.981 + // LM Resp Length (twice) 1.982 + addUShort(lmRespLen); 1.983 + addUShort(lmRespLen); 1.984 + 1.985 + // LM Resp Offset 1.986 + addULong(lmRespOffset); 1.987 + 1.988 + // NT Resp Length (twice) 1.989 + addUShort(ntRespLen); 1.990 + addUShort(ntRespLen); 1.991 + 1.992 + // NT Resp Offset 1.993 + addULong(ntRespOffset); 1.994 + 1.995 + // Domain length (twice) 1.996 + addUShort(domainLen); 1.997 + addUShort(domainLen); 1.998 + 1.999 + // Domain offset. 1.1000 + addULong(domainOffset); 1.1001 + 1.1002 + // User Length (twice) 1.1003 + addUShort(userLen); 1.1004 + addUShort(userLen); 1.1005 + 1.1006 + // User offset 1.1007 + addULong(userOffset); 1.1008 + 1.1009 + // Host length (twice) 1.1010 + addUShort(hostLen); 1.1011 + addUShort(hostLen); 1.1012 + 1.1013 + // Host offset 1.1014 + addULong(hostOffset); 1.1015 + 1.1016 + // 4 bytes of zeros - not sure what this is 1.1017 + addULong(0); 1.1018 + 1.1019 + // Message length 1.1020 + addULong(finalLength); 1.1021 + 1.1022 + // Flags. Currently: NEGOTIATE_NTLM + UNICODE_ENCODING + 1.1023 + // TARGET_DESIRED + NEGOTIATE_128 1.1024 + addULong(FLAG_NEGOTIATE_NTLM | FLAG_UNICODE_ENCODING | FLAG_TARGET_DESIRED 1.1025 + | FLAG_NEGOTIATE_128 | (type2Flags & FLAG_NEGOTIATE_NTLM2) 1.1026 + | (type2Flags & FLAG_NEGOTIATE_SIGN) | (type2Flags & FLAG_NEGOTIATE_SEAL) 1.1027 + | (type2Flags & FLAG_NEGOTIATE_KEY_EXCH) 1.1028 + | (type2Flags & FLAG_NEGOTIATE_ALWAYS_SIGN)); 1.1029 + 1.1030 + // Add the actual data 1.1031 + addBytes(lmResp); 1.1032 + addBytes(ntResp); 1.1033 + addBytes(domainBytes); 1.1034 + addBytes(userBytes); 1.1035 + addBytes(hostBytes); 1.1036 + 1.1037 + return super.getResponse(); 1.1038 + } 1.1039 + } 1.1040 + 1.1041 + static void writeULong(byte[] buffer, int value, int offset) { 1.1042 + buffer[offset] = (byte) (value & 0xff); 1.1043 + buffer[offset + 1] = (byte) (value >> 8 & 0xff); 1.1044 + buffer[offset + 2] = (byte) (value >> 16 & 0xff); 1.1045 + buffer[offset + 3] = (byte) (value >> 24 & 0xff); 1.1046 + } 1.1047 + 1.1048 + static int F(int x, int y, int z) { 1.1049 + return ((x & y) | (~x & z)); 1.1050 + } 1.1051 + 1.1052 + static int G(int x, int y, int z) { 1.1053 + return ((x & y) | (x & z) | (y & z)); 1.1054 + } 1.1055 + 1.1056 + static int H(int x, int y, int z) { 1.1057 + return (x ^ y ^ z); 1.1058 + } 1.1059 + 1.1060 + static int rotintlft(int val, int numbits) { 1.1061 + return ((val << numbits) | (val >>> (32 - numbits))); 1.1062 + } 1.1063 + 1.1064 + /** 1.1065 + * Cryptography support - MD4. The following class was based loosely on the 1.1066 + * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java. 1.1067 + * Code correctness was verified by looking at MD4.java from the jcifs 1.1068 + * library (http://jcifs.samba.org). It was massaged extensively to the 1.1069 + * final form found here by Karl Wright (kwright@metacarta.com). 1.1070 + */ 1.1071 + static class MD4 { 1.1072 + protected int A = 0x67452301; 1.1073 + protected int B = 0xefcdab89; 1.1074 + protected int C = 0x98badcfe; 1.1075 + protected int D = 0x10325476; 1.1076 + protected long count = 0L; 1.1077 + protected byte[] dataBuffer = new byte[64]; 1.1078 + 1.1079 + MD4() { 1.1080 + } 1.1081 + 1.1082 + void update(byte[] input) { 1.1083 + // We always deal with 512 bits at a time. Correspondingly, there is 1.1084 + // a buffer 64 bytes long that we write data into until it gets 1.1085 + // full. 1.1086 + int curBufferPos = (int) (count & 63L); 1.1087 + int inputIndex = 0; 1.1088 + while (input.length - inputIndex + curBufferPos >= dataBuffer.length) { 1.1089 + // We have enough data to do the next step. Do a partial copy 1.1090 + // and a transform, updating inputIndex and curBufferPos 1.1091 + // accordingly 1.1092 + int transferAmt = dataBuffer.length - curBufferPos; 1.1093 + System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); 1.1094 + count += transferAmt; 1.1095 + curBufferPos = 0; 1.1096 + inputIndex += transferAmt; 1.1097 + processBuffer(); 1.1098 + } 1.1099 + 1.1100 + // If there's anything left, copy it into the buffer and leave it. 1.1101 + // We know there's not enough left to process. 1.1102 + if (inputIndex < input.length) { 1.1103 + int transferAmt = input.length - inputIndex; 1.1104 + System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); 1.1105 + count += transferAmt; 1.1106 + curBufferPos += transferAmt; 1.1107 + } 1.1108 + } 1.1109 + 1.1110 + byte[] getOutput() { 1.1111 + // Feed pad/length data into engine. This must round out the input 1.1112 + // to a multiple of 512 bits. 1.1113 + int bufferIndex = (int) (count & 63L); 1.1114 + int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex); 1.1115 + byte[] postBytes = new byte[padLen + 8]; 1.1116 + // Leading 0x80, specified amount of zero padding, then length in 1.1117 + // bits. 1.1118 + postBytes[0] = (byte) 0x80; 1.1119 + // Fill out the last 8 bytes with the length 1.1120 + for (int i = 0; i < 8; i++) { 1.1121 + postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i)); 1.1122 + } 1.1123 + 1.1124 + // Update the engine 1.1125 + update(postBytes); 1.1126 + 1.1127 + // Calculate final result 1.1128 + byte[] result = new byte[16]; 1.1129 + writeULong(result, A, 0); 1.1130 + writeULong(result, B, 4); 1.1131 + writeULong(result, C, 8); 1.1132 + writeULong(result, D, 12); 1.1133 + return result; 1.1134 + } 1.1135 + 1.1136 + protected void processBuffer() { 1.1137 + // Convert current buffer to 16 ulongs 1.1138 + int[] d = new int[16]; 1.1139 + 1.1140 + for (int i = 0; i < 16; i++) { 1.1141 + d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8) 1.1142 + + ((dataBuffer[i * 4 + 2] & 0xff) << 16) 1.1143 + + ((dataBuffer[i * 4 + 3] & 0xff) << 24); 1.1144 + } 1.1145 + 1.1146 + // Do a round of processing 1.1147 + int AA = A; 1.1148 + int BB = B; 1.1149 + int CC = C; 1.1150 + int DD = D; 1.1151 + round1(d); 1.1152 + round2(d); 1.1153 + round3(d); 1.1154 + A += AA; 1.1155 + B += BB; 1.1156 + C += CC; 1.1157 + D += DD; 1.1158 + 1.1159 + } 1.1160 + 1.1161 + protected void round1(int[] d) { 1.1162 + A = rotintlft((A + F(B, C, D) + d[0]), 3); 1.1163 + D = rotintlft((D + F(A, B, C) + d[1]), 7); 1.1164 + C = rotintlft((C + F(D, A, B) + d[2]), 11); 1.1165 + B = rotintlft((B + F(C, D, A) + d[3]), 19); 1.1166 + 1.1167 + A = rotintlft((A + F(B, C, D) + d[4]), 3); 1.1168 + D = rotintlft((D + F(A, B, C) + d[5]), 7); 1.1169 + C = rotintlft((C + F(D, A, B) + d[6]), 11); 1.1170 + B = rotintlft((B + F(C, D, A) + d[7]), 19); 1.1171 + 1.1172 + A = rotintlft((A + F(B, C, D) + d[8]), 3); 1.1173 + D = rotintlft((D + F(A, B, C) + d[9]), 7); 1.1174 + C = rotintlft((C + F(D, A, B) + d[10]), 11); 1.1175 + B = rotintlft((B + F(C, D, A) + d[11]), 19); 1.1176 + 1.1177 + A = rotintlft((A + F(B, C, D) + d[12]), 3); 1.1178 + D = rotintlft((D + F(A, B, C) + d[13]), 7); 1.1179 + C = rotintlft((C + F(D, A, B) + d[14]), 11); 1.1180 + B = rotintlft((B + F(C, D, A) + d[15]), 19); 1.1181 + } 1.1182 + 1.1183 + protected void round2(int[] d) { 1.1184 + A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3); 1.1185 + D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5); 1.1186 + C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9); 1.1187 + B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13); 1.1188 + 1.1189 + A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3); 1.1190 + D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5); 1.1191 + C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9); 1.1192 + B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13); 1.1193 + 1.1194 + A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3); 1.1195 + D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5); 1.1196 + C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9); 1.1197 + B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13); 1.1198 + 1.1199 + A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3); 1.1200 + D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5); 1.1201 + C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9); 1.1202 + B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13); 1.1203 + 1.1204 + } 1.1205 + 1.1206 + protected void round3(int[] d) { 1.1207 + A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3); 1.1208 + D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9); 1.1209 + C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11); 1.1210 + B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15); 1.1211 + 1.1212 + A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3); 1.1213 + D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9); 1.1214 + C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11); 1.1215 + B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15); 1.1216 + 1.1217 + A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3); 1.1218 + D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9); 1.1219 + C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11); 1.1220 + B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15); 1.1221 + 1.1222 + A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3); 1.1223 + D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9); 1.1224 + C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11); 1.1225 + B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15); 1.1226 + 1.1227 + } 1.1228 + 1.1229 + } 1.1230 + 1.1231 + /** 1.1232 + * Cryptography support - HMACMD5 - algorithmically based on various web 1.1233 + * resources by Karl Wright 1.1234 + */ 1.1235 + static class HMACMD5 { 1.1236 + protected byte[] ipad; 1.1237 + protected byte[] opad; 1.1238 + protected MessageDigest md5; 1.1239 + 1.1240 + HMACMD5(byte[] key) throws NTLMEngineException { 1.1241 + try { 1.1242 + md5 = MessageDigest.getInstance("MD5"); 1.1243 + } catch (Exception ex) { 1.1244 + // Umm, the algorithm doesn't exist - throw an 1.1245 + // NTLMEngineException! 1.1246 + throw new NTLMEngineException( 1.1247 + "Error getting md5 message digest implementation: " + ex.getMessage(), ex); 1.1248 + } 1.1249 + 1.1250 + // Initialize the pad buffers with the key 1.1251 + ipad = new byte[64]; 1.1252 + opad = new byte[64]; 1.1253 + 1.1254 + int keyLength = key.length; 1.1255 + if (keyLength > 64) { 1.1256 + // Use MD5 of the key instead, as described in RFC 2104 1.1257 + md5.update(key); 1.1258 + key = md5.digest(); 1.1259 + keyLength = key.length; 1.1260 + } 1.1261 + int i = 0; 1.1262 + while (i < keyLength) { 1.1263 + ipad[i] = (byte) (key[i] ^ (byte) 0x36); 1.1264 + opad[i] = (byte) (key[i] ^ (byte) 0x5c); 1.1265 + i++; 1.1266 + } 1.1267 + while (i < 64) { 1.1268 + ipad[i] = (byte) 0x36; 1.1269 + opad[i] = (byte) 0x5c; 1.1270 + i++; 1.1271 + } 1.1272 + 1.1273 + // Very important: update the digest with the ipad buffer 1.1274 + md5.reset(); 1.1275 + md5.update(ipad); 1.1276 + 1.1277 + } 1.1278 + 1.1279 + /** Grab the current digest. This is the "answer". */ 1.1280 + byte[] getOutput() { 1.1281 + byte[] digest = md5.digest(); 1.1282 + md5.update(opad); 1.1283 + return md5.digest(digest); 1.1284 + } 1.1285 + 1.1286 + /** Update by adding a complete array */ 1.1287 + void update(byte[] input) { 1.1288 + md5.update(input); 1.1289 + } 1.1290 + 1.1291 + /** Update the algorithm */ 1.1292 + void update(byte[] input, int offset, int length) { 1.1293 + md5.update(input, offset, length); 1.1294 + } 1.1295 + 1.1296 + } 1.1297 + 1.1298 + public String generateType1Msg( 1.1299 + final String domain, 1.1300 + final String workstation) throws NTLMEngineException { 1.1301 + return getType1Message(workstation, domain); 1.1302 + } 1.1303 + 1.1304 + public String generateType3Msg( 1.1305 + final String username, 1.1306 + final String password, 1.1307 + final String domain, 1.1308 + final String workstation, 1.1309 + final String challenge) throws NTLMEngineException { 1.1310 + Type2Message t2m = new Type2Message(challenge); 1.1311 + return getType3Message( 1.1312 + username, 1.1313 + password, 1.1314 + workstation, 1.1315 + domain, 1.1316 + t2m.getChallenge(), 1.1317 + t2m.getFlags(), 1.1318 + t2m.getTarget(), 1.1319 + t2m.getTargetInfo()); 1.1320 + } 1.1321 + 1.1322 +}