Silent proxy-authentication via NTLMv2 and Kerberos

2.7k views Asked by At

My customer's Java-code (fat client) needs to access web-services through a network-proxy. The network-proxy requires authentication. The customer expects silent authentication as the current Windows-user (single-sign-on, no password-prompt!). I did lots of research and it seems most people use NTLMv2 or Kerberos for their server-side. But I need it on the client-side for a network-proxy-authentication.

I saw that Java seems to support Kerberos out-of-the-box -- this here looks good (but this does not -- I don't have the current user's password).

Java also seems to support NTLM out-of-the-box -- this and this look interesting.

Unfortunately, both solutions require user-name and password to be provided by my code. I do not know the password. I'd have to prompt the user, which I cannot do -- according to the spec it must be silent. Both solutions I found so far are not silent! I definitely need silent single-sign-on, though.

Hence, I wonder, whether I could somehow use Waffle or any other native (=> JNA) lib to solve my problem (requiring user+pw) for both NTLMv2- and Kerberos-based proxy-authentication. And it would also be cool, if such lib would save us the work of writing a kerberos-configuration-file.

But I didn't find anything like this in the Waffle docs. And Waffle is the only native lib I found so far. I assume that it must be native code, because anything else should have security barriers preventing it from getting a Kerberos-ticket (or a comparable token in NTLMv2) for the current user. But native code should IMHO be able to access such an authentication-token as the native OS-lib can access Windows-API which hands out the current user's NTLM/Kerberos-token to the current user only.

AFAIK IE and other native programs are able to access the web-services through the NTLM/Kerberos-protected proxy. Hence, it must be possible somehow.

Any hints on how to do silent authentication with a network-proxy?

If there's nothing ready-made, yet, maybe you can give me some hints how to implement a solution using Waffle or any other (native, JNI/JNA) lib?

If there is really no solution, yet, I'd even implement one using JNA or JNI myself -- could you please give me some hints into the right direction? I'm a GNU/Linux-guy and have to admit that I don't even know where to start searching for this in the Windows-API-documentation.

Btw. I already asked this in the Waffle-community, but didn't get any answer, yet. And maybe Waffle isn't the best solution, anyway? Maybe there's a better way?

1

There are 1 answers

4
Michael-O On

You wrote a lot, little substantial. Let me break down things for you.

Java also seems to support NTLM out-of-the-box -- this and this look interesting.

That's wrong. This is a private implementation using SSPI exclusively for HttpUrlConnection. Forget both.

Forget NTLM, it is connection-based, and proprietary. Use Kerberos!

I don't know what HTTP library you are using, but the approach is quite simple and you don't need Waffle for this, but solely JNA:

import java.io.IOException;
import java.util.Base64;

import org.apache.commons.io.HexDump;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import com.sun.jna.platform.win32.Secur32;
import com.sun.jna.platform.win32.Sspi;
import com.sun.jna.ptr.IntByReference;

public class SspiAuth {

  public static void main(String[] args)
      throws ArrayIndexOutOfBoundsException, IllegalArgumentException, IOException {

    Sspi.CredHandle cred = new Sspi.CredHandle();

    int status = Secur32.INSTANCE.AcquireCredentialsHandle(null, "Negotiate",
        Sspi.SECPKG_CRED_OUTBOUND, null, null, null, null, cred, null);
    System.out.println(status);

    Sspi.CtxtHandle ctxt = new Sspi.CtxtHandle();

    Sspi.SecBufferDesc bufferDesc = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN,
        Sspi.MAX_TOKEN_SIZE);
    IntByReference ctxtAttrs = new IntByReference();

    status = Secur32.INSTANCE.InitializeSecurityContext(cred, null,
        "HTTP/<fqdn of the proxy>",
        Sspi.ISC_REQ_MUTUAL_AUTH, 0, Sspi.SECURITY_NATIVE_DREP, null, 0, ctxt, bufferDesc,
        ctxtAttrs, null);

    System.out.println(status);
    HexDump.dump(bufferDesc.getBytes(), 0, System.out, 0);

    HttpClientBuilder builder = HttpClientBuilder.create();

    try (CloseableHttpClient httpClient = builder.build()) {

      HttpGet method = new HttpGet("https://deblndw024v.ad001.siemens.net:8444/manager/html");
      method.addHeader("Authorization",
          "Negotiate " + Base64.getEncoder().encodeToString(bufferDesc.getBytes()));
      CloseableHttpResponse response = httpClient.execute(method);
      EntityUtils.consumeQuietly(response.getEntity());
      response.close();

      System.out.println(response.getStatusLine());
      String responseToken = response.getFirstHeader("WWW-Authenticate").getValue()
          .substring(10);
      System.out.println(responseToken);
      byte[] rawResponseToken = Base64.getDecoder().decode(responseToken);
      Sspi.SecBufferDesc bufferDesc2 = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN,
          rawResponseToken);
      Sspi.CtxtHandle ctxt2 = new Sspi.CtxtHandle();
      Sspi.SecBufferDesc bufferDesc3 = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN,
          Sspi.MAX_TOKEN_SIZE);
      status = Secur32.INSTANCE.InitializeSecurityContext(cred, ctxt,
          "HTTP/<fqdn of the proxy>",
          Sspi.ISC_REQ_MUTUAL_AUTH, 0, Sspi.SECURITY_NATIVE_DREP, bufferDesc2, 0, ctxt2,
          bufferDesc3, ctxtAttrs, null);
      System.out.printf("0x%x%n", status);
    }

  }

}

Consider to free handles to avoid memory leaks!