Thursday, October 29, 2009

Securing Web Services with SAML Sender Vouches

After securing you web applications with SAML is the next step to secure your web services with SAML Sender Vouches ws-security policy, this can be complex because you need to know a lot over the weblogic server configuration and its java security frameworks. For example you need to configure two Weblogic servers, the first is the Web Service server and the second server is the Secure Token Service ( STS ). After that you need to add some client credential providers to the generated web service proxy client. Thanks to Vishal Jain of Oracle who provided me a working example.
This is how SAML Sender Vouches works and what we need to do in weblogic / java.
The short version is, the web service proxy client call the STS server to get an SAML assertion on behalf of the User to call the Web Service.

The long version, the user provides its credentials to the ws proxy client and the ws proxy client calls the STS server and provides the username / password of the user and the client key.
The STS validates the user and the ws proxy client certificate and the STS returns the STS identity assertion to the ws proxy client. The ws proxy client uses this STS assertion together with the ws client and ws server certificate to call the web service.

First we need to have 3 certificates, the first is alice, this will be used in the ws proxy client and the second certificate is bob, this will be used in the Weblogic web service server and the last we use wssipsts certificate for the Weblogic STS server. Add these keys into a java keystore.

Setting up the Secure Token Service (STS)
Create a new Weblogic 10.3.1 domain and start the admin server. First we need to enable SSL in the general tab of the server and then add our keystores in the keystore tab.
Provide the STS certificate alias, in my case wssipprv

Go the myrealm security where we add the Alice user and provide a password, Very important the username must match with the CN of the Alice certificate. The user provides the credentials and must match with user in WLS and the ws proxy client provides the Alice certificate and this must match with the PKI Credential mapping.

Go to the Credential mapping tab of the Provider tab and add a PKI Credential Mapping where we import the keystore and Add a SAML Credential Mapping version 2 where we add the Web Service URL.

Open the just created PKI credential mapping and add the keystore.

Next we open the SAML Credential Mapping version 2 and provide the Issuer URL and Name Qualifier.

Add the public key of the wssipsts

Add a WSS/Sender-Vouches Relying Party

Enable this and provide the target url of the Web Service Url and assign assertions and include key info


That is all for the STS server and now we can deploy the STS web service.


package nl.whitehorses.sts;

import weblogic.jws.Policy;
import weblogic.wsee.security.saml.SAMLTrustTokenProvider;
import weblogic.wsee.security.wst.framework.TrustTokenProviderRegistry;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
@Policy(uri="policy:Wssp1.2-2007-Wssc1.3-Bootstrap-Https-UNT.xml")
public class StsUnt {

static {
init();
}

@WebMethod
@Policy(uri="policy:Wssp1.2-2007-SignBody.xml")
public String dummyMethod(String s) {
return s;
}

static void init() {
TrustTokenProviderRegistry reg = TrustTokenProviderRegistry.getInstance();
SAMLTrustTokenProvider provider = new MySAMLTrustTokenProvider();
reg.registerProvider("http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID", provider);
reg.registerProvider("http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID", provider);
reg.registerProvider("http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0", provider);
reg.registerProvider("http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.0", provider);
reg.registerProvider("http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1", provider);
}

static class MySAMLTrustTokenProvider extends SAMLTrustTokenProvider {

}
}


Configure the Weblogic server for the Web Services
Create a new Weblogic domain and use the same keystore, we don't need to setup SSL on this server.

Go the myrealm security and go to providers tab where we add a new PKI Credential Mapping in the credentials tab. ( Use the same setting as the STS server )
We need to add 2 authentication providers and change the 2 default providers.
Create a SAML Authentication and SAML Identity Assertion provider.

Every authentication provider need to have the SUFFICIENT control flag.

Change the SAML Identity Assertion by adding an asserting party and the STS public certificate

Import the STS certificate

Change the just create Sender-Vouches asserting party. Enable this and provide the target url of the web service and add the issuer url, signature required and expect the STS certificate and allow virtual user.


Change the defaultIdentityAsserter and add wsse:PasswordDigest and X.509 as active types.
In the provider specific tab we need to set CN in the Default User Name Mapper Attribute Type and enable Use Default User Name Mapper.


In the DefaultAuthenticator we need to set control flag to SUFFICIENT
And in the provider specific tab. Enable Password Digests and Minimum Password Length to 1.

That's all for the Web Service server. Now we can deploy the webservice.

package nl.whitehorses.sts.ws;

import weblogic.jws.Policies;
import weblogic.jws.Policy;
import javax.jws.WebService;

@Policies(
{
@Policy(uri = "policy:Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml"),
@Policy(uri = "policy:Wssp1.2-2007-SignBody.xml"),
@Policy(uri = "policy:Wssp1.2-2007-EncryptBody.xml")
}
)

@WebService
public class EchoService {
public String echo( String hello){
return hello;
}
}


Generating the Web Service Proxy Client
The last step we need to generate a web service proxy client and add the username and the client credentials mappings.

package nl.whitehorses.sts.ws.client;

import java.io.InputStream;
import java.io.ByteArrayInputStream;

import java.security.cert.X509Certificate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import java.net.URL;
import javax.xml.namespace.QName;


import javax.xml.ws.BindingProvider;
import javax.xml.ws.WebServiceRef;

import weblogic.security.SSL.TrustManager;

import weblogic.wsee.message.WlMessageContext;
import weblogic.wsee.security.bst.ClientBSTCredentialProvider;
import weblogic.wsee.security.saml.SAMLTrustCredentialProvider;
import weblogic.wsee.security.unt.ClientUNTCredentialProvider;

import weblogic.xml.crypto.wss.WSSecurityContext;
import weblogic.xml.crypto.wss.provider.CredentialProvider;

public class EchoServicePortClient
{
@WebServiceRef
private static EchoServiceService echoServiceService;

private static String stsUntPolicy =
"<?xml version=\"1.0\"?>\n" +
"<wsp:Policy\n" +
" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\"\n" +
" xmlns:sp=\"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702\"\n" +
" >\n" +
" <sp:TransportBinding>\n" +
" <wsp:Policy>\n" +
" <sp:TransportToken>\n" +
" <wsp:Policy>\n" +
" <sp:HttpsToken/>\n" +
" </wsp:Policy>\n" +
" </sp:TransportToken>\n" +
" <sp:AlgorithmSuite>\n" +
" <wsp:Policy>\n" +
" <sp:Basic256/>\n" +
" </wsp:Policy>\n" +
" </sp:AlgorithmSuite>\n" +
" <sp:Layout>\n" +
" <wsp:Policy>\n" +
" <sp:Lax/>\n" +
" </wsp:Policy>\n" +
" </sp:Layout>\n" +
" <sp:IncludeTimestamp/>\n" +
" </wsp:Policy>\n" +
" </sp:TransportBinding>\n" +
" <sp:SupportingTokens>\n" +
" <wsp:Policy>\n" +
" <sp:UsernameToken\n" +
" sp:IncludeToken=\"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient\">\n" +
" <wsp:Policy>\n" +
" <sp:WssUsernameToken10/>\n" +
" </wsp:Policy>\n" +
" </sp:UsernameToken>\n" +
" </wsp:Policy>\n" +
" </sp:SupportingTokens>\n" +
"</wsp:Policy>";

public static void main(String[] args) {
System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
try {

String wsURL = "http://10.10.10.10:7011/saml-ws-context-root/EchoServicePort?WSDL";

echoServiceService = new EchoServiceService( new URL(wsURL)
, new QName("http://ws.sts.whitehorses.nl/", "EchoServiceService"));
EchoService echoService = echoServiceService.getEchoServicePort();

System.setProperty("javax.net.ssl.trustStore", "C:/projecten/workspace/11g_prod/saml1.1_ws/wsttest1/certs/cacerts");

Map<String, Object> requestContext = ((BindingProvider)echoService).getRequestContext();

List<CredentialProvider> credList = new ArrayList<CredentialProvider>();

// Add the necessary credential providers to the list
InputStream policy = new ByteArrayInputStream(stsUntPolicy.getBytes("UTF-8"));
requestContext.put(WlMessageContext.WST_BOOT_STRAP_POLICY, policy );

String stsURL = "https://localhost:7022/sts/StsUntPort";


requestContext.put(WlMessageContext.STS_ENDPOINT_ADDRESS_PROPERTY, stsURL);
requestContext.put(WSSecurityContext.TRUST_MANAGER,
new TrustManager() {
public boolean certificateCallback(X509Certificate[] chain, int validateErr) {
// need to validate if the server cert can be trusted
return true;
}
});

credList.add(new SAMLTrustCredentialProvider());

String username = "Alice";
String password = "weblogic1";
credList.add(new ClientUNTCredentialProvider(username.getBytes(), password.getBytes()));

// ClientBSTCredentialProvider
String defaultClientcert = "C:/projecten/workspace/11g_prod/saml1.1_ws/wsttest1/certs/Alice.cer";
String clientcert = System.getProperty("target.clientcert", defaultClientcert);
String defaultClientkey = "C:/projecten/workspace/11g_prod/saml1.1_ws/wsttest1/certs/Alice.prv";
String clientkey = System.getProperty("target.clientkey", defaultClientkey);

String defaultServerCert = "C:/projecten/workspace/11g_prod/saml1.1_ws/wsttest1/certs/Bob.cer";
String serverCert = System.getProperty("target.serverCert", defaultServerCert);

credList.add(new ClientBSTCredentialProvider(clientcert, clientkey, serverCert));

requestContext.put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST, credList);

// Add your code to call the desired methods.
System.out.println(echoService.echo("Hello"));

} catch (Exception ex) {
ex.printStackTrace();
}
}

}

0 comments: