Using NTLM authentication in Java applications
Asked Answered
B

5

17

I want to use Windows NTLM authentication in my Java application to authenticate intranet users transparently. The users should not notice any authentication if using their browsers (single sign-on).

I've found a few libs with NTLM support, but don't know which one to use:

Any suggestions where to start?

Benedick answered 22/2, 2013 at 9:25 Comment(1)
Also be aware that in using NTLM for authentication, active attackers can authenticate their own session using a valid user's negotiation with the server.Mcelhaney
G
10

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).

Glebe answered 22/2, 2013 at 16:4 Comment(2)
Waffle is also a very good option for WIndows servers only, been using for 3 years, thousands of logins of day at multiple sites no problem, supports v2Ism
jcifs.samba.org/src/docs/faq.html#ntlmv2 >Q: Does jCIFS support NTLMv2? >A: Yes. As of 1.3.0, JCIFS fully supports NTLMv2 and uses it by default.` Note: The NTLM HTTP SSO Filter that used to be included with JCIFS cannot support NTLMv2.Battue
I
4

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:[email protected]/");

        // 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.

Inge answered 16/12, 2015 at 20:14 Comment(3)
Can you make that URL part a little more clear, I mean how to pass the username and passwordElasticize
%5C is backslash. e.g h-t-t-p://CORP\username:[email protected]/Inge
The first method gave me problems with complex urls, but commented out Method2 works well using Java 1.7 and jcifs 1.3.17 against IIS on Windows Server 2012.Ignacioignacius
C
4

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"

Choate answered 20/4, 2021 at 16:7 Comment(2)
Is this solution is available for the spring-webclient too?Disposition
You would need to configure credentials differently for webclient, probably somewhere as an authorization header, I never had to do it so I don't have an example, but what I would do is grab this example, run it, look in debugger what kind of headers end up getting created and try to recreate same authorization related headers manually for spring webclient - but that is just how I would go about solving it, actual implementation is up to youChoate
B
1

Ref: https://jcifs.samba.org/src/docs/faq.html#ntlmv2

Q: Does jCIFS support NTLMv2?
A: Yes. As of 1.3.0, JCIFS fully supports NTLMv2 and uses it by default.

Note: The NTLM HTTP SSO Filter that used to be included with JCIFS cannot support NTLMv2.

Battue answered 22/2, 2017 at 19:6 Comment(0)
S
0

Relatively from the list you gave,I would go with JCIFS. The library is mature , and their documentation is good. To top it off they had fairly regular releases , and the last one being Nov 2011.

Personal Experience : it was fairly easy to get started when compared to others i have tried (spnego and ntmv2auth)

Stygian answered 22/2, 2013 at 9:41 Comment(6)
JCIFS does not support NTLMv2 (ie Windows 7/8), unless explicitely enabled on client machines via Global Policy or a registry change.Ism
Could you clarify what did you mean by the explicit setup in Windows 7/8?Gauntlet
@MiroslavLigas. I think he means that Win 7/8 no longer use NTLMv1 since it is deprecated and considered open to exploits. Now if JCIFS only supports NTLMv1 then you'll need to force your Win 7/8 desktops to allow use of NTLMv1 (which is done by a change to Win Registry) in order for such desktops to work with JCIFS. In most companies the administrator will never allow such a change. For all practical purposes NTLMv1 is dead!Willodeanwilloughby
"JCIFS uses cryptography including RC4 128 (for NTLMv2) and AES 256 (for Kerberos) for authentication, digital signatures and encryption. Products that use cryptography and which are exported from the U.S. to other countries are supposed to obtain an export classification." is what I found on the first page of jcifs.samba.org ... and the entry is from 2009, so I call $hit on Tony's assumption. I have never tested it though ...They
@They - JCIFS uses NTLMv2 for the CIFS client. The HTTP bits in JCIFS never supported NTLMv2 and never will. All HTTP stuff in JCIFS is deprecated and will be removed.Brucie
@Brucie : You are right. JCIFS uses NTLMv2 not in HTTP. Ref: jcifs.samba.org/src/docs/faq.html#ntlmv2Battue

© 2022 - 2024 — McMap. All rights reserved.