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?

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. Commented Feb 23, 2013 at 10:43

5 Answers 5

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

Sign up to request clarification or add additional context in comments.

2 Comments

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

3 Comments

Can you make that URL part a little more clear, I mean how to pass the username and password
%5C is backslash. e.g h-t-t-p://CORP\username:[email protected]/
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.
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"

2 Comments

Is this solution is available for the spring-webclient too?
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 you
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.

Comments

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)

6 Comments

JCIFS does not support NTLMv2 (ie Windows 7/8), unless explicitely enabled on client machines via Global Policy or a registry change.
Could you clarify what did you mean by the explicit setup in Windows 7/8?
@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!
"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 ...
@thecarpy - 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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.