hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kwri...@apache.org
Subject svn commit: r1787486 - in /httpcomponents/httpclient/branches/4.5.x: ./ httpclient/ httpclient/src/main/java/org/apache/http/client/config/ httpclient/src/main/java/org/apache/http/impl/auth/ httpclient/src/main/java/org/apache/http/impl/client/ httpcl...
Date Fri, 17 Mar 2017 19:46:34 GMT
Author: kwright
Date: Fri Mar 17 19:46:33 2017
New Revision: 1787486

URL: http://svn.apache.org/viewvc?rev=1787486&view=rev
Log:
Fix for HTTPCLIENT-1834.

Added:
    httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/CredSspScheme.java
      - copied unchanged from r1787485, httpcomponents/httpclient/branches/pull-66/httpclient/src/main/java/org/apache/http/impl/auth/CredSspScheme.java
    httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/CredSspSchemeFactory.java
      - copied unchanged from r1787485, httpcomponents/httpclient/branches/pull-66/httpclient/src/main/java/org/apache/http/impl/auth/CredSspSchemeFactory.java
    httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/DebugUtil.java
      - copied unchanged from r1787485, httpcomponents/httpclient/branches/pull-66/httpclient/src/main/java/org/apache/http/impl/auth/DebugUtil.java
Modified:
    httpcomponents/httpclient/branches/4.5.x/   (props changed)
    httpcomponents/httpclient/branches/4.5.x/RELEASE_NOTES.txt
    httpcomponents/httpclient/branches/4.5.x/httpclient/   (props changed)
    httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java
    httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java
    httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java
    httpcomponents/httpclient/branches/4.5.x/httpclient/src/test/java/org/apache/http/impl/auth/TestNTLMEngineImpl.java

Propchange: httpcomponents/httpclient/branches/4.5.x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Mar 17 19:46:33 2017
@@ -4,3 +4,4 @@
 /httpcomponents/httpclient/branches/branch_4_1:755593-811107
 /httpcomponents/httpclient/branches/conn-mgmt-redesign:1154913-1160573
 /httpcomponents/httpclient/branches/notice-plugin-test:1024348-1031454
+/httpcomponents/httpclient/branches/pull-66:1787157-1787485

Modified: httpcomponents/httpclient/branches/4.5.x/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/branches/4.5.x/RELEASE_NOTES.txt?rev=1787486&r1=1787485&r2=1787486&view=diff
==============================================================================
--- httpcomponents/httpclient/branches/4.5.x/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpclient/branches/4.5.x/RELEASE_NOTES.txt Fri Mar 17 19:46:33 2017
@@ -8,6 +8,9 @@ Please note that as of 4.4 HttpClient re
 Changelog:
 -------------------
 
+* [HTTPCLIENT-1834] Add support for CredSsp.
+  Contributed by Radovan Semancik <radovan.semancik@evolveum.com>
+
 * [HTTPCLIENT-1833] Fix Windows Negotiate-NTLM handling of proxies.
   Contributed by Roman Stoffel <roman.stoffel at gamler.info>
 

Propchange: httpcomponents/httpclient/branches/4.5.x/httpclient/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Mar 17 19:46:33 2017
@@ -4,4 +4,5 @@
 /httpcomponents/httpclient/branches/branch_4_1/httpclient:755593-811107
 /httpcomponents/httpclient/branches/conn-mgmt-redesign/httpclient:1154913-1160573
 /httpcomponents/httpclient/branches/notice-plugin-test/httpclient:1024348-1031454
+/httpcomponents/httpclient/branches/pull-66/httpclient:1787157-1787485
 /httpcomponents/httpclient/trunk/httpclient:1682442

Modified: httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java?rev=1787486&r1=1787485&r2=1787486&view=diff
==============================================================================
--- httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java (original)
+++ httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java Fri Mar 17 19:46:33 2017
@@ -65,6 +65,11 @@ public final class AuthSchemes {
      */
     public static final String KERBEROS = "Kerberos";
 
+    /**
+     * CredSSP authentication scheme defined in [MS-CSSP].
+     */
+    public static final String CREDSSP = "CredSSP";
+
     private AuthSchemes() {
     }
 

Modified: httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java?rev=1787486&r1=1787485&r2=1787486&view=diff
==============================================================================
--- httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java (original)
+++ httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java Fri Mar 17 19:46:33 2017
@@ -26,20 +26,21 @@
  */
 package org.apache.http.impl.auth;
 
-import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
+import org.apache.http.Consts;
 import java.security.Key;
 import java.security.MessageDigest;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.Random;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.SecretKeySpec;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.Certificate;
 
 import org.apache.commons.codec.binary.Base64;
-import org.apache.http.Consts;
-import org.apache.http.util.CharsetUtils;
-import org.apache.http.util.EncodingUtils;
 
 /**
  * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM
@@ -50,7 +51,7 @@ import org.apache.http.util.EncodingUtil
 final class NTLMEngineImpl implements NTLMEngine {
 
     /** Unicode encoding */
-    private static final Charset UNICODE_LITTLE_UNMARKED = CharsetUtils.lookup("UnicodeLittleUnmarked");
+    private static final Charset UNICODE_LITTLE_UNMARKED = Charset.forName("UnicodeLittleUnmarked");
     /** Character encoding */
     private static final Charset DEFAULT_CHARSET = Consts.ASCII;
 
@@ -58,22 +59,41 @@ final class NTLMEngineImpl implements NT
     // http://davenport.sourceforge.net/ntlm.html
     // and
     // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx
-    protected static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001;      // Unicode string encoding requested
-    protected static final int FLAG_REQUEST_TARGET = 0x00000004;                      // Requests target field
-    protected static final int FLAG_REQUEST_SIGN = 0x00000010;  // Requests all messages have a signature attached, in NEGOTIATE message.
-    protected static final int FLAG_REQUEST_SEAL = 0x00000020;  // Request key exchange for message confidentiality in NEGOTIATE message.  MUST be used in conjunction with 56BIT.
-    protected static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080;    // Request Lan Manager key instead of user session key
-    protected static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security.  MUST be set in NEGOTIATE and CHALLENGE both
-    protected static final int FLAG_DOMAIN_PRESENT = 0x00001000;        // Domain is present in message
-    protected static final int FLAG_WORKSTATION_PRESENT = 0x00002000;   // Workstation is present in message
-    protected static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000;   // Requests a signature block on all messages.  Overridden by REQUEST_SIGN and REQUEST_SEAL.
-    protected static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security
-    protected static final int FLAG_REQUEST_VERSION = 0x02000000;       // Request protocol version
-    protected static final int FLAG_TARGETINFO_PRESENT = 0x00800000;    // From server in challenge message, indicating targetinfo is present
-    protected static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange
-    protected static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000;     // Request explicit key exchange
-    protected static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000;      // Must be used in conjunction with SEAL
-
+    // [MS-NLMP] section 2.2.2.5
+    static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001;      // Unicode string encoding requested
+    static final int FLAG_REQUEST_OEM_ENCODING = 0x00000002;      // OEM string encoding requested
+    static final int FLAG_REQUEST_TARGET = 0x00000004;                      // Requests target field
+    static final int FLAG_REQUEST_SIGN = 0x00000010;  // Requests all messages have a signature attached, in NEGOTIATE message.
+    static final int FLAG_REQUEST_SEAL = 0x00000020;  // Request key exchange for message confidentiality in NEGOTIATE message.  MUST be used in conjunction with 56BIT.
+    static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080;    // Request Lan Manager key instead of user session key
+    static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security.  MUST be set in NEGOTIATE and CHALLENGE both
+    static final int FLAG_DOMAIN_PRESENT = 0x00001000;        // Domain is present in message
+    static final int FLAG_WORKSTATION_PRESENT = 0x00002000;   // Workstation is present in message
+    static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000;   // Requests a signature block on all messages.  Overridden by REQUEST_SIGN and REQUEST_SEAL.
+    static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security
+    static final int FLAG_REQUEST_VERSION = 0x02000000;       // Request protocol version
+    static final int FLAG_TARGETINFO_PRESENT = 0x00800000;    // From server in challenge message, indicating targetinfo is present
+    static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange
+    static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000;     // Request explicit key exchange
+    static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000;      // Must be used in conjunction with SEAL
+
+    // Attribute-value identifiers (AvId)
+    // according to [MS-NLMP] section 2.2.2.1
+    static final int MSV_AV_EOL = 0x0000; // Indicates that this is the last AV_PAIR in the list.
+    static final int MSV_AV_NB_COMPUTER_NAME = 0x0001; // The server's NetBIOS computer name.
+    static final int MSV_AV_NB_DOMAIN_NAME = 0x0002; // The server's NetBIOS domain name.
+    static final int MSV_AV_DNS_COMPUTER_NAME = 0x0003; // The fully qualified domain name (FQDN) of the computer.
+    static final int MSV_AV_DNS_DOMAIN_NAME = 0x0004; // The FQDN of the domain.
+    static final int MSV_AV_DNS_TREE_NAME = 0x0005; // The FQDN of the forest.
+    static final int MSV_AV_FLAGS = 0x0006; // A 32-bit value indicating server or client configuration.
+    static final int MSV_AV_TIMESTAMP = 0x0007; // server local time
+    static final int MSV_AV_SINGLE_HOST = 0x0008; // A Single_Host_Data structure.
+    static final int MSV_AV_TARGET_NAME = 0x0009; // The SPN of the target server.
+    static final int MSV_AV_CHANNEL_BINDINGS = 0x000A; // A channel bindings hash.
+
+    static final int MSV_AV_FLAGS_ACCOUNT_AUTH_CONSTAINED = 0x00000001; // Indicates to the client that the account authentication is constrained.
+    static final int MSV_AV_FLAGS_MIC = 0x00000002; // Indicates that the client is providing message integrity in the MIC field in the AUTHENTICATE_MESSAGE.
+    static final int MSV_AV_FLAGS_UNTRUSTED_TARGET_SPN = 0x00000004; // Indicates that the client is providing a target SPN generated from an untrusted source.
 
     /** Secure random generator */
     private static final java.security.SecureRandom RND_GEN;
@@ -87,17 +107,36 @@ final class NTLMEngineImpl implements NT
     }
 
     /** The signature string as bytes in the default encoding */
-    private static final byte[] SIGNATURE;
+    private static final byte[] SIGNATURE = getNullTerminatedAsciiString("NTLMSSP");
 
-    static {
-        final byte[] bytesWithoutNull = "NTLMSSP".getBytes(Consts.ASCII);
-        SIGNATURE = new byte[bytesWithoutNull.length + 1];
-        System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length);
-        SIGNATURE[bytesWithoutNull.length] = (byte) 0x00;
+    // Key derivation magic strings for the SIGNKEY algorithm defined in
+    // [MS-NLMP] section 3.4.5.2
+    private static final byte[] SIGN_MAGIC_SERVER = getNullTerminatedAsciiString(
+        "session key to server-to-client signing key magic constant");
+    private static final byte[] SIGN_MAGIC_CLIENT = getNullTerminatedAsciiString(
+        "session key to client-to-server signing key magic constant");
+    private static final byte[] SEAL_MAGIC_SERVER = getNullTerminatedAsciiString(
+        "session key to server-to-client sealing key magic constant");
+    private static final byte[] SEAL_MAGIC_CLIENT = getNullTerminatedAsciiString(
+        "session key to client-to-server sealing key magic constant");
+
+    // prefix for GSS API channel binding
+    private static final byte[] MAGIC_TLS_SERVER_ENDPOINT = "tls-server-end-point:".getBytes(Consts.ASCII);
+
+    private static byte[] getNullTerminatedAsciiString( final String source )
+    {
+        final byte[] bytesWithoutNull = source.getBytes(Consts.ASCII);
+        final byte[] target = new byte[bytesWithoutNull.length + 1];
+        System.arraycopy(bytesWithoutNull, 0, target, 0, bytesWithoutNull.length);
+        target[bytesWithoutNull.length] = (byte) 0x00;
+        return target;
     }
 
     private static final String TYPE_1_MESSAGE = new Type1Message().getResponse();
 
+    NTLMEngineImpl() {
+    }
+
     /**
      * Returns the response for the given message.
      *
@@ -112,7 +151,7 @@ final class NTLMEngineImpl implements NT
      * @param domain
      *            the NT domain to authenticate in.
      * @return The response.
-     * @throws org.apache.http.HttpException
+     * @throws org.apache.hc.core5.http.HttpException
      *             If the messages cannot be retrieved.
      */
     static String getResponseFor(final String message, final String username, final String password,
@@ -123,8 +162,41 @@ final class NTLMEngineImpl implements NT
             response = getType1Message(host, domain);
         } else {
             final Type2Message t2m = new Type2Message(message);
-            response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m
-                    .getFlags(), t2m.getTarget(), t2m.getTargetInfo());
+            response = getType3Message(username, password, host, domain, t2m.getChallenge(),
+                t2m.getFlags(), t2m.getTarget(), t2m.getTargetInfo());
+        }
+        return response;
+    }
+
+    /**
+     * Returns the response for the given message.
+     *
+     * @param message
+     *            the message that was received from the server.
+     * @param username
+     *            the username to authenticate with.
+     * @param password
+     *            the password to authenticate with.
+     * @param host
+     *            The host.
+     * @param domain
+     *            the NT domain to authenticate in.
+     * @return The response.
+     * @throws org.apache.http.HttpException
+     *             If the messages cannot be retrieved.
+     */
+    static String getResponseFor(final String message, final String username, final String password,
+            final String host, final String domain, final Certificate peerServerCertificate) throws NTLMEngineException {
+
+        final String response;
+        if (message == null || message.trim().equals("")) {
+            response = new Type1Message(host, domain).getResponse();
+        } else {
+            final Type1Message t1m = new Type1Message(host, domain);
+            final Type2Message t2m = new Type2Message(message);
+            response = getType3Message(username, password, host, domain, t2m.getChallenge(),
+                t2m.getFlags(), t2m.getTarget(), t2m.getTargetInfo(),
+                peerServerCertificate, t1m.getBytes(), t2m.getBytes());
         }
         return response;
     }
@@ -140,7 +212,7 @@ final class NTLMEngineImpl implements NT
      *            The domain to authenticate with.
      * @return String the message to add to the HTTP request header.
      */
-    static String getType1Message(final String host, final String domain) throws NTLMEngineException {
+    static String getType1Message(final String host, final String domain) {
         // For compatibility reason do not include domain and host in type 1 message
         //return new Type1Message(domain, host).getResponse();
         return TYPE_1_MESSAGE;
@@ -173,26 +245,32 @@ final class NTLMEngineImpl implements NT
                 targetInformation).getResponse();
     }
 
-    /** Strip dot suffix from a name */
-    private static String stripDotSuffix(final String value) {
-        if (value == null) {
-            return null;
-        }
-        final int index = value.indexOf(".");
-        if (index != -1) {
-            return value.substring(0, index);
-        }
-        return value;
-    }
-
-    /** Convert host to standard form */
-    private static String convertHost(final String host) {
-        return stripDotSuffix(host);
-    }
-
-    /** Convert domain to standard form */
-    private static String convertDomain(final String domain) {
-        return stripDotSuffix(domain);
+    /**
+     * Creates the type 3 message using the given server nonce. The type 3
+     * message includes all the information for authentication, host, domain,
+     * username and the result of encrypting the nonce sent by the server using
+     * the user's password as the key.
+     *
+     * @param user
+     *            The user name. This should not include the domain name.
+     * @param password
+     *            The password.
+     * @param host
+     *            The host that is originating the authentication request.
+     * @param domain
+     *            The domain to authenticate within.
+     * @param nonce
+     *            the 8 byte array the server sent.
+     * @return The type 3 message.
+     * @throws NTLMEngineException
+     *             If {@encrypt(byte[],byte[])} fails.
+     */
+    static String getType3Message(final String user, final String password, final String host, final String domain,
+            final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation,
+            final Certificate peerServerCertificate, final byte[] type1Message, final byte[] type2Message)
+            throws NTLMEngineException {
+        return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
+                targetInformation, peerServerCertificate, type1Message, type2Message).getResponse();
     }
 
     private static int readULong(final byte[] src, final int index) throws NTLMEngineException {
@@ -223,31 +301,28 @@ final class NTLMEngineImpl implements NT
     }
 
     /** Calculate a challenge block */
-    private static byte[] makeRandomChallenge() throws NTLMEngineException {
-        if (RND_GEN == null) {
-            throw new NTLMEngineException("Random generator not available");
-        }
+    private static byte[] makeRandomChallenge(final Random random) throws NTLMEngineException {
         final byte[] rval = new byte[8];
-        synchronized (RND_GEN) {
-            RND_GEN.nextBytes(rval);
+        synchronized (random) {
+            random.nextBytes(rval);
         }
         return rval;
     }
 
     /** Calculate a 16-byte secondary key */
-    private static byte[] makeSecondaryKey() throws NTLMEngineException {
-        if (RND_GEN == null) {
-            throw new NTLMEngineException("Random generator not available");
-        }
+    private static byte[] makeSecondaryKey(final Random random) throws NTLMEngineException {
         final byte[] rval = new byte[16];
-        synchronized (RND_GEN) {
-            RND_GEN.nextBytes(rval);
+        synchronized (random) {
+            random.nextBytes(rval);
         }
         return rval;
     }
 
     protected static class CipherGen {
 
+        protected final Random random;
+        protected final long currentTime;
+
         protected final String domain;
         protected final String user;
         protected final String password;
@@ -279,10 +354,14 @@ final class NTLMEngineImpl implements NT
         protected byte[] ntlm2SessionResponseUserSessionKey = null;
         protected byte[] lanManagerSessionKey = null;
 
-        public CipherGen(final String domain, final String user, final String password,
+        public CipherGen(final Random random, final long currentTime,
+            final String domain, final String user, final String password,
             final byte[] challenge, final String target, final byte[] targetInformation,
             final byte[] clientChallenge, final byte[] clientChallenge2,
             final byte[] secondaryKey, final byte[] timestamp) {
+            this.random = random;
+            this.currentTime = currentTime;
+
             this.domain = domain;
             this.target = target;
             this.user = user;
@@ -295,16 +374,21 @@ final class NTLMEngineImpl implements NT
             this.timestamp = timestamp;
         }
 
-        public CipherGen(final String domain, final String user, final String password,
-            final byte[] challenge, final String target, final byte[] targetInformation) {
-            this(domain, user, password, challenge, target, targetInformation, null, null, null, null);
+        public CipherGen(final Random random, final long currentTime,
+            final String domain,
+            final String user,
+            final String password,
+            final byte[] challenge,
+            final String target,
+            final byte[] targetInformation) {
+            this(random, currentTime, domain, user, password, challenge, target, targetInformation, null, null, null, null);
         }
 
         /** Calculate and return client challenge */
         public byte[] getClientChallenge()
             throws NTLMEngineException {
             if (clientChallenge == null) {
-                clientChallenge = makeRandomChallenge();
+                clientChallenge = makeRandomChallenge(random);
             }
             return clientChallenge;
         }
@@ -313,7 +397,7 @@ final class NTLMEngineImpl implements NT
         public byte[] getClientChallenge2()
             throws NTLMEngineException {
             if (clientChallenge2 == null) {
-                clientChallenge2 = makeRandomChallenge();
+                clientChallenge2 = makeRandomChallenge(random);
             }
             return clientChallenge2;
         }
@@ -322,7 +406,7 @@ final class NTLMEngineImpl implements NT
         public byte[] getSecondaryKey()
             throws NTLMEngineException {
             if (secondaryKey == null) {
-                secondaryKey = makeSecondaryKey();
+                secondaryKey = makeSecondaryKey(random);
             }
             return secondaryKey;
         }
@@ -384,7 +468,7 @@ final class NTLMEngineImpl implements NT
         /** Calculate a timestamp */
         public byte[] getTimestamp() {
             if (timestamp == null) {
-                long time = System.currentTimeMillis();
+                long time = this.currentTime;
                 time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch.
                 time *= 10000; // tenths of a microsecond.
                 // convert to little-endian byte array.
@@ -552,7 +636,7 @@ final class NTLMEngineImpl implements NT
     static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge,
             final byte[] clientChallenge) throws NTLMEngineException {
         try {
-            final MessageDigest md5 = MessageDigest.getInstance("MD5");
+            final MessageDigest md5 = getMD5();
             md5.update(challenge);
             md5.update(clientChallenge);
             final byte[] digest = md5.digest();
@@ -719,6 +803,191 @@ final class NTLMEngineImpl implements NT
         return lmv2Response;
     }
 
+    static enum Mode
+    {
+        CLIENT, SERVER;
+    }
+
+    static class Handle
+    {
+        final private byte[] exportedSessionKey;
+        private byte[] signingKey;
+        private byte[] sealingKey;
+        private Cipher rc4;
+        final Mode mode;
+        final private boolean isConnection;
+        int sequenceNumber = 0;
+
+
+        Handle( final byte[] exportedSessionKey, final Mode mode, final boolean isConnection )
+            throws NTLMEngineException
+        {
+            this.exportedSessionKey = exportedSessionKey;
+            this.isConnection = isConnection;
+            this.mode = mode;
+            try
+            {
+                final MessageDigest signMd5 = getMD5();
+                final MessageDigest sealMd5 = getMD5();
+                signMd5.update( exportedSessionKey );
+                sealMd5.update( exportedSessionKey );
+                if ( mode == Mode.CLIENT )
+                {
+                    signMd5.update( SIGN_MAGIC_CLIENT );
+                    sealMd5.update( SEAL_MAGIC_CLIENT );
+                }
+                else
+                {
+                    signMd5.update( SIGN_MAGIC_SERVER );
+                    sealMd5.update( SEAL_MAGIC_SERVER );
+                }
+                signingKey = signMd5.digest();
+                sealingKey = sealMd5.digest();
+            }
+            catch ( final Exception e )
+            {
+                throw new NTLMEngineException( e.getMessage(), e );
+            }
+            rc4 = initCipher();
+        }
+
+        public byte[] getSigningKey()
+        {
+            return signingKey;
+        }
+
+
+        public byte[] getSealingKey()
+        {
+            return sealingKey;
+        }
+
+        private Cipher initCipher() throws NTLMEngineException
+        {
+            Cipher cipher;
+            try
+            {
+                cipher = Cipher.getInstance( "RC4" );
+                if ( mode == Mode.CLIENT )
+                {
+                    cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
+                }
+                else
+                {
+                    cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
+                }
+            }
+            catch ( Exception e )
+            {
+                throw new NTLMEngineException( e.getMessage(), e );
+            }
+            return cipher;
+        }
+
+
+        private void advanceMessageSequence() throws NTLMEngineException
+        {
+            if ( !isConnection )
+            {
+                final MessageDigest sealMd5 = getMD5();
+                sealMd5.update( sealingKey );
+                final byte[] seqNumBytes = new byte[4];
+                writeULong( seqNumBytes, sequenceNumber, 0 );
+                sealMd5.update( seqNumBytes );
+                sealingKey = sealMd5.digest();
+                initCipher();
+            }
+            sequenceNumber++;
+        }
+
+        private byte[] encrypt( final byte[] data ) throws NTLMEngineException
+        {
+            return rc4.update( data );
+        }
+
+        private byte[] decrypt( final byte[] data ) throws NTLMEngineException
+        {
+            return rc4.update( data );
+        }
+
+        private byte[] computeSignature( final byte[] message ) throws NTLMEngineException
+        {
+            final byte[] sig = new byte[16];
+
+            // version
+            sig[0] = 0x01;
+            sig[1] = 0x00;
+            sig[2] = 0x00;
+            sig[3] = 0x00;
+
+            // HMAC (first 8 bytes)
+            final HMACMD5 hmacMD5 = new HMACMD5( signingKey );
+            hmacMD5.update( encodeLong( sequenceNumber ) );
+            hmacMD5.update( message );
+            final byte[] hmac = hmacMD5.getOutput();
+            final byte[] trimmedHmac = new byte[8];
+            System.arraycopy( hmac, 0, trimmedHmac, 0, 8 );
+            final byte[] encryptedHmac = encrypt( trimmedHmac );
+            System.arraycopy( encryptedHmac, 0, sig, 4, 8 );
+
+            // sequence number
+            encodeLong( sig, 12, sequenceNumber );
+
+            return sig;
+        }
+
+        private boolean validateSignature( final byte[] signature, final byte message[] ) throws NTLMEngineException
+        {
+            final byte[] computedSignature = computeSignature( message );
+            //            log.info( "SSSSS validateSignature("+seqNumber+")\n"
+            //                + "  received: " + DebugUtil.dump( signature ) + "\n"
+            //                + "  computed: " + DebugUtil.dump( computedSignature ) );
+            return Arrays.equals( signature, computedSignature );
+        }
+
+        public byte[] signAndEncryptMessage( final byte[] cleartextMessage ) throws NTLMEngineException
+        {
+            final byte[] encryptedMessage = encrypt( cleartextMessage );
+            final byte[] signature = computeSignature( cleartextMessage );
+            final byte[] outMessage = new byte[signature.length + encryptedMessage.length];
+            System.arraycopy( signature, 0, outMessage, 0, signature.length );
+            System.arraycopy( encryptedMessage, 0, outMessage, signature.length, encryptedMessage.length );
+            advanceMessageSequence();
+            return outMessage;
+        }
+
+        public byte[] decryptAndVerifySignedMessage( final byte[] inMessage ) throws NTLMEngineException
+        {
+            final byte[] signature = new byte[16];
+            System.arraycopy( inMessage, 0, signature, 0, signature.length );
+            final byte[] encryptedMessage = new byte[inMessage.length - 16];
+            System.arraycopy( inMessage, 16, encryptedMessage, 0, encryptedMessage.length );
+            final byte[] cleartextMessage = decrypt( encryptedMessage );
+            if ( !validateSignature( signature, cleartextMessage ) )
+            {
+                throw new NTLMEngineException( "Wrong signature" );
+            }
+            advanceMessageSequence();
+            return cleartextMessage;
+        }
+
+    }
+
+    private static byte[] encodeLong( final int value )
+    {
+        final byte[] enc = new byte[4];
+        encodeLong( enc, 0, value );
+        return enc;
+    }
+
+    private static void encodeLong( final byte[] buf, final int offset, final int value )
+    {
+        buf[offset + 0] = ( byte ) ( value & 0xff );
+        buf[offset + 1] = ( byte ) ( value >> 8 & 0xff );
+        buf[offset + 2] = ( byte ) ( value >> 16 & 0xff );
+        buf[offset + 3] = ( byte ) ( value >> 24 & 0xff );
+    }
+
     /**
      * Creates the NTLMv2 blob from the given target information block and
      * client challenge.
@@ -802,21 +1071,65 @@ final class NTLMEngineImpl implements NT
         }
     }
 
+    /**
+     * Find the character set based on the flags.
+     * @param flags is the flags.
+     * @return the character set.
+     */
+    private static Charset getCharset(final int flags) throws NTLMEngineException
+    {
+        if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) {
+            return DEFAULT_CHARSET;
+        } else {
+            if (UNICODE_LITTLE_UNMARKED == null) {
+                throw new NTLMEngineException( "Unicode not supported" );
+            }
+            return UNICODE_LITTLE_UNMARKED;
+        }
+    }
+
+    /** Strip dot suffix from a name */
+    private static String stripDotSuffix(final String value) {
+        if (value == null) {
+            return null;
+        }
+        final int index = value.indexOf(".");
+        if (index != -1) {
+            return value.substring(0, index);
+        }
+        return value;
+    }
+
+    /** Convert host to standard form */
+    private static String convertHost(final String host) {
+        return stripDotSuffix(host);
+    }
+
+    /** Convert domain to standard form */
+    private static String convertDomain(final String domain) {
+        return stripDotSuffix(domain);
+    }
+
     /** NTLM message generation, base class */
     static class NTLMMessage {
         /** The current response */
-        private byte[] messageContents = null;
+        protected byte[] messageContents = null;
 
         /** The current output position */
-        private int currentOutputPosition = 0;
+        protected int currentOutputPosition = 0;
 
         /** Constructor to use when message contents are not yet known */
         NTLMMessage() {
         }
 
-        /** Constructor to use when message contents are known */
+        /** Constructor taking a string */
         NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException {
-            messageContents = Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET));
+            this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)), expectedType);
+        }
+
+        /** Constructor to use when message bytes are known */
+        NTLMMessage(final byte[] message, final int expectedType) throws NTLMEngineException {
+            messageContents = message;
             // Look for NTLM message
             if (messageContents.length < SIGNATURE.length) {
                 throw new NTLMEngineException("NTLM message decoding error - packet too short");
@@ -888,7 +1201,7 @@ final class NTLMEngineImpl implements NT
          * Prepares the object to create a response of the given length.
          *
          * @param maxlength
-         *            the maximum length of the response to prepare, not
+         *            the maximum length of the response to prepare,
          *            including the type and the signature (which this method
          *            adds).
          */
@@ -946,18 +1259,26 @@ final class NTLMEngineImpl implements NT
          *
          * @return The response as above.
          */
-        String getResponse() {
+        public String getResponse() {
+            return new String(Base64.encodeBase64(getBytes()), Consts.ASCII);
+        }
+
+        public byte[] getBytes() {
+            if (messageContents == null) {
+                buildMessage();
+            }
             final byte[] resp;
-            if (messageContents.length > currentOutputPosition) {
+            if ( messageContents.length > currentOutputPosition ) {
                 final byte[] tmp = new byte[currentOutputPosition];
-                System.arraycopy(messageContents, 0, tmp, 0, currentOutputPosition);
-                resp = tmp;
-            } else {
-                resp = messageContents;
+                System.arraycopy( messageContents, 0, tmp, 0, currentOutputPosition );
+                messageContents = tmp;
             }
-            return EncodingUtils.getAsciiString(Base64.encodeBase64(resp));
+            return messageContents;
         }
 
+        protected void buildMessage() {
+            throw new RuntimeException("Message builder not implemented for "+getClass().getName());
+        }
     }
 
     /** Type 1 message assembly class */
@@ -965,12 +1286,16 @@ final class NTLMEngineImpl implements NT
 
         private final byte[] hostBytes;
         private final byte[] domainBytes;
+        private final int flags;
 
         Type1Message(final String domain, final String host) throws NTLMEngineException {
+            this(domain, host, null);
+        }
+
+        Type1Message(final String domain, final String host, final Integer flags) throws NTLMEngineException {
             super();
-            if (UNICODE_LITTLE_UNMARKED == null) {
-                throw new NTLMEngineException("Unicode not supported");
-            }
+            this.flags = ((flags == null)?getDefaultFlags():flags);
+
             // Strip off domain name from the host!
             final String unqualifiedHost = convertHost(host);
             // Use only the base domain name!
@@ -986,56 +1311,72 @@ final class NTLMEngineImpl implements NT
             super();
             hostBytes = null;
             domainBytes = null;
+            flags = getDefaultFlags();
+        }
+
+        private int getDefaultFlags() {
+            return
+                //FLAG_WORKSTATION_PRESENT |
+                //FLAG_DOMAIN_PRESENT |
+
+                // Required flags
+                //FLAG_REQUEST_LAN_MANAGER_KEY |
+                FLAG_REQUEST_NTLMv1 |
+                FLAG_REQUEST_NTLM2_SESSION |
+
+                // Protocol version request
+                FLAG_REQUEST_VERSION |
+
+                // Recommended privacy settings
+                FLAG_REQUEST_ALWAYS_SIGN |
+                //FLAG_REQUEST_SEAL |
+                //FLAG_REQUEST_SIGN |
+
+                // These must be set according to documentation, based on use of SEAL above
+                FLAG_REQUEST_128BIT_KEY_EXCH |
+                FLAG_REQUEST_56BIT_ENCRYPTION |
+                //FLAG_REQUEST_EXPLICIT_KEY_EXCH |
+
+                FLAG_REQUEST_UNICODE_ENCODING;
+
         }
+
         /**
          * Getting the response involves building the message before returning
          * it
          */
         @Override
-        String getResponse() {
+        protected void buildMessage() {
+            int domainBytesLength = 0;
+            if ( domainBytes != null ) {
+                domainBytesLength = domainBytes.length;
+            }
+            int hostBytesLength = 0;
+            if ( hostBytes != null ) {
+                hostBytesLength = hostBytes.length;
+            }
+
             // Now, build the message. Calculate its length first, including
             // signature or type.
-            final int finalLength = 32 + 8 /*+ hostBytes.length + domainBytes.length */;
+            final int finalLength = 32 + 8 + hostBytesLength + domainBytesLength;
 
             // Set up the response. This will initialize the signature, message
             // type, and flags.
             prepareResponse(finalLength, 1);
 
             // Flags. These are the complete set of flags we support.
-            addULong(
-                    //FLAG_WORKSTATION_PRESENT |
-                    //FLAG_DOMAIN_PRESENT |
-
-                    // Required flags
-                    //FLAG_REQUEST_LAN_MANAGER_KEY |
-                    FLAG_REQUEST_NTLMv1 |
-                    FLAG_REQUEST_NTLM2_SESSION |
-
-                    // Protocol version request
-                    FLAG_REQUEST_VERSION |
-
-                    // Recommended privacy settings
-                    FLAG_REQUEST_ALWAYS_SIGN |
-                    //FLAG_REQUEST_SEAL |
-                    //FLAG_REQUEST_SIGN |
-
-                    // These must be set according to documentation, based on use of SEAL above
-                    FLAG_REQUEST_128BIT_KEY_EXCH |
-                    FLAG_REQUEST_56BIT_ENCRYPTION |
-                    //FLAG_REQUEST_EXPLICIT_KEY_EXCH |
-
-                    FLAG_REQUEST_UNICODE_ENCODING);
+            addULong(flags);
 
             // Domain length (two times).
-            addUShort(/*domainBytes.length*/0);
-            addUShort(/*domainBytes.length*/0);
+            addUShort(domainBytesLength);
+            addUShort(domainBytesLength);
 
             // Domain offset.
-            addULong(/*hostBytes.length +*/ 32 + 8);
+            addULong(hostBytesLength + 32 + 8);
 
             // Host length (two times).
-            addUShort(/*hostBytes.length*/0);
-            addUShort(/*hostBytes.length*/0);
+            addUShort(hostBytesLength);
+            addUShort(hostBytesLength);
 
             // Host offset (always 32 + 8).
             addULong(32 + 8);
@@ -1055,20 +1396,22 @@ final class NTLMEngineImpl implements NT
             if (domainBytes != null) {
                 addBytes(domainBytes);
             }
-
-            return super.getResponse();
         }
 
     }
 
     /** Type 2 message class */
     static class Type2Message extends NTLMMessage {
-        protected byte[] challenge;
+        protected final byte[] challenge;
         protected String target;
         protected byte[] targetInfo;
-        protected int flags;
+        protected final int flags;
+
+        Type2Message(final String messageBody) throws NTLMEngineException {
+            this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)));
+        }
 
-        Type2Message(final String message) throws NTLMEngineException {
+        Type2Message(final byte[] message) throws NTLMEngineException {
             super(message, 2);
 
             // Type 2 message is laid out as follows:
@@ -1091,12 +1434,6 @@ final class NTLMEngineImpl implements NT
 
             flags = readULong(20);
 
-            if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) {
-                throw new NTLMEngineException(
-                        "NTLM type 2 message indicates no support for Unicode. Flags are: "
-                                + Integer.toString(flags));
-            }
-
             // Do the target!
             target = null;
             // The TARGET_DESIRED flag is said to not have understood semantics
@@ -1105,11 +1442,7 @@ final class NTLMEngineImpl implements NT
             if (getMessageLength() >= 12 + 8) {
                 final byte[] bytes = readSecurityBuffer(12);
                 if (bytes.length != 0) {
-                    try {
-                        target = new String(bytes, "UnicodeLittleUnmarked");
-                    } catch (final UnsupportedEncodingException e) {
-                        throw new NTLMEngineException(e.getMessage(), e);
-                    }
+                    target = new String(bytes, getCharset(flags));
                 }
             }
 
@@ -1148,32 +1481,113 @@ final class NTLMEngineImpl implements NT
 
     /** Type 3 message assembly class */
     static class Type3Message extends NTLMMessage {
+        // For mic computation
+        protected final byte[] type1Message;
+        protected final byte[] type2Message;
         // Response flags from the type2 message
-        protected int type2Flags;
+        protected final int type2Flags;
 
-        protected byte[] domainBytes;
-        protected byte[] hostBytes;
-        protected byte[] userBytes;
+        protected final byte[] domainBytes;
+        protected final byte[] hostBytes;
+        protected final byte[] userBytes;
 
         protected byte[] lmResp;
         protected byte[] ntResp;
-        protected byte[] sessionKey;
+        protected final byte[] sessionKey;
+        protected final byte[] exportedSessionKey;
+
+        protected final boolean computeMic;
 
+        /** More primitive constructor: don't include cert or previous messages.
+        */
+        Type3Message(final String domain,
+            final String host,
+            final String user,
+            final String password,
+            final byte[] nonce,
+            final int type2Flags,
+            final String target,
+            final byte[] targetInformation)
+            throws NTLMEngineException {
+            this(domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
+        }
+
+        /** More primitive constructor: don't include cert or previous messages.
+        */
+        Type3Message(final Random random, final long currentTime,
+            final String domain,
+            final String host,
+            final String user,
+            final String password,
+            final byte[] nonce,
+            final int type2Flags,
+            final String target,
+            final byte[] targetInformation)
+            throws NTLMEngineException {
+            this(random, currentTime, domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
+        }
 
         /** Constructor. Pass the arguments we will need */
-        Type3Message(final String domain, final String host, final String user, final String password, final byte[] nonce,
-                final int type2Flags, final String target, final byte[] targetInformation)
-                throws NTLMEngineException {
+        Type3Message(final String domain,
+            final String host,
+            final String user,
+            final String password,
+            final byte[] nonce,
+            final int type2Flags,
+            final String target,
+            final byte[] targetInformation,
+            final Certificate peerServerCertificate,
+            final byte[] type1Message,
+            final byte[] type2Message)
+            throws NTLMEngineException {
+            this(RND_GEN, System.currentTimeMillis(), domain, host, user, password, nonce, type2Flags, target, targetInformation, peerServerCertificate, type1Message, type2Message);
+        }
+
+        /** Constructor. Pass the arguments we will need */
+        Type3Message(final Random random, final long currentTime,
+            final String domain,
+            final String host,
+            final String user,
+            final String password,
+            final byte[] nonce,
+            final int type2Flags,
+            final String target,
+            final byte[] targetInformation,
+            final Certificate peerServerCertificate,
+            final byte[] type1Message,
+            final byte[] type2Message)
+            throws NTLMEngineException {
+
+            if (random == null) {
+                throw new NTLMEngineException("Random generator not available");
+            }
+
             // Save the flags
             this.type2Flags = type2Flags;
+            this.type1Message = type1Message;
+            this.type2Message = type2Message;
 
             // Strip off domain name from the host!
             final String unqualifiedHost = convertHost(host);
             // Use only the base domain name!
             final String unqualifiedDomain = convertDomain(domain);
 
-            // Create a cipher generator class.  Use domain BEFORE it gets modified!
-            final CipherGen gen = new CipherGen(unqualifiedDomain, user, password, nonce, target, targetInformation);
+            byte[] responseTargetInformation = targetInformation;
+            if (peerServerCertificate != null) {
+                responseTargetInformation = addGssMicAvsToTargetInfo(targetInformation, peerServerCertificate);
+                computeMic = true;
+            } else {
+                computeMic = false;
+            }
+
+             // Create a cipher generator class.  Use domain BEFORE it gets modified!
+            final CipherGen gen = new CipherGen(random, currentTime,
+                unqualifiedDomain,
+                user,
+                password,
+                nonce,
+                target,
+                responseTargetInformation);
 
             // Use the new code to calculate the responses, including v2 if that
             // seems warranted.
@@ -1226,25 +1640,37 @@ final class NTLMEngineImpl implements NT
 
             if ((type2Flags & FLAG_REQUEST_SIGN) != 0) {
                 if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) {
-                    sessionKey = RC4(gen.getSecondaryKey(), userSessionKey);
+                    exportedSessionKey = gen.getSecondaryKey();
+                    sessionKey = RC4(exportedSessionKey, userSessionKey);
                 } else {
                     sessionKey = userSessionKey;
+                    exportedSessionKey = sessionKey;
                 }
             } else {
+                if (computeMic) {
+                    throw new NTLMEngineException("Cannot sign/seal: no exported session key");
+                }
                 sessionKey = null;
+                exportedSessionKey = null;
             }
-            if (UNICODE_LITTLE_UNMARKED == null) {
-                throw new NTLMEngineException("Unicode not supported");
-            }
-            hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null;
-            domainBytes = unqualifiedDomain != null ? unqualifiedDomain
-                    .toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null;
-            userBytes = user.getBytes(UNICODE_LITTLE_UNMARKED);
+            final Charset charset = getCharset(type2Flags);
+            hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(charset) : null;
+             domainBytes = unqualifiedDomain != null ? unqualifiedDomain
+                .toUpperCase(Locale.ROOT).getBytes(charset) : null;
+            userBytes = user.getBytes(charset);
+        }
+
+        public byte[] getEncryptedRandomSessionKey() {
+            return sessionKey;
+        }
+
+        public byte[] getExportedSessionKey() {
+            return exportedSessionKey;
         }
 
         /** Assemble the response */
         @Override
-        String getResponse() {
+        protected void buildMessage() {
             final int ntRespLen = ntResp.length;
             final int lmRespLen = lmResp.length;
 
@@ -1259,7 +1685,8 @@ final class NTLMEngineImpl implements NT
             }
 
             // Calculate the layout within the packet
-            final int lmRespOffset = 72;  // allocate space for the version
+            final int lmRespOffset = 72 + // allocate space for the version
+                ( computeMic ? 16 : 0 ); // and MIC
             final int ntRespOffset = lmRespOffset + lmRespLen;
             final int domainOffset = ntRespOffset + ntRespLen;
             final int userOffset = domainOffset + domainLen;
@@ -1314,6 +1741,7 @@ final class NTLMEngineImpl implements NT
 
             // Flags.
             addULong(
+                    /*
                     //FLAG_WORKSTATION_PRESENT |
                     //FLAG_DOMAIN_PRESENT |
 
@@ -1338,6 +1766,8 @@ final class NTLMEngineImpl implements NT
                     (type2Flags & FLAG_TARGETINFO_PRESENT) |
                     (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) |
                     (type2Flags & FLAG_REQUEST_TARGET)
+                        */
+                type2Flags
             );
 
             // Version
@@ -1347,6 +1777,12 @@ final class NTLMEngineImpl implements NT
             // NTLM revision
             addUShort(0x0f00);
 
+            int micPosition = -1;
+            if ( computeMic ) {
+                micPosition = currentOutputPosition;
+                currentOutputPosition += 16;
+            }
+
             // Add the actual data
             addBytes(lmResp);
             addBytes(ntResp);
@@ -1357,8 +1793,69 @@ final class NTLMEngineImpl implements NT
                 addBytes(sessionKey);
             }
 
-            return super.getResponse();
+            // Write the mic back into its slot in the message
+
+            if (computeMic) {
+                // Computation of message integrity code (MIC) as specified in [MS-NLMP] section 3.2.5.1.2.
+                final HMACMD5 hmacMD5 = new HMACMD5( exportedSessionKey );
+                hmacMD5.update( type1Message );
+                hmacMD5.update( type2Message );
+                hmacMD5.update( messageContents );
+                final byte[] mic = hmacMD5.getOutput();
+                System.arraycopy( mic, 0, messageContents, micPosition, mic.length );
+            }
         }
+
+        /**
+         * Add GSS channel binding hash and MIC flag to the targetInfo.
+         * Looks like this is needed if we want to use exported session key for GSS wrapping.
+         */
+        private byte[] addGssMicAvsToTargetInfo( final byte[] originalTargetInfo,
+            final Certificate peerServerCertificate ) throws NTLMEngineException
+        {
+            final byte[] newTargetInfo = new byte[originalTargetInfo.length + 8 + 20];
+            final int appendLength = originalTargetInfo.length - 4; // last tag is MSV_AV_EOL, do not copy that
+            System.arraycopy( originalTargetInfo, 0, newTargetInfo, 0, appendLength );
+            writeUShort( newTargetInfo, MSV_AV_FLAGS, appendLength );
+            writeUShort( newTargetInfo, 4, appendLength + 2 );
+            writeULong( newTargetInfo, MSV_AV_FLAGS_MIC, appendLength + 4 );
+            writeUShort( newTargetInfo, MSV_AV_CHANNEL_BINDINGS, appendLength + 8 );
+            writeUShort( newTargetInfo, 16, appendLength + 10 );
+
+            byte[] channelBindingsHash;
+            try
+            {
+                final byte[] certBytes = peerServerCertificate.getEncoded();
+                final MessageDigest sha256 = MessageDigest.getInstance( "SHA-256" );
+                final byte[] certHashBytes = sha256.digest( certBytes );
+                final byte[] channelBindingStruct = new byte[16 + 4 + MAGIC_TLS_SERVER_ENDPOINT.length
+                    + certHashBytes.length];
+                writeULong( channelBindingStruct, 0x00000035, 16 );
+                System.arraycopy( MAGIC_TLS_SERVER_ENDPOINT, 0, channelBindingStruct, 20,
+                    MAGIC_TLS_SERVER_ENDPOINT.length );
+                System.arraycopy( certHashBytes, 0, channelBindingStruct, 20 + MAGIC_TLS_SERVER_ENDPOINT.length,
+                    certHashBytes.length );
+                final MessageDigest md5 = getMD5();
+                channelBindingsHash = md5.digest( channelBindingStruct );
+            }
+            catch ( CertificateEncodingException e )
+            {
+                throw new NTLMEngineException( e.getMessage(), e );
+            }
+            catch ( NoSuchAlgorithmException e )
+            {
+                throw new NTLMEngineException( e.getMessage(), e );
+            }
+
+            System.arraycopy( channelBindingsHash, 0, newTargetInfo, appendLength + 12, 16 );
+            return newTargetInfo;
+         }
+
+    }
+
+    static void writeUShort(final byte[] buffer, final int value, final int offset) {
+        buffer[offset] = ( byte ) ( value & 0xff );
+        buffer[offset + 1] = ( byte ) ( value >> 8 & 0xff );
     }
 
     static void writeULong(final byte[] buffer, final int value, final int offset) {
@@ -1384,6 +1881,14 @@ final class NTLMEngineImpl implements NT
         return ((val << numbits) | (val >>> (32 - numbits)));
     }
 
+    static MessageDigest getMD5() {
+        try {
+            return MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException ex) {
+            throw new RuntimeException("MD5 message digest doesn't seem to exist - fatal error: "+ex.getMessage(), ex);
+        }
+    }
+
     /**
      * Cryptography support - MD4. The following class was based loosely on the
      * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java.
@@ -1397,7 +1902,7 @@ final class NTLMEngineImpl implements NT
         protected int C = 0x98badcfe;
         protected int D = 0x10325476;
         protected long count = 0L;
-        protected byte[] dataBuffer = new byte[64];
+        protected final byte[] dataBuffer = new byte[64];
 
         MD4() {
         }
@@ -1556,20 +2061,13 @@ final class NTLMEngineImpl implements NT
      * resources by Karl Wright
      */
     static class HMACMD5 {
-        protected byte[] ipad;
-        protected byte[] opad;
-        protected MessageDigest md5;
+        protected final byte[] ipad;
+        protected final byte[] opad;
+        protected final MessageDigest md5;
 
-        HMACMD5(final byte[] input) throws NTLMEngineException {
+        HMACMD5(final byte[] input) {
             byte[] key = input;
-            try {
-                md5 = MessageDigest.getInstance("MD5");
-            } catch (final Exception ex) {
-                // Umm, the algorithm doesn't exist - throw an
-                // NTLMEngineException!
-                throw new NTLMEngineException(
-                        "Error getting md5 message digest implementation: " + ex.getMessage(), ex);
-            }
+            md5 = getMD5();
 
             // Initialize the pad buffers with the key
             ipad = new byte[64];
@@ -1594,7 +2092,7 @@ final class NTLMEngineImpl implements NT
                 i++;
             }
 
-            // Very important: update the digest with the ipad buffer
+            // Very important: processChallenge the digest with the ipad buffer
             md5.reset();
             md5.update(ipad);
 

Modified: httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java?rev=1787486&r1=1787485&r2=1787486&view=diff
==============================================================================
--- httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java (original)
+++ httpcomponents/httpclient/branches/4.5.x/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java Fri Mar 17 19:46:33 2017
@@ -73,9 +73,9 @@ abstract class AuthenticationStrategyImp
                 AuthSchemes.SPNEGO,
                 AuthSchemes.KERBEROS,
                 AuthSchemes.NTLM,
+                AuthSchemes.CREDSSP,
                 AuthSchemes.DIGEST,
                 AuthSchemes.BASIC));
-
     private final int challengeCode;
     private final String headerName;
 

Modified: httpcomponents/httpclient/branches/4.5.x/httpclient/src/test/java/org/apache/http/impl/auth/TestNTLMEngineImpl.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/branches/4.5.x/httpclient/src/test/java/org/apache/http/impl/auth/TestNTLMEngineImpl.java?rev=1787486&r1=1787485&r2=1787486&view=diff
==============================================================================
--- httpcomponents/httpclient/branches/4.5.x/httpclient/src/test/java/org/apache/http/impl/auth/TestNTLMEngineImpl.java (original)
+++ httpcomponents/httpclient/branches/4.5.x/httpclient/src/test/java/org/apache/http/impl/auth/TestNTLMEngineImpl.java Fri Mar 17 19:46:33 2017
@@ -29,6 +29,11 @@ package org.apache.http.impl.auth;
 import org.apache.http.Consts;
 import org.junit.Assert;
 import org.junit.Test;
+import java.util.Random;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
 
 public class TestNTLMEngineImpl {
 
@@ -51,6 +56,9 @@ public class TestNTLMEngineImpl {
         if (c >= 'a' && c <= 'f') {
             return (byte) (c - 'a' + 0x0a);
         }
+        if (c >= 'A' && c <= 'F') {
+            return (byte) (c - 'A' + 0x0a);
+        }
         return (byte) (c - '0');
     }
 
@@ -89,6 +97,8 @@ public class TestNTLMEngineImpl {
     @Test
     public void testLMResponse() throws Exception {
         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
+            new Random(1234),
+            1234L,
             null,
             null,
             "SecREt01",
@@ -107,6 +117,8 @@ public class TestNTLMEngineImpl {
     @Test
     public void testNTLMResponse() throws Exception {
         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
+            new Random(1234),
+            1234L,
             null,
             null,
             "SecREt01",
@@ -125,6 +137,8 @@ public class TestNTLMEngineImpl {
     @Test
     public void testLMv2Response() throws Exception {
         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
+            new Random(1234),
+            1234L,
             "DOMAIN",
             "user",
             "SecREt01",
@@ -143,6 +157,8 @@ public class TestNTLMEngineImpl {
     @Test
     public void testNTLMv2Response() throws Exception {
         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
+            new Random(1234),
+            1234L,
             "DOMAIN",
             "user",
             "SecREt01",
@@ -163,6 +179,8 @@ public class TestNTLMEngineImpl {
     @Test
     public void testLM2SessionResponse() throws Exception {
         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
+            new Random(1234),
+            1234L,
             "DOMAIN",
             "user",
             "SecREt01",
@@ -181,6 +199,8 @@ public class TestNTLMEngineImpl {
     @Test
     public void testNTLM2SessionResponse() throws Exception {
         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
+            new Random(1234),
+            1234L,
             "DOMAIN",
             "user",
             "SecREt01",
@@ -199,6 +219,8 @@ public class TestNTLMEngineImpl {
     @Test
     public void testNTLMUserSessionKey() throws Exception {
         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
+            new Random(1234),
+            1234L,
             "DOMAIN",
             "user",
             "SecREt01",
@@ -216,20 +238,74 @@ public class TestNTLMEngineImpl {
 
     @Test
     public void testType1Message() throws Exception {
-        new NTLMEngineImpl().getType1Message("myhost", "mydomain");
+        final byte[] bytes = new NTLMEngineImpl.Type1Message("myhost", "mydomain").getBytes();
+        final byte[] bytes2 = toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400");
+        checkArraysMatch(bytes2, bytes);
     }
 
     @Test
     public void testType3Message() throws Exception {
-        new NTLMEngineImpl().getType3Message("me", "mypassword", "myhost", "mydomain",
+        final byte[] bytes = new NTLMEngineImpl.Type3Message(
+            new Random(1234),
+            1234L,
+            "me", "mypassword", "myhost", "mydomain",
+            toBytes("0001020304050607"),
+            0xffffffff,
+            null,null).getBytes();
+        checkArraysMatch(toBytes("4E544C4D53535000030000001800180048000000180018006000000004000400780000000C000C007C0000001400140088000000100010009C000000FFFFFFFF0501280A0000000FA86886A5D297814200000000000000000000000000000000EEC7568E00798491244959B9C942F4F367C5CBABEEF546F74D0045006D00790068006F00730074006D007900700061007300730077006F007200640094DDAB1EBB82C9A1AB914CAE6F199644"),
+            bytes);
+        final byte[] bytes2 = new NTLMEngineImpl.Type3Message(
+            new Random(1234),
+            1234L,
+            "me", "mypassword", "myhost", "mydomain",
             toBytes("0001020304050607"),
             0xffffffff,
-            null,null);
-        new NTLMEngineImpl().getType3Message("me", "mypassword", "myhost", "mydomain",
+            "mytarget",
+            toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000")).getBytes();
+        checkArraysMatch(toBytes("4E544C4D53535000030000001800180048000000920092006000000004000400F20000000C000C00F600000014001400020100001000100016010000FFFFFFFF0501280A0000000F3695F1EA7B164788A437892FA7504320DA2D8CF378EBC83CE856A8FB985BF7783545828A91A13AE8010100000000000020CBFAD5DEB19D01A86886A5D29781420000000002000C0044004F004D00410049004E0001000C005300450052005600450052000400140064006F006D00610069006E002E0063006F006D00030022007300650072007600650072002E0064006F006D00610069006E002E0063006F006D0000000000000000004D0045006D00790068006F00730074006D007900700061007300730077006F0072006400BB1AAD36F11631CC7CBC8800CEEE1C99"),
+            bytes2);
+    }
+
+    private static String cannedCert =
+        "-----BEGIN CERTIFICATE-----\n"+
+        "MIIDIDCCAgigAwIBAgIEOqKaWTANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEQMA4GA1UEBxMH\n"+
+        "TXkgQ2l0eTEYMBYGA1UEChMPTXkgT3JnYW5pemF0aW9uMRcwFQYDVQQDEw5NeSBBcHBsaWNhdGlvbjAe\n"+
+        "Fw0xNzAzMTcxNDAyMzRaFw0yNzAzMTUxNDAyMzRaMFIxCzAJBgNVBAYTAlVTMRAwDgYDVQQHEwdNeSBD\n"+
+        "aXR5MRgwFgYDVQQKEw9NeSBPcmdhbml6YXRpb24xFzAVBgNVBAMTDk15IEFwcGxpY2F0aW9uMIIBIjAN\n"+
+        "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArc+mbViBaHeRSt82KrJ5IF+62b/Qob95Lca4DJIislTY\n"+
+        "vLPIo0R1faBV8BkEeUQwo01srkf3RaGLCHNZnFal4KEzbtiUy6W+n08G5E9w9YG+WSwW2dmjvEI7k2a2\n"+
+        "xqlaM4NdMKL4ONPXcxfZsMDqxDgpdkaNPKpZ10NDq6rmBTkQw/OSG0z1KLtwLkF1ZQ/3mXdjVzvP83V2\n"+
+        "g17AqBazb0Z1YHsVKmkGjPqnq3niJH/6Oke4N+5k/1cE5lSJcQNGP0nqeGdJfvqQZ+gk6gH/sOngZL9X\n"+
+        "hPVkpseAwHa+xuPneDSjibLgLmMt3XGDK6jGfjdp5FWqFvAD5E3LHbW9gwIDAQABMA0GCSqGSIb3DQEB\n"+
+        "CwUAA4IBAQCpUXUHhl5LyMSO5Q0OktEc9AaFjZtVfknpPde6Zeh35Pqd2354ErvJSBWgzFAphda0oh2s\n"+
+        "OIAFkM6LJQEnVDTbXDXN+YY8e3gb9ryfh85hkhC0XI9qp17WPSkmw8XgDfvRd6YQgKm1AnLxjOCwG2jg\n"+
+        "i09iZBIWkW3ZeRAMvWPHHjvq44iZB5ZrEl0apgumS6MxpUzKOr5Pcq0jxJDw2UCj5YloFMNl+UINv2vV\n"+
+        "aL/DR6ivc61dOfN1E/VNBGkkCk/AogNyucGiFMCq9hd25Y9EbkBBqObYTH1XMX+ufsJh+6hG7KDQ1e/F\n"+
+        "nRrlhKwM2uRe+aSH0D6/erjDBT7tXvwn\n"+
+        "-----END CERTIFICATE-----";
+
+    @Test
+    public void testType3MessageWithCert() throws Exception {
+        final ByteArrayInputStream fis = new ByteArrayInputStream(cannedCert.getBytes(Consts.ASCII));
+
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        final Certificate cert = cf.generateCertificate(fis);
+
+        final byte[] bytes = new NTLMEngineImpl.Type3Message(
+            new Random(1234),
+            1234L,
+            "me", "mypassword", "myhost", "mydomain",
             toBytes("0001020304050607"),
             0xffffffff,
             "mytarget",
-            toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"));
+            toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"),
+            cert,
+            toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400"),
+            toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400FFFEFDFCFBFA")).getBytes();
+
+        checkArraysMatch(toBytes("4E544C4D53535000030000001800180058000000AE00AE0070000000040004001E0100000C000C0022010000140014002E0100001000100042010000FFFFFFFF0501280A0000000FEEFCCE4187D6CDF1F91C686C4E571D943695F1EA7B164788A437892FA7504320DA2D8CF378EBC83C59D7A3B2951929079B66621D7CF4326B010100000000000020CBFAD5DEB19D01A86886A5D29781420000000002000C0044004F004D00410049004E0001000C005300450052005600450052000400140064006F006D00610069006E002E0063006F006D00030022007300650072007600650072002E0064006F006D00610069006E002E0063006F006D0006000400020000000A00100038EDC0B7EF8D8FE9E1E6A83F6DFEB8FF00000000000000004D0045006D00790068006F00730074006D007900700061007300730077006F0072006400BB1AAD36F11631CC7CBC8800CEEE1C99"),
+            bytes);
     }
 
     @Test
@@ -247,4 +323,5 @@ public class TestNTLMEngineImpl {
             Assert.assertEquals(a1[i],a2[i]);
         }
     }
+
 }



Mime
View raw message