mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java

changeset 0
6474c204b198
     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 +}

mercurial