Step by step, how to create an HttpClient that supports NTLM authentication in Java
The Problem:
Suppose that we have an instance of Apache HttpClient ( we will use the CloseableHttpClient implementation). We want to perform ΗΤΤP requests to a server that it uses the NTLM authentication security.
Some Theory:
NTLM is a challenge-response authentication protocol which uses three messages to authenticate a client .
Participants:
- Client
- Server
- Domain Controller(DC)
The Flow:
- The client sends the user name to the server (format DomainName\Username) (Username)
- The server generates a 16-byte random number, called a Challenge or Nonce, and sends it to the client. (Challenge)
- The client encrypts this challenge with the hash of the user’s password and returns the result to the server. (Response)
- The server sends to the DC the Username,Challenge,Response
- The DC validates if the authentication is successful.
For more info check the Microsoft Doc
Let’s go to Action!
Maven dependency:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
The Code:
In order to create the ΝTLM auth provider we need to create:
1. CredentialsProvider
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new NTCredentials(“username”, “password”,”workstasion”,”domain name”));
2. AuthSchemeRegistry
Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.NTLM, new NTLMSchemeFactory())
.build()
AuthSchemeProvider implementation creates and initializes NTLMScheme instances configured to use the default NTLMEngine implementation. The NTLMEngine can be used to generate Type1 messages and Type3 messages in response to a Type2 challenge
3. Set the TargetPreferredAuthSchemes
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(connectionTimeout * 1000)
.setConnectionRequestTimeout(connectionRequestTimeout * 1000)
.setCookieSpec(CookieSpecs.DEFAULT)
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.KERBEROS, AuthSchemes.SPNEGO))
.build();
We give higher priority to NTLM auth schema compare to others
** Notice **
The order is important , also, if you set only setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM)) you will fail to authenticate and will have in logs :
“Authentication scheme Negotiate not supported” .
This means that the client is only willing to do NTLM while the server is only willing to do Negotiate, thus failing to agree on a common authentication scheme.
4. Build the Client:
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultAuthSchemeRegistry(authSchemeRegistry)
.setConnectionManager(new PoolingHttpClientConnectionManager())
.setDefaultCookieStore(new BasicCookieStore())
.setDefaultRequestConfig(config)
.build()
Our client is ready to use!
5. Unit Test
HttpGet httpGet = new HttpGet(“path”);
httpGet.addHeader(“Content-type”, “application/json; charset=utf-8”);
CloseableHttpResponse response = httpClient.execute(httpGet);
Assert.assertEquals(200,response.getStatusLine().getStatusCode());
Check the code in GitHub Repo:
https://github.com/despoina555/CodeExamples
Class: /src/main/java/org/despina/NtlmAuthImplemetation.java
Unit test: src/test/java/org/despina/AppTest.java