Using NTLM authentication in Java applications Using NTLM authentication in Java applications windows windows

Using NTLM authentication in Java applications


Out of the above list, only ntlmv2-auth and Jespa support NTLMv2. Jespa is workable but commercial. ntlmv2-auth I haven't tried but it's based on the code from Liferay, which I've seen working before.

'ntlm-authentication-in-java' is only NTLMv1, which is old, insecure, and works in a dwindling number of environments as people upgrade to newer Windows versions. JCIFS used to have an NTLMv1 HTTP auth filter, but it was removed in later versions, as the way it was implemented amounts to a man-in-the-middle attack on the insecure protocol. (The same appears to be true of 'ntlm-authentication-in-java'.)

The 'spnego' project is Kerberos not NTLM. If you want to replicate full IWA as IIS does it, you'd need to support both NTLMv2 and Kerberos ('NTLM' auth, 'Negotiate' auth, NTLMSSP-in-SPNego auth and NTLM-masquerading-as-Negotiate auth).


Luigi Dragone's script is really old and seems to always fail.

HttpURLConnection can work with NTLM if you add library jcifs, this example works with latest jcifs-1.3.18 :

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.net.UnknownHostException;import java.util.HashMap;import java.util.Map;import org.apache.http.impl.auth.NTLMEngineException;public class TestNTLMConnection {    public static void main(String[] args) throws UnknownHostException, IOException, NTLMEngineException {        // Method 1 : authentication in URL        jcifs.Config.registerSmbURLHandler();        URL urlRequest = new URL("http://domain%5Cuser:pass@127.0.0.1/");        // or Method 2 : authentication via System.setProperty()        // System.setProperty("http.auth.ntlm.domain", "domain");        // System.setProperty("jcifs.smb.client.domain", "domain");        // System.setProperty("jcifs.smb.client.username", "user");        // System.setProperty("jcifs.smb.client.password", "pass");        // Not verified // System.setProperty("jcifs.netbios.hostname", "host");        // System.setProperty("java.protocol.handler.pkgs", "jcifs");        // URL urlRequest = new URL("http://127.0.0.1:8180/simulate_get.php");        HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();        StringBuilder response = new StringBuilder();        try {            InputStream stream = conn.getInputStream();            BufferedReader in = new BufferedReader(new InputStreamReader(stream));            String str = "";            while ((str = in.readLine()) != null) {                response.append(str);            }            in.close();               System.out.println(response);        } catch(IOException err) {            System.out.println(err);        } finally {            Map<String, String> msgResponse = new HashMap<String, String>();            for (int i = 0;; i++) {                String headerName = conn.getHeaderFieldKey(i);                String headerValue = conn.getHeaderField(i);                if (headerName == null && headerValue == null) {                    break;                }                msgResponse.put(headerName == null ? "Method" : headerName, headerValue);            }            System.out.println(msgResponse);        }    }}

And if you are curious about the content of each handshake, you can find another example using jcifs and Socket on this thread.


Had to recently implement this at work hence here is updated solution with Spring's RestTemplate:

import org.apache.http.auth.AuthScope;import org.apache.http.auth.NTCredentials;import org.apache.http.impl.client.BasicCredentialsProvider;import org.apache.http.impl.client.HttpClients;import org.springframework.http.HttpEntity;import org.springframework.http.ResponseEntity;import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;import org.springframework.web.client.RestTemplate;import java.io.IOException;public class Runner {        public static void main(String[] args) {        var credentialsProvider = new BasicCredentialsProvider();        credentialsProvider.setCredentials(AuthScope.ANY, new NTCredentials("username", "password", "", "someDomain"));        try (var client = HttpClients.custom()                .setDefaultCredentialsProvider(credentialsProvider)                .build();) {            var requestFactory = new HttpComponentsClientHttpRequestFactory();            requestFactory.setHttpClient(client);            RestTemplate restTemplate = new RestTemplate(requestFactory);            ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("url", new HttpEntity<>("yourDtoObject"), String.class);        } catch (IOException e) {            e.printStackTrace();        }    }}

dependencies needed are: spring-web and org.apache.httpcomponents

ps: it is important to enter username without domain otherwise it doesn't work. As in if your domain is companyName/username often people just enter that whole thing as username and what you should do is enter them separately where domain="companyName" and username="username"