NTLM support in HttpClient

Background

NTLM is a proprietary authentication scheme developed by Microsoft and optimized for Windows operating system.

Until year 2008 there was no official, publicly available, complete documentation of the protocol. Unofficial 3rd party protocol descriptions existed as a result of reverse-engineering efforts. It was not really known whether the protocol based on the reverse-engineering were complete or even correct.

Microsoft published MS-NLMP and MS-NTHT specifications in February 2008 as a part of its Interoperability Principles initiative.

HttpClient as of version 4.1 initially supported NTLMv1, NTLMv2, and NTLM2SessionResponse authentication protocols, based on the reverse engineering approach. As of version 4.2.3, HttpClient now supports a more correct implementation, based in large part on Microsoft's own specifications. This is expected to correct a number of problems, especially since Microsoft (as of Windows Server 2008 R2) began using a new implementation of its protocols. This new Microsoft implementation has led to authentication failures in some cases from some of the older reverse-engineered client implementations of NTLM.

The new HttpClient NTLM implementation is known to have been tried successfully against at least the following systems:

  • Windows Server 2000 and Server 2003 systems, configured to use LM and NTLMv1 authentication

  • Windows Server 2003 systems, configured to use NTLMv2 authentication

  • Windows Server 2008 R2 systems, configured to use NTLM2SessionResponse authentication

If the current HttpClient NTLM implementation should prove problematic in your environment, we'd definitely like to hear about it. You are also welcome to try an alternative NTLM implementation, should it seem necessary. One can also use JCIFS, which includes an NTLM engine developed by members of the Samba project.

Using Samba JCIFS as an alternative NTLM engine

Follow these instructions to build an NTLMEngine implementation using JCIFS library

Disclaimer: Use code at your own discretion. Do NOT report any issues related to the use of JCIFS library to Apache HttpComponents project.

  • Download version 1.3.14 or newer of the JCIFS library from the Samba web site

  • Implement NTLMEngine interface

    import java.io.IOException;
    
    import jcifs.ntlmssp.NtlmFlags;
    import jcifs.ntlmssp.Type1Message;
    import jcifs.ntlmssp.Type2Message;
    import jcifs.ntlmssp.Type3Message;
    import jcifs.util.Base64;
    
    import org.apache.http.impl.auth.NTLMEngine;
    import org.apache.http.impl.auth.NTLMEngineException;
    
    public final class JCIFSEngine implements NTLMEngine {
    
        private static final int TYPE_1_FLAGS = 
                NtlmFlags.NTLMSSP_NEGOTIATE_56 | 
                NtlmFlags.NTLMSSP_NEGOTIATE_128 | 
                NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 | 
                NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | 
                NtlmFlags.NTLMSSP_REQUEST_TARGET;
    
        public String generateType1Msg(final String domain, final String workstation)
                throws NTLMEngineException {
            final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
            return Base64.encode(type1Message.toByteArray());
        }
    
        public String generateType3Msg(final String username, final String password,
                final String domain, final String workstation, final String challenge)
                throws NTLMEngineException {
            Type2Message type2Message;
            try {
                type2Message = new Type2Message(Base64.decode(challenge));
            } catch (final IOException exception) {
                throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
            }
            final int type2Flags = type2Message.getFlags();
            final int type3Flags = type2Flags
                    & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
            final Type3Message type3Message = new Type3Message(type2Message, password, domain,
                    username, workstation, type3Flags);
            return Base64.encode(type3Message.toByteArray());
        }
    
    }
    
  • Implement AuthSchemeProvider interface

    public class JCIFSNTLMSchemeFactory implements AuthSchemeProvider {
    
        public AuthScheme create(final HttpContext context) {
            return new NTLMScheme(new JCIFSEngine());
        }
    }
    
  • Register NTLMSchemeFactory with the HttpClient instance you want to NTLM enable.

    Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
          .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
          .register(AuthSchemes.BASIC, new BasicSchemeFactory())
          .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
          .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
          .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
          .build();
    CloseableHttpClient httpClient = HttpClients.custom()
          .setDefaultAuthSchemeRegistry(authSchemeRegistry)
          .build();
    
  • Set NTCredentials for the web server you are going to access.