1

I want to implement a preemptive authentication for the DefaultHttpClient with NTLM. I found a libary from Dan Hounshell which works fine for normal authentication.

But I cannot figger out how to make this work with preemptive authentication. I found the question Preemptive Basic authentication with Apache HttpClient 4 with this cool answer:

UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
HttpRequest request = ...
request.addHeader(new BasicScheme().authenticate(creds, request));

Not perfect for my problem but a good idea. So I tried to use the NTLMSchemeFactory to create a new instance of AuthScheme with gives me the authenticate function.

NTCredentials ntc = new NTCredentials("example.com/user:pwd");
httpPost.addHeader(new NTLMScheme(new JCIFSEngine()).authenticate(ntc, httpPost));

When this function is called I'll get an exception:

org.apache.http.auth.AuthenticationException: Unexpected state: UNINITIATED

How can I fix that?

POST /login/ HTTP/1.1
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
Host: example.com
Connection: Keep-Alive
data=...

HTTP/1.1 401 Unauthorized
Content-Type: text/html
Server: Microsoft-IIS/7.5
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Wed, 12 Dec 2012 14:36:26 GMT
Content-Length: 1344

Much data...

POST /login/ HTTP/1.1
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
Host: example.com
Connection: Keep-Alive
Authorization: NTLM AAABBBCCC...FFF==
data=...

I think the first request is absolut useless.

Community
  • 1
  • 1
rekire
  • 47,260
  • 30
  • 167
  • 264

2 Answers2

0

My solution is this here:

public class PreemptiveNTLMHeader implements Header {
    private HttpRequest request;
    private NTCredentials ntc;

    public PreemptiveNTLMHeader(HttpRequest request, NTCredentials ntc) {
        this.request = request;
        this.ntc = ntc;
    }

    /* (non-Javadoc)
     * @see org.apache.http.Header#getName()
     */
    public String getName() {
        return "Authorization";
    }

    /* (non-Javadoc)
     * @see org.apache.http.Header#getValue()
     */
    public String getValue() {
        request.removeHeader(this);
        try {
            return "NTLM " + new JCIFSEngine().generateType1Msg(ntc.getDomain(), ntc.getWorkstation());
        } catch(NTLMEngineException e) {
            return "Failed";
        }
    }

    /* (non-Javadoc)
     * @see org.apache.http.Header#getElements()
     */
    public HeaderElement[] getElements() throws ParseException {
        return null;
    }
}

With this usage:

NTCredentials ntc = new NTCredentials("example.com/user:password");
httpPost.addHeader(new PreemptiveNTLMHeader(httpPost, ntc));

So will the DefaultHttpClient send a Authorization: NTLM AAA... header which skips the initial request. After the first usage of this header this header deletes itself to avoid that this dummy header overrides the real auth process. This works for me.

rekire
  • 47,260
  • 30
  • 167
  • 264
-1

NTLM authentication cannot be performed preemptively. It is a complex scheme that involves multiple (3) message exchanges.

ok2c
  • 26,450
  • 5
  • 63
  • 71
  • But the first call is really useless, there is no challange response or something like that. See my update. – rekire Dec 12 '12 at 14:47
  • The handshake is always initiated by the NTLM server by challenging the client. Feel free to consult the specification. – ok2c Dec 12 '12 at 14:56
  • Well I did not read the specification, but I see the comunication and based on this knowledge I think that the client starts with a kind of salt which is used for a chellange response auth. So far I think I can skip the part where the Server tells that he support NTLM. – rekire Dec 12 '12 at 15:30
  • 1
    Given that NTLM is a stateful scheme, an attempt to 'optimize' it in such a bizarre way is likely to get you nowhere, but there is no harm in trying. – ok2c Dec 12 '12 at 15:43
  • I found a way see my own answer :-) – rekire Dec 13 '12 at 08:40