3

One one end a SOAP service (Java) protected with WS-security. One the other end, an HTTP server (Java) that acts as a client to the SOAP server.

I would like to implement single sign-on (using Negotiate/Kerberos) for users with the following delegation chain: Browser >> HTTP Server >> SOAP Server

Browser >> HTTP Server is not a problem and working (Negotiate over HTTP).

Impersonating the user on the HTTP server, the second hop HTTP Server >> SOAP Server is more challenging as I would like to use JNA, and I would also like the authentication to be performed using one single token (avoid the handshaking). I read it is supposed to be possible, as I only need one way authentication.

Passing on a token from HTTP server to SOAP server in the envelope is also working, with an interceptor on the SOAP server end. My only problem is to generate an interesting token that will not lead to a SEC_I_CONTINUE_NEEDED status when calling the AcceptSecurityContext.

Client code to get a token for the SOAP service (JNA is wrapped by the Waffle project):

import sun.misc.BASE64Encoder;
import waffle.windows.auth.IWindowsSecurityContext;
import waffle.windows.auth.impl.WindowsSecurityContextImpl;

public class WindowsAuthenticator {

public static final String securityPackage = "Negotiate";

public static String getKrbToken(String targetSPN) {
    if (null == targetSPN || targetSPN.trim().isEmpty()) {
        return null;
    }
    IWindowsSecurityContext ctx = WindowsSecurityContextImpl.getCurrent(securityPackage, targetSPN);
    byte[] token = ctx.getToken();
    return new BASE64Encoder().encode(token);
}

private WindowsAuthenticator() {
    super();
}

}

SOAP server code to validate Kerberos token

import org.apache.log4j.Logger;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.handler.RequestData;
import org.apache.ws.security.message.token.BinarySecurity;
import org.apache.ws.security.validate.Credential;
import org.apache.ws.security.validate.Validator;
import waffle.windows.auth.IWindowsAuthProvider;
import waffle.windows.auth.IWindowsIdentity;
import waffle.windows.auth.IWindowsSecurityContext;
import waffle.windows.auth.impl.WindowsAuthProviderImpl;

public class KerberosTokenValidator implements Validator {

private static final Logger logger =      Logger.getLogger(KerberosTokenValidator.class);
private final IWindowsAuthProvider windowsAuthProvider;

public KerberosTokenValidator() {
    this.windowsAuthProvider = new WindowsAuthProviderImpl();
}

@Override
public Credential validate(Credential credential, RequestData data) throws     WSSecurityException {
    BinarySecurity token = credential.getBinarySecurityToken();
    byte[] binaryToken = token.getToken();
    String id = token.getID();
    String type = token.getValueType();
    final IWindowsSecurityContext securityContext;
    try {
        securityContext = this.windowsAuthProvider.acceptSecurityToken(id, binaryToken, "Negotiate");


        this.windowsAuthProvider.acceptSecurityToken(id, binaryToken, "Negotiate");

        final IWindowsIdentity windowsIdentity = securityContext.getIdentity();
        securityContext.dispose();

    } catch (Exception exception) {
        logger.warn("error logging in user: " + exception.getMessage());
        throw new WSSecurityException("Could not auth....");
    }

    return null;
}

}

The problem is that acceptSecurityToken returns a SEC_I_CONTINUE_NEEDED and I end up engaging in a handshake (as done over HTTP for first hop). Here I really would like (if possible) the server to be happy with a single token (current design of SOAP API is completely stateless and is desired to be kept in this way).

geo.wolfer
  • 41
  • 6

0 Answers0