JBoss Community Archive (Read Only)

JBoss AS 7.2

Trust and STS

Trust overview

The WS-Trust specification defines extensions to WS-Security to deal with the issuing, renewing, and validating of security tokens; it also defines how to establish, assess the presence of, and broker trust relationships between participants in a secure message exchange.

Complex applications spanning multiple domains typically suffer from the need for generating and sharing multiple keys; moreover dealing with services updates when credentials change is usually painful. With WS-Trust, a trusted Security Token Service (STS) can be used to obtain security tokens which are then used as authentication / authorization. A client authenticates itself with the STS based on policies and requirements defined by the STS; the STS then provides a security token (e.g. a SAML token) that the client then uses to talk to the target service; the service can validate that token to make sure it really came from the trusted STS.

Security Token Service

The security token service is the core of the WS-Trust extension to WS-Security; it is a service that offers some or all of the following functionalities:

  • issuing security tokens of different types depending on the received credentials

  • validation of security tokens

  • renewal of security tokens

  • cancellation of security tokens

  • transformation of security tokens into different type ones

In the basic scenario, the WSDL contract for an endpoint service will usually include a WS-Security policy stating that a particular security token type is required to access the service. Clients reading that contract will ask the STS for a security token of the required type and attach it to the message invocation to the service. The endpoint service will locally validate the received token or dispatch it to the STS for validation.

Apache CXF support

JBossWS inherits Apache CXF support for WS-Trust, which is fully integration with WS-Security Policy support. On client side, a STSClient is used to contact the STS and e.g get the security token; the STSClient can either be programmatically provided or automatically created by CXF runtime (through policy support) and configured through properties as done with plain WS-Security (keystore locations, aliases, etc.).

Any specification compliant STS can be used; however Apache CXF comes with its own STS implementation, which can be deployed on JBoss Application Server using JBossWS integration too. Detailed information on the Apache CXF STS implementation in a multiple blog post series here.

Example

Here is an example of a basic WS-Trust scenario. A service provider published a WSDL with policy assertions establishing who the communication must happen. A SAML 2.0 token issued by an STS is required to access the service endpoint. The client will authenticate itself to the STS using a UsernameToken over the symmetric binding and the STS will issue it the desired SAML 2.0 token, which the client will then forwards to the service provider endpoint. As the IssuedToken is defined as the InitiatorToken of the Asymmetric binding in the policy of the service provider, the client will use the associated secret key for message signing.

Endpoint

The service provider is a contract-first endpoint and the need for WS-Trust communication is completely driven by the wsdl. It comes with policies requiring signature and encryption of messages and setting the WS-Trust requirements (SAML 2.0 security token and STS location). WS-AddressingMetadata is used to specify the wsdl location of the STS and the servive/port name in it to be used:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" name="SecurityService"
		xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy"
		xmlns:xsd="http://www.w3.org/2001/XMLSchema"
		xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
		xmlns="http://schemas.xmlsoap.org/wsdl/"
		xmlns:wsp="http://www.w3.org/ns/ws-policy"
		xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
        xmlns:wsaws="http://www.w3.org/2005/08/addressing"
        xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
        xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
  <types>
    <xsd:schema>
      <xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" schemaLocation="SecurityService_schema1.xsd"/>
    </xsd:schema>
  </types>
  <message name="sayHello">
    <part name="parameters" element="tns:sayHello"/>
  </message>
  <message name="sayHelloResponse">
    <part name="parameters" element="tns:sayHelloResponse"/>
  </message>
  <portType name="ServiceIface">
    <operation name="sayHello">
      <input message="tns:sayHello"/>
      <output message="tns:sayHelloResponse"/>
    </operation>
  </portType>
  <binding name="SecurityServicePortBinding" type="tns:ServiceIface">
    <wsp:PolicyReference URI="#AsymmetricSAML2Policy" />
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="sayHello">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
        <wsp:PolicyReference URI="#Input_Policy" />
      </input>
      <output>
        <soap:body use="literal"/>
        <wsp:PolicyReference URI="#Output_Policy" />
      </output>
    </operation>
  </binding>
  <service name="SecurityService">
    <port name="SecurityServicePort" binding="tns:SecurityServicePortBinding">
      <soap:address location="http://localhost:8080/jaxws-samples-wsse-policy-trust/SecurityService"/>
    </port>
  </service>

  <wsp:Policy wsu:Id="AsymmetricSAML2Policy">
		<wsp:ExactlyOne>
			<wsp:All>
				<wsam:Addressing wsp:Optional="false">
					<wsp:Policy />
				</wsam:Addressing>
				<sp:AsymmetricBinding>
					<wsp:Policy>
						<sp:InitiatorToken>
							<wsp:Policy>
								<sp:IssuedToken
									sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
									<sp:RequestSecurityTokenTemplate>
										<t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType>
										<t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey</t:KeyType>
									</sp:RequestSecurityTokenTemplate>
									<wsp:Policy>
										<sp:RequireInternalReference />
									</wsp:Policy>
									<sp:Issuer>
										<wsaws:Address>http://localhost:8080/jaxws-samples-wsse-policy-trust-sts/SecurityTokenService</wsaws:Address>
										<wsaws:Metadata xmlns:wsdli="http://www.w3.org/2006/01/wsdl-instance"
										                wsdli:wsdlLocation="http://localhost:8080/jaxws-samples-wsse-policy-trust-sts/SecurityTokenService?wsdl">
										    <wsaw:ServiceName xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
										                    xmlns:stsns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
										                    EndpointName="UT_Port">stsns:SecurityTokenService</wsaw:ServiceName>
										</wsaws:Metadata>
									</sp:Issuer>
								</sp:IssuedToken>
							</wsp:Policy>
						</sp:InitiatorToken>
						<sp:RecipientToken>
							<wsp:Policy>
								<sp:X509Token
									sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
									<wsp:Policy>
										<sp:WssX509V3Token10 />
										<sp:RequireIssuerSerialReference />
									</wsp:Policy>
								</sp:X509Token>
							</wsp:Policy>
						</sp:RecipientToken>
						<sp:Layout>
							<wsp:Policy>
								<sp:Lax />
							</wsp:Policy>
						</sp:Layout>
						<sp:IncludeTimestamp />
						<sp:OnlySignEntireHeadersAndBody />
						<sp:AlgorithmSuite>
							<wsp:Policy>
								<sp:Basic256 />
							</wsp:Policy>
						</sp:AlgorithmSuite>
					</wsp:Policy>
				</sp:AsymmetricBinding>
				<sp:Wss11>
					<wsp:Policy>
						<sp:MustSupportRefIssuerSerial />
						<sp:MustSupportRefThumbprint />
						<sp:MustSupportRefEncryptedKey />
					</wsp:Policy>
				</sp:Wss11>
				<sp:Trust13>
					<wsp:Policy>
						<sp:MustSupportIssuedTokens />
						<sp:RequireClientEntropy />
						<sp:RequireServerEntropy />
					</wsp:Policy>
				</sp:Trust13>
			</wsp:All>
		</wsp:ExactlyOne>
	</wsp:Policy>

	<wsp:Policy wsu:Id="Input_Policy">
		<wsp:ExactlyOne>
			<wsp:All>
				<sp:EncryptedParts>
					<sp:Body />
				</sp:EncryptedParts>
				<sp:SignedParts>
					<sp:Body />
					<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing" />
				</sp:SignedParts>
			</wsp:All>
		</wsp:ExactlyOne>
	</wsp:Policy>

	<wsp:Policy wsu:Id="Output_Policy">
		<wsp:ExactlyOne>
			<wsp:All>
				<sp:EncryptedParts>
					<sp:Body />
				</sp:EncryptedParts>
				<sp:SignedParts>
					<sp:Body />
					<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing" />
					<sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing" />
				</sp:SignedParts>
			</wsp:All>
		</wsp:ExactlyOne>
	</wsp:Policy>
</definitions>

The endpoint implementation class is a POJO featuring Apache CXF @EndpointProperty annotations to provide WSS4J security properties:

package org.jboss.test.ws.jaxws.samples.wsse.policy.trust;

import javax.jws.WebService;

import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;

@WebService
(
   portName = "SecurityServicePort",
   serviceName = "SecurityService",
   wsdlLocation = "WEB-INF/wsdl/SecurityService.wsdl",
   targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy",
   endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.ServiceIface"
)
@EndpointProperties(value = {
      @EndpointProperty(key = "ws-security.signature.username", value = "myservicekey"),
      @EndpointProperty(key = "ws-security.signature.properties", value = "serviceKeystore.properties"),
      @EndpointProperty(key = "ws-security.encryption.properties", value = "serviceKeystore.properties"),
      @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.ServerCallbackHandler")
})
public class ServiceImpl implements ServiceIface
{
   public String sayHello()
   {
      return "WS-Trust Hello World!";
   }
}

... the serviceKeystore.properties file references the keystore, aliases, etc.

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=sspass
org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
org.apache.ws.security.crypto.merlin.keystore.file=servicestore.jks

... while ServerCallbackHandler is an usual implementation of CallbackHandler to allow Apache CXF access to the keystore:

package org.jboss.test.ws.jaxws.samples.wsse.policy.trust;

import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;

public class ServerCallbackHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof WSPasswordCallback) {
                WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
                if ("myservicekey".equals(pc.getIdentifier())) {
                    pc.setPassword("skpass");
                    break;
                }
            }
        }
    }
}

Assuming the servicestore.jks keystore has been properly generated and contains service provider (server) full key (private/certificate + public key) as well as the STS public key, we can proceed to packaging the endpoint. Here is the expected content (the endpoint is a POJO one in a war archive, but EJB3 endpoints in jar archives are of course also supported):

alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-tests/target/test-libs/jaxws-samples-wsse-policy-trust.war
     0 Thu Jun 21 14:03:24 CEST 2012 META-INF/
   159 Thu Jun 21 14:03:22 CEST 2012 META-INF/MANIFEST.MF
     0 Thu Jun 21 14:03:24 CEST 2012 WEB-INF/
     0 Thu Jun 21 14:03:24 CEST 2012 WEB-INF/classes/
     0 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/
     0 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/
     0 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/test/
     0 Thu Jun 21 14:03:22 CEST 2012 WEB-INF/classes/org/jboss/test/ws/
     0 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/
     0 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/
     0 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/
     0 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/
     0 Thu Jun 21 14:03:22 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/
   705 Thu Jun 21 14:03:22 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/SayHello.class
  1069 Thu Jun 21 14:03:22 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/SayHelloResponse.class
     0 Thu Jun 21 14:03:22 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/
  1159 Thu Jun 21 14:03:22 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/ServerCallbackHandler.class
   383 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/ServiceIface.class
  1365 Thu Jun 21 14:03:20 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/ServiceImpl.class
     0 Thu Jun 21 14:03:18 CEST 2012 WEB-INF/wsdl/
  6478 Thu Jun 21 14:03:18 CEST 2012 WEB-INF/wsdl/SecurityService.wsdl
   653 Thu Jun 21 14:03:18 CEST 2012 WEB-INF/wsdl/SecurityService_schema1.xsd
  1121 Thu Jun 21 14:03:18 CEST 2012 WEB-INF/classes/serviceKeystore.properties
  3350 Thu Jun 21 14:03:18 CEST 2012 WEB-INF/classes/servicestore.jks

As you can see, the jaxws classes generated by the tools are of course also included. The manifest declares the JBoss Modules dependencies for allowing Apache CXF annotations and WSS4J usage:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.6.0_26-b03 (Sun Microsystems Inc.)
Dependencies: org.apache.ws.security,org.apache.cxf

Client

You start by consuming the published WSDL contract using the wsconsume tool on client side too. Then you simply invoke the the endpoint as a standard JAX-WS one:

QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService");
URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
ServiceIface proxy = (ServiceIface) service.getPort(ServiceIface.class);

//setup WS-Security
Map<String, Object> ctx = ((BindingProvider) proxy).getRequestContext();
ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey");
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey");
ctx.put(SecurityConstants.USERNAME + ".it", "alice");
ctx.put(SecurityConstants.CALLBACK_HANDLER + ".it", new ClientCallbackHandler());
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES + ".it", Thread.currentThread().getContextClassLoader().getResource("META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_USERNAME + ".it", "mystskey");
ctx.put(SecurityConstants.STS_TOKEN_USERNAME + ".it", "myclientkey");
ctx.put(SecurityConstants.STS_TOKEN_PROPERTIES + ".it", Thread.currentThread().getContextClassLoader().getResource("META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO + ".it", "true");
ctx.put("ws-security.sts.disable-wsmex-call-using-epr-address", "true");

proxy.sayHello();

As you can see, as usual the WS-Security properties are set in the request context. The ".it" suffix is used for properties related to communication with the Security Token Service (which is described below and also includes policies enforcing signed and encrypted messages). The ClientCallbackHandler is basically similar to the endpoint server one. The clientKeystore.properties file is the client side equivalent of the serviceKeystore.properties and references the clientstore.jks keystore file, which has been populated with the client full key (private/certificate + public key) as well as the server endpoint and STS public keys.

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=cspass
org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey
org.apache.ws.security.crypto.merlin.keystore.file=META-INF/clientstore.jks

The Apache CXF WS-Policy engine will digest the security requirements in the endpoint contract and ensure a valid secure communication is in place for interacting with the server endpoint. More in details, here is what will be happening:

  1. the client get the service endpoint WSDL; the Apache CXF engine parses it, processes the included policy and decides to contact the STS

  2. the STS client is automatically injected into the application client and gets the STS wsdl; the Apache CXF engine processes the policy in it

  3. a WS-Security enabled communication (as per STS advertised policy) is established and the client issues a request for getting a SAML assertion token from the STS

  4. the STS endpoint receives the request, extracts the client identify from it and authenticates the client

  5. the SAML token is returned to the client, which uses it for establishing a new connection with the service endpoint; the Apache CXF engine again setups signature/encryption as per endpoint advertised policy

  6. the service endpoint receives the token provided through the STS and performs the required operation

Apache CXF STS

As mentioned above, Apache CXF comes with its own Security Token Service implementation. That is completely configurable and can be used as a JAX-WS WebServiceProvider endpoint running in payload service mode. An extension to the org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider is created, properly annotated and included in an usual webservice endpoint deployment.

package org.jboss.test.ws.jaxws.samples.wsse.policy.trust;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.xml.ws.WebServiceProvider;

import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.interceptor.InInterceptors;
import org.apache.cxf.sts.StaticSTSProperties;
import org.apache.cxf.sts.operation.TokenIssueOperation;
import org.apache.cxf.sts.operation.TokenValidateOperation;
import org.apache.cxf.sts.service.ServiceMBean;
import org.apache.cxf.sts.service.StaticService;
import org.apache.cxf.sts.token.provider.SAMLTokenProvider;
import org.apache.cxf.sts.token.validator.SAMLTokenValidator;
import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider;

@WebServiceProvider(serviceName = "SecurityTokenService",
      portName = "UT_Port",
      targetNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/",
      wsdlLocation = "WEB-INF/wsdl/ws-trust-1.4-service.wsdl")
@EndpointProperties(value = {
      @EndpointProperty(key = "ws-security.signature.username", value = "mystskey"),
      @EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"),
      @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.STSCallbackHandler"),
      @EndpointProperty(key = "ws-security.validate.token", value = "false") //to let the JAAS integration deal with validation through the interceptor below
})
@InInterceptors(interceptors = {"org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingPolicyInterceptor"})
public class SampleSTS extends SecurityTokenServiceProvider
{
   public SampleSTS() throws Exception
   {
      super();

      StaticSTSProperties props = new StaticSTSProperties();
      props.setSignaturePropertiesFile("stsKeystore.properties");
      props.setSignatureUsername("mystskey");
      props.setCallbackHandlerClass(STSCallbackHandler.class.getName());
      props.setIssuer("DoubleItSTSIssuer");

      List<ServiceMBean> services = new LinkedList<ServiceMBean>();
      StaticService service = new StaticService();
      service.setEndpoints(Arrays.asList("http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService", "http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService"));
      services.add(service);

      TokenIssueOperation issueOperation = new TokenIssueOperation();
      issueOperation.setServices(services);
      issueOperation.getTokenProviders().add(new SAMLTokenProvider());
      issueOperation.setStsProperties(props);

      TokenValidateOperation validateOperation = new TokenValidateOperation();
      validateOperation.getTokenValidators().add(new SAMLTokenValidator());
      validateOperation.setStsProperties(props);

      this.setIssueOperation(issueOperation);
      this.setValidateOperation(validateOperation);
   }
}

The @WebServiceProvider annotation references a WS-SecurityPolicy enriched version of the WS-Trust 1.4 wsdl.

The @EndpointProperty annotations provides the usual WSS4J configuration elements.

The @InInterceptor annotation is used to specify a JBossWS integration interceptor to be used for authenticating incoming requests; JAAS integration is used here for authentication, so basically the username/passoword coming from the UsernameToken in the client message are used for authenticating the client against a security domain on the application server hosting the STS deployment.

The stsKeystore.properties file is as follows:

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=stsspass
org.apache.ws.security.crypto.merlin.keystore.file=stsstore.jks

... while STSCallbackHandler grants access to the stsstore.jks, which has been populated with the STS full key (private/certificate + public key) as well as the server endpoint and client public keys.

package org.jboss.test.ws.jaxws.samples.wsse.policy.trust;

import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;

public class STSCallbackHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof WSPasswordCallback) {
                WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
                if ("mystskey".equals(pc.getIdentifier())) {
                    pc.setPassword("stskpass");
                    break;
                }
            }
        }
    }
}

Here is how the STS webservice provider is packaged:

alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-tests/target/test-libs/jaxws-samples-wsse-policy-trust-sts.war
     0 Mon Jun 25 13:39:06 CEST 2012 META-INF/
   164 Mon Jun 25 13:39:04 CEST 2012 META-INF/MANIFEST.MF
     0 Mon Jun 25 13:39:06 CEST 2012 WEB-INF/
     0 Mon Jun 25 13:39:06 CEST 2012 WEB-INF/classes/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/
     0 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/
  1148 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/STSCallbackHandler.class
  3456 Mon Jun 25 13:39:04 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/SampleSTS.class
   251 Mon Jun 25 13:39:02 CEST 2012 WEB-INF/jboss-web.xml
     0 Mon Jun 25 13:39:02 CEST 2012 WEB-INF/wsdl/
 13635 Mon Jun 25 13:39:02 CEST 2012 WEB-INF/wsdl/ws-trust-1.4-service.wsdl
  1054 Mon Jun 25 13:39:02 CEST 2012 WEB-INF/classes/stsKeystore.properties
  3978 Mon Jun 25 13:39:02 CEST 2012 WEB-INF/classes/stsstore.jks

The jboss-web.xml descriptor is used to set the security domain to be used for authentication (in this case the domain will need to be configured to allow alice / clarinet username/password couple):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.4//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">
<jboss-web>
   <security-domain>java:/jaas/JBossWS-trust-sts</security-domain>
</jboss-web>

... and the manifest contains the usual declaration of JBoss Application Server 7 module dependencies (Apache CXF internals are needed here to build up the STS configuration in SampleSTS constructor as shown above):

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.6.0_26-b03 (Sun Microsystems Inc.)
Dependencies: org.apache.ws.security,org.apache.cxf.impl
WS-MetadataExchange and interoperability

To achieve better interoperability, you might consider allowing the STS endpoint to reply to WS-MetadataExchange messages directed to the /mex URL sub-path (e.g. http://localhost:8080/jaxws-samples-wsse-policy-trust-sts/SecurityTokenService/mex). This can be done by tweaking the url-pattern for the underlying endpoint servlet, for instance by adding a web.xml  descriptor as follows to the deployment:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
   version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
   <servlet>
      <servlet-name>TestSecurityTokenService</servlet-name>
      <servlet-class>org.jboss.test.ws.jaxws.samples.wsse.policy.trust.SampleSTS</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>TestSecurityTokenService</servlet-name>
      <url-pattern>/SecurityTokenService/*</url-pattern>
   </servlet-mapping>
</web-app>

As a matter of fact, at the time of writing some webservices implementations (including Metro) assume the /mex URL as the default choice for directing WS-MetadataExchange requests to and use that to retrieve STS wsdl contracts.

PicketLink STS

PicketLink provides facilities for building up an alternative to the Apache CXF Security Token Service implementation.

Similarly to the previous implementation, the STS is served through a WebServiceProvider annotated POJO: 

package org.jboss.test.ws.jaxws.samples.wsse.policy.trust;

import javax.annotation.Resource;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceProvider;

import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.interceptor.InInterceptors;
import org.picketlink.identity.federation.core.wstrust.PicketLinkSTS;

@WebServiceProvider(serviceName = "PicketLinkSTS", portName = "PicketLinkSTSPort", targetNamespace = "urn:picketlink:identity-federation:sts", wsdlLocation = "WEB-INF/wsdl/PicketLinkSTS.wsdl")
@ServiceMode(value = Service.Mode.MESSAGE)
//be sure to have dependency on org.apache.cxf module when on AS7, otherwise Apache CXF annotations are ignored
@EndpointProperties(value = {
      @EndpointProperty(key = "ws-security.signature.username", value = "mystskey"),
      @EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"),
      @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.STSCallbackHandler"),
      @EndpointProperty(key = "ws-security.validate.token", value = "false") //to let the JAAS integration deal with validation through the interceptor below
})
@InInterceptors(interceptors = {"org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingPolicyInterceptor"})
public class PicketLinkSTService extends PicketLinkSTS {
   @Resource
   public void setWSC(WebServiceContext wctx) {
       this.context = wctx;
   }
}

The @WebServiceProvider annotation references the following WS-Policy enabled wsdl contract; please note the wsdl operations, messages and such must match the PicketLinkSTS implementation:

<?xml version="1.0"?>
<wsdl:definitions name="PicketLinkSTS" targetNamespace="urn:picketlink:identity-federation:sts"
	xmlns:tns="urn:picketlink:identity-federation:sts"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:wsap10="http://www.w3.org/2006/05/addressing/wsdl"
	xmlns:wsp="http://www.w3.org/ns/ws-policy"
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
	xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/">
  <wsdl:types>
    <xs:schema elementFormDefault="qualified" targetNamespace='http://docs.oasis-open.org/ws-sx/ws-trust/200512' xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:element name='RequestSecurityToken' type='wst:AbstractRequestSecurityTokenType' />
      <xs:element name='RequestSecurityTokenResponse' type='wst:AbstractRequestSecurityTokenType' />
      <xs:complexType name='AbstractRequestSecurityTokenType' >
        <xs:sequence>
          <xs:any namespace='##any' processContents='lax' minOccurs='0' maxOccurs='unbounded' />
        </xs:sequence>
        <xs:attribute name='Context' type='xs:anyURI' use='optional' />
        <xs:anyAttribute namespace='##other' processContents='lax' />
      </xs:complexType>
      <xs:element name='RequestSecurityTokenCollection' type='wst:RequestSecurityTokenCollectionType' />
      <xs:complexType name='RequestSecurityTokenCollectionType' >
        <xs:sequence>
          <xs:element name='RequestSecurityToken' type='wst:AbstractRequestSecurityTokenType' minOccurs='2' maxOccurs='unbounded'/>
        </xs:sequence>
      </xs:complexType>
      <xs:element name='RequestSecurityTokenResponseCollection' type='wst:RequestSecurityTokenResponseCollectionType' />
      <xs:complexType name='RequestSecurityTokenResponseCollectionType' >
        <xs:sequence>
          <xs:element ref='wst:RequestSecurityTokenResponse' minOccurs='1' maxOccurs='unbounded' />
        </xs:sequence>
        <xs:anyAttribute namespace='##other' processContents='lax' />
      </xs:complexType>
    </xs:schema>
  </wsdl:types>

  <wsdl:message name="RequestSecurityTokenMsg">
    <wsdl:part name="request" element="wst:RequestSecurityToken" />
  </wsdl:message>
  <wsdl:message name="RequestSecurityTokenResponseCollectionMsg">
    <wsdl:part name="responseCollection"
            element="wst:RequestSecurityTokenResponseCollection"/>
  </wsdl:message>

  <wsdl:portType name="SecureTokenService">
    <wsdl:operation name="IssueToken">
      <wsdl:input wsap10:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue" message="tns:RequestSecurityTokenMsg"/>
      <wsdl:output wsap10:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal" message="tns:RequestSecurityTokenResponseCollectionMsg"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="STSBinding" type="tns:SecureTokenService">
    <wsp:PolicyReference URI="#UT_policy" />
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="IssueToken">
      <soap12:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue" style="document"/>
      <wsdl:input>
        <wsp:PolicyReference URI="#Input_policy" />
        <soap12:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <wsp:PolicyReference URI="#Output_policy" />
        <soap12:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="PicketLinkSTS">
    <wsdl:port name="PicketLinkSTSPort" binding="tns:STSBinding">
      <soap12:address location="http://localhost:8080/picketlink-sts/PicketLinkSTS"/>
    </wsdl:port>
  </wsdl:service>

  <wsp:Policy wsu:Id="UT_policy">
      <wsp:ExactlyOne>
         <wsp:All>
            <wsap10:UsingAddressing/>
            <sp:SymmetricBinding
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:Policy>
                  <sp:ProtectionToken>
                     <wsp:Policy>
                        <sp:X509Token
                           sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
                           <wsp:Policy>
                              <sp:RequireDerivedKeys />
                              <sp:RequireThumbprintReference />
                              <sp:WssX509V3Token10 />
                           </wsp:Policy>
                        </sp:X509Token>
                     </wsp:Policy>
                  </sp:ProtectionToken>
                  <sp:AlgorithmSuite>
                     <wsp:Policy>
                        <sp:Basic256 />
                     </wsp:Policy>
                  </sp:AlgorithmSuite>
                  <sp:Layout>
                     <wsp:Policy>
                        <sp:Lax />
                     </wsp:Policy>
                  </sp:Layout>
                  <sp:IncludeTimestamp />
                  <sp:EncryptSignature />
                  <sp:OnlySignEntireHeadersAndBody />
               </wsp:Policy>
            </sp:SymmetricBinding>
            <sp:SignedSupportingTokens
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:Policy>
                  <sp:UsernameToken
                     sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                     <wsp:Policy>
                        <sp:WssUsernameToken10 />
                     </wsp:Policy>
                  </sp:UsernameToken>
               </wsp:Policy>
            </sp:SignedSupportingTokens>
            <sp:Wss11
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:Policy>
                  <sp:MustSupportRefKeyIdentifier />
                  <sp:MustSupportRefIssuerSerial />
                  <sp:MustSupportRefThumbprint />
                  <sp:MustSupportRefEncryptedKey />
               </wsp:Policy>
            </sp:Wss11>
            <sp:Trust13
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:Policy>
                  <sp:MustSupportIssuedTokens />
                  <sp:RequireClientEntropy />
                  <sp:RequireServerEntropy />
               </wsp:Policy>
            </sp:Trust13>
         </wsp:All>
      </wsp:ExactlyOne>
   </wsp:Policy>

   <wsp:Policy wsu:Id="Input_policy">
      <wsp:ExactlyOne>
         <wsp:All>
            <sp:SignedParts
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <sp:Body />
               <sp:Header Name="To"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="From"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="FaultTo"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="ReplyTo"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="MessageID"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="RelatesTo"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="Action"
                  Namespace="http://www.w3.org/2005/08/addressing" />
            </sp:SignedParts>
            <sp:EncryptedParts
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <sp:Body />
            </sp:EncryptedParts>
         </wsp:All>
      </wsp:ExactlyOne>
   </wsp:Policy>

   <wsp:Policy wsu:Id="Output_policy">
      <wsp:ExactlyOne>
         <wsp:All>
            <sp:SignedParts
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <sp:Body />
               <sp:Header Name="To"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="From"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="FaultTo"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="ReplyTo"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="MessageID"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="RelatesTo"
                  Namespace="http://www.w3.org/2005/08/addressing" />
               <sp:Header Name="Action"
                  Namespace="http://www.w3.org/2005/08/addressing" />
            </sp:SignedParts>
            <sp:EncryptedParts
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <sp:Body />
            </sp:EncryptedParts>
         </wsp:All>
      </wsp:ExactlyOne>
   </wsp:Policy>

</wsdl:definitions>

Differently from the Apache CXF STS example described above, the PicketLink based STS gets its configuration from a picketlink-sts.xml descriptor which must be added in WEB-INF into the deployment; please refer to the PicketLink documentation for further information:

<PicketLinkSTS xmlns="urn:picketlink:identity-federation:config:1.0"
	STSName="PicketLinkSTS" TokenTimeout="7200" EncryptToken="false">
	<KeyProvider ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">
		<Auth Key="KeyStoreURL" Value="stsstore.jks"/>
  		<Auth Key="KeyStorePass" Value="stsspass"/>
  		<Auth Key="SigningKeyAlias" Value="mystskey"/>
   		<Auth Key="SigningKeyPass" Value="stskpass"/>
  		<ValidatingAlias Key="http://localhost:8080/jaxws-samples-wsse-policy-trust/SecurityService" Value="myservicekey"/>
	</KeyProvider>
	<TokenProviders>
            <TokenProvider ProviderClass="org.picketlink.identity.federation.core.wstrust.plugins.saml.SAML11TokenProvider"
                TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"
	        TokenElement="Assertion"
	        TokenElementNS="urn:oasis:names:tc:SAML:1.0:assertion"/>
            <TokenProvider ProviderClass="org.picketlink.identity.federation.core.wstrust.plugins.saml.SAML20TokenProvider"
                TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
	        TokenElement="Assertion"
	        TokenElementNS="urn:oasis:names:tc:SAML:2.0:assertion"/>
	</TokenProviders>
</PicketLinkSTS>

Finally, the PicketLink alternative approach of course requires different JBoss AS module dependencies to be declared in the MANIFEST.MF:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.6.0_26-b03 (Sun Microsystems Inc.)
Dependencies: org.apache.ws.security,org.apache.cxf,org.picketlink

Here is how the PicketLink STS endpoint is packaged:

alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-tests/target/test-libs/jaxws-samples-wsse-policy-trustPicketLink-sts.war
     0 Mon Sep 03 17:38:38 CEST 2012 META-INF/
   174 Mon Sep 03 17:38:36 CEST 2012 META-INF/MANIFEST.MF
     0 Mon Sep 03 17:38:38 CEST 2012 WEB-INF/
     0 Mon Sep 03 17:38:38 CEST 2012 WEB-INF/classes/
     0 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/org/
     0 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/org/jboss/
     0 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/org/jboss/test/
     0 Mon Sep 03 16:35:52 CEST 2012 WEB-INF/classes/org/jboss/test/ws/
     0 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/
     0 Mon Sep 03 16:35:52 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/
     0 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/
     0 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/
     0 Mon Sep 03 16:35:52 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/
  1686 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/PicketLinkSTService.class
  1148 Mon Sep 03 16:35:52 CEST 2012 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/trust/STSCallbackHandler.class
   251 Mon Sep 03 17:38:34 CEST 2012 WEB-INF/jboss-web.xml
     0 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/wsdl/
  9070 Mon Sep 03 17:38:34 CEST 2012 WEB-INF/wsdl/PicketLinkSTS.wsdl
  1267 Mon Sep 03 17:38:34 CEST 2012 WEB-INF/classes/picketlink-sts.xml
  1054 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/stsKeystore.properties
  3978 Mon Sep 03 16:35:50 CEST 2012 WEB-INF/classes/stsstore.jks
JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-13 13:28:29 UTC, last content change 2012-11-22 16:23:13 UTC.