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

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial