Friday, April 29, 2011

Talend Service Factory 2.4.0 released

Talend Service Factory 2.4.0 has been released. It's based on Apache CXF 2.4.0, and so contains all of the security features in WSS4J 1.6.0, as well as the extensive support for SAML Assertions in CXF 2.4.0, that I have been blogging about for the last while.

In addition to this, some examples are available for download, which illustrate how to get these (security) features working in an OSGi container. A lot of work has gone into making sure that security libraries such as Apache Santuario, Apache WSS4J and Opensaml can be used in an OSGi environment, so I recommend checking the examples out to see how it can be done.

See Glen's blog for more information on the security examples. Also, see Sergey's blog for a discussion on some other examples based around transforming XML.

Thursday, April 21, 2011

SAML support in CXF 2.4.0

The recent Apache CXF 2.4.0 release contains support for creating, securing, processing and validating SAML Assertions according to the WS-Security 1.1 SAML Token Profile. As there is no documentation available as yet on this new feature, in this blog post I will go through a SAML system test in CXF 2.4.0 in detail.

1) Running the Test

To run the SAML system test you can do the following:

svn co https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.0/systests/ws-security
cd ws-security
mvn compile
mvn test -Dtest=SamlTokenTest

2) The Client

2.1) The Client code

You can view the source of the tests here. There are a number of tests involving creating SAML 1.1 and 2.0 assertions, and sending them to a service provider over various security bindings (Transport/Symmetric/Asymmetric). To simplify things, we will focus on the fourth test named "testSaml2OverAsymmetric". Minus some negative tests, the basic test client invocation code is as simple as:

SpringBusFactory bf = new SpringBusFactory();
URL busFile = SamlTokenTest.class.getResource("client/client.xml");
Bus bus = bf.createBus(busFile.toString());
SpringBusFactory.setDefaultBus(bus);
SpringBusFactory.setThreadDefaultBus(bus);

DoubleItService service = new DoubleItService();
DoubleItPortType saml2Port = service.getDoubleItSaml2AsymmetricPort();
((BindingProvider)saml2Port).getRequestContext().put(
"ws-security.saml-callback-handler", new SamlCallbackHandler()
);
BigInteger result = saml2Port.doubleIt(BigInteger.valueOf(25));
assert result.equals(BigInteger.valueOf(50));

2.2) The WSDL

The service is described in the WSDL here. Take a look at the WS-SecurityPolicy called "DoubleItSaml2AsymmetricPolicy", which defines the security requirements for the "DoubleItSaml2AsymmetricPort". It defines an Asymmetric Binding, where the InitiatorToken (which defines the credential used to sign the request) is always sent to the recipient, and the RecipientToken (which defines the credential used to encrypt the request) is never sent to the recipient. Both Initiator and Recipient tokens are defined as X509 tokens. The input and output policies in the WSDL enforce that the SOAP Body must be signed using the Initiator credential, and encrypted using the Recipient credential.

In addition to specifying an asymmetric binding, the policy also defines a SignedSupportingToken, which contains a SAML (2.0) Token which is always sent to the recipient. In order to successfully invoke on the service, the client must include a SAML 2.0 token in the security header of the request. This policy looks like:

<sp:SignedSupportingTokens>
    <wsp:Policy>
        <sp:SamlToken sp:IncludeToken="...AlwaysToRecipient">
            <wsp:Policy>
                <sp:WssSamlV20Token11/>
            </wsp:Policy>
        </sp:SamlToken>
    </wsp:Policy>
</sp:SignedSupportingTokens>

2.3) The Client configuration

The client.xml referenced in the code block above contains a jaxws:client configuration for the DoubleItSaml2AsymmetricPort. It sets the following relevant jaxws:properties:
  1. ws-security.encryption.properties - The Crypto properties file which describes where to find the service provider's public key.
  2. ws-security.encryption.username -  The alias to use to obtain the service provider's public key from the keystore reference in the Crypto properties file above.
  3. ws-security.callback-handler - A CallbackHandler object which is expected to supply the password used to access the private key for signature creation, or decryption.
  4. ws-security.signature.properties - The Crypto properties file which describes where to find the client's public/private key.
  5. ws-security.signature.username - The alias to use to obtain the client's private key from the keystore reference in the Crypto properties file above.
2.3) Creating a SAML token

CXF 2.4.0 defines a new jaxws:property ("ws-security-saml-callback-handler") which specifies a CallbackHandler instance used to create SAML Assertions. This object is added to the outbound request context above dynamically, however it could also have been configured in the spring bean along with the other ws-security parameters. The CallbackHandler object used in this test can be seen here. The CallbackHandler implementation is expected to obtain a SAMLCallback object, and to set the appropriate values on this object, e.g. SAML version, Subject, issuer, Authentication/Authorization/Attribute Statements, etc. In the example provided in this test, it creates a SAML 2.0 assertion (by default), sets a mock issuer, subject and attribute statement, and sets a subject confirmation method of sender-vouches. Some code in WSS4J then constructs a SAML Assertion by processing this SAMLCallback object. It's easy to construct a SAML Assertion in this way, as the following (edited) code shows:

SAMLCallback callback = (SAMLCallback) callbacks[i];
callback.setSamlVersion(SAMLVersion.VERSION_20);
callback.setIssuer("sts");
String subjectName = "uid=sts-client,o=mock-sts.com";
String subjectQualifier = "www.mock-sts.com";

SubjectBean subjectBean = new SubjectBean(subjectName, subjectQualifier, SAML2Constants.CONF_SENDER_VOUCHES);
callback.setSubject(subjectBean);

AttributeStatementBean attrBean = new AttributeStatementBean();
attrBean.setSubject(subjectBean);
AttributeBean attributeBean = new AttributeBean();
attributeBean.setSimpleName("subject-role");
attributeBean.setAttributeValues(Collections.singletonList("system-user"));
attrBean.setSamlAttributes(Collections.singletonList(attributeBean));
callback.setAttributeStatementData(Collections.singletonList(attrBean));

2.4) The service request

The service request has a security header that contains the following elements:
  1. A BinarySecurityToken which consists of the X509Certificate of the client.
  2. A Timestamp.
  3. An EncryptedKey which consists of a symmetric key encrypted with the public key of the service provider, which is used to encrypt the SOAP Body.
  4. A SAML2 Assertion.
  5. A SecurityTokenReference to the SAML Assertion.
  6. A signature which signs the Timestamp, the SAML Assertion (via the SecurityTokenReference) and the (decrypted) SOAP body. The signing credential is the BinarySecurityToken element described above.
The SAML 2.0 assertion looks like (edited):

<saml2:Assertion ... Version="2.0">
    <saml2:Issuer>sts</saml2:Issuer>
    <saml2:Subject>
      <saml2:NameID ...>uid=sts-client,o=mocksts.com</saml2:NameID>
     <saml2:SubjectConfirmation Method="...:sender-vouches">
     </saml2:SubjectConfirmation>
   </saml2:Subject>
   <saml2:Conditions NotBefore="..." NotOnOrAfter="..."/>
   <saml2:AttributeStatement>
     <saml2:Attribute FriendlyName="subject-role" ...>
       <saml2:AttributeValue...>system-user</saml2:AttributeValue>
     </saml2:Attribute>
   </saml2:AttributeStatement>
</saml2:Assertion>

One thing to note is that as the SAML Assertion has a subject confirmation method of "sender-vouches", the client will automatically add the quality-of-service requirement that the signature which covers the SOAP Body will also cover the SAML Assertion.

3) The Server

3.1) The Server code

The SEI implementation is here, and the Server code itself is here.  The configuration is entirely driven through the WSDL and spring configuration, and so the code is as trivial as (edited):

URL busFile = Server.class.getResource("server.xml");
Bus busLocal = new SpringBusFactory().createBus(busFile);
BusFactory.setDefaultBus(busLocal);
setBus(busLocal);
new Server();

3.2) The Server configuration

The server.xml configuration file referenced above can be seen here. The jaxws:Endpoint configuration for this port should be self-explanatory (edited):

<jaxws:endpoint
       id="Saml2TokenOverAsymmetric"
       address="http://localhost:9001/DoubleItSaml2Asymmetric"
       serviceName="s:DoubleItService"
       endpointName="s:DoubleItSaml2AsymmetricPort"
       xmlns:s="http://WSSec/saml"
       implementor="org.apache.cxf.systest.ws.saml.server.DoubleItImpl"
       wsdlLocation="wsdl_systest_wssec/saml/DoubleItSaml.wsdl">
        
       <jaxws:properties>
           <entry key="ws-security.username" value="bob"/>
           <entry key="ws-security.callback-handler"
                  value="....KeystorePasswordCallback"/>
           <entry key="ws-security.signature.properties"
                  value="...bob.properties"/>
           <entry key="ws-security.encryption.properties"
                  value="...alice.properties"/>
           <entry key="ws-security.encryption.username" value="alice"/>
       </jaxws:properties>
 </jaxws:endpoint>

The server will process the request as per the security policy in the WSDL, checking that there is a signature in the security header, that covers the SOAP Body and SAML Assertion, that the SOAP Body is Encrypted, that a Timestamp is present and valid, and that the SAML Assertion is present, and is the correct version, etc. Authentication is done on the basis of trust verification of the client's X509Certificate, which was used to verify the signature element.

The SAML Assertion is ignored beyond this point for this system test. It is saved in the security processing results, so that a custom interceptor can do some additional validation or processing on it. In a future blog post, I will describe how to validate the Assertion that has been received in some custom manner.

Wednesday, April 20, 2011

CXF 2.4.0 released

Apache CXF 2.4.0 has been released. CXF 2.4.0 contains a number of new and improved features in the security space. From the release statement:
WS-Security improvements including support for SAML2 tokens, improved 
validation of security tokens, better performance, increased WS-I Basic 
Security Profile compliance, and much more.
If those new features seem familiar, it's because most of the new functionality is driven by WSS4J 1.6.0, which I've blogged extensively about over the last few months. Probably the most significant new security functionality in CXF 2.4.0 is greatly enhanced support for SAML Assertions. CXF 2.4.0 supports the ability to create, secure, process and validate SAML Assertions in accordance with the WS-Security 1.1 SAML Token Profile. I intend to blog in more detail how to use these new features in CXF 2.4.0 over the next while.

See Dan Kulp's blog for more in-depth thoughts on the new release.

Friday, April 15, 2011

WSS4J 1.6.0 released

WSS4J 1.6.0 has been released:
The Apache Web Services team is pleased to announce the release of WSS4J 1.6.0. WSS4J 1.6.0 features support for SAML2 assertions, JSR-105 support, better spec compliance, performance work, support for trust-stores and a lot more besides. It is not API-compatible with the 1.5.x series of releases.
For more information on the new features and changes in WSS4J 1.6.0 go to:
http://ws.apache.org/wss4j/wss4j16.html
To download WSS4J 1.6.0 go to:
http://ws.apache.org/wss4j/download.html
-- The Apache Web Services Team

Tuesday, April 5, 2011

[WSS4J 1.6] Introducing Validators

WSS4J 1.6 introduces the concept of a Validator, for validating credentials that have been processed by a Processor instance. This task was covered by the JIRA WSS-266.

An inbound security header is processed by WSS4J by iterating through each child element of the header, and by calling the appropriate Processor implementation to deal with each element. In WSS4J 1.5.x, some processors perform validation on the received token (e.g. UsernameTokens), whereas others store the processing results for later verification by third-party WS-Handler implementations (e.g. Timestamp verification, Certificate trust verification). There are some problems with this approach:
  • It is not consistent, some processors perform validation, others do not.
  • There is a potential security hole, in that it is assumed third-party code will know to validate the credentials that the WSS4J processors do not validate.
  • WSS4J will continue to process the rest of the security header even if the Timestamp is invalid, or the certificate non-trusted, which could lead to denial-of-service attacks.
  • There is no separation of concerns between processing the token and validating the token. If you want to change how the token is validated, you must replace the processor instance.
WSS4J 1.6 has moved Timestamp verification and certificate trust validation back into the processing of the security header, thus solving the first three points above. The fourth point is met by the new concept of Validators, as well as some changes to the way Processors and CallbackHandler implementations are used in WSS4J 1.6.

In WSS4J 1.5.x, CallbackHandler implementations are used in different ways by different processors, sometimes they are expected to verify a password (as for processing UsernameTokens), and other times they are expected to supply a password (as for decryption). In WSS4J 1.6, CallbackHandler implementations are only expected to supply a password (if it exists) to the processors. The Processor implementations do not perform any validation of the security token, instead they package up the processed token, along with any (password) information extracted from the CallbackHandler, and hand it off to a Validator implementation for Validation.

The Processor implementations get the specific Validator implementation to use via the RequestData parameter, which in turn asks a WSSConfig object for the Validator implementation. If the Validator is null, then no Validation is performed on the received token. The Processor then stores the received token as normal. WSS4J 1.6 comes with several default Validators, which are:
  • NoOpValidator: Does no processing of the credential
  • TimestampValidator: Validates a Timestamp
  • UsernameTokenValidator: Validates a UsernameToken
  • SignatureTrustValidator: Verifies trust in a signature
  • SamlAssertionValidator: Checks some HOK requirements on a SAML Assertion, and verifies trust on the (enveloped) signature.
There are some additional WSSecurityEngineResult constants that pertain to the Validator implementations:
  • TAG_VALIDATED_TOKEN: Indicates that the token corresponding to this result has been validated by a Validator implementation. Some of the processors do not have a default Validator implementation.
  • TAG_TRANSFORMED_TOKEN: A Validator implementation may transform a credential (into a SAML Assertion) as a result of Validation. This tag holds a reference to an AssertionWrapper instance, that represents a transformed version of the validated credential.
To validate an inbound UsernameToken in some custom way, simply associate the NoOpValidator with the UsernameToken QName in the WSSConfig of the RequestData object used to supply context information to the processors. After WSS4J has finished processing the security header, then extract the WSSecurityEngineResult instance corresponding to the WSConstants.UT action, and perform some custom validation on the token.

An example of how to add a custom Validator implementation is the STSTokenValidator in CXF 2.4.0. The STSTokenValidator tries to validate a received SAML Assertion locally, and if that fails, it dispatches it to a Security Token Service (STS) via the WS-Trust interface for validation. It also supports validating a UsernameToken and BinarySecurityToken in the same manner. The SecurityConstants class defines some configuration tags for specifying a custom validator for inbound SAML1, SAML2, UsernameToken, BinarySecurityToken, Signature and Timestamps. The STSTokenValidator can be configured by associating it with the appropriate configuration tag.