Wednesday, May 11, 2011

WS-Trust sample in Talend Service Factory 2.4.0

In this post I will walk through the WS-Trust sample that ships with Talend Service Factory 2.4.0.

1) Download the artifacts

Go here and download Talend Service Factory 2.4.0. When this is done, go here and download the Talend Service Factory 2.4.0 examples (registration required). Extract the examples into the Talend Service Factory (TSF) install directory ($TSF_HOME).

2) Build and run the sample

Go to $TSF_HOME/examples/jaxws-ws-trust and start with the README.txt. Run "mvn eclipse:eclipse" to generate eclipse projects, and "mvn install" to build and install the various modules.

Both the CXF service provider and Metro STS used in this sample are deployed in Tomcat. To see how to configure Maven to install/uninstall these artifacts in Tomcat follow the instructions here. Finally, you need to make sure that the path to the keystores is correct for the Metro STS - follow the instructions in the README.txt for this.

Start Tomcat, and from the sts-war folder run "mvn install tomcat:deploy". Run the same command from the service-war folder to deploy the CXF service provider in Tomcat.

Finally to run the test, go to the client folder and run "mvn install exec:exec". You also have the option of running the client in Karaf (follow the instructions in the README.txt).

3) The sample

Three service invocations take place as part of this sample. For simplicity, I'll just concentrate on the third one, which shows how a SAML2 Assertion is used in a WS-Trust scenario.

3.1) The Service Provider

We'll start with the Service Provider, as the client will use the security policies defined in the WSDL of the Service Provider to access the STS. The Service is spring-loaded via the following configuration:

<jaxws:endpoint id="doubleitsaml2"
      implementor="service.DoubleItPortTypeImpl"
      address="/doubleitsaml2"
      endpointName="DoubleItPortSAML2"
      wsdlLocation="WEB-INF/wsdl/DoubleIt.wsdl">
       
      <jaxws:properties>
         <entry key="ws-security.callback-handler" value="..."/>
         <entry key="ws-security.signature.properties" value="..."/>
         <entry key="ws-security.is-bsp-compliant" value="false"/>
      </jaxws:properties>
</jaxws:endpoint>

Three properties are required for the endpoint. The CallbackHandler implementation is required to provider the password used to access the private key in the Keystore, which is in turn configured in the "ws-security.signature.properties" file. The "ws-security.is-bsp-compliant" configuration turns off Basic Security Profile 1.1 compliance enforcement. This is required as the Metro STS generates a non BSP-compliant SAML Assertion (try removing this line, redeploying the service provider in tomcat and see what happens when the test is re-run).

The WSDL (DoubleIt.wsdl) contains the security policies for the service provider. It requires that the input and output SOAP Body elements must be signed and encrypted, and that all of the addressing headers must be signed in both directions. It also contains the following policy snippet:

<sp:SymmetricBinding>
  <wsp:Policy>
    <sp:ProtectionToken>
      <wsp:Policy>
        <sp:IssuedToken sp:IncludeToken="...AlwaysToRecipient">
          <sp:RequestSecurityTokenTemplate>
            <t:TokenType>...#SAMLV2.0</t:TokenType>
            <t:KeyType>.../SymmetricKey</t:KeyType>
            <t:KeySize>256</t:KeySize>
          </sp:RequestSecurityTokenTemplate>
          ...
          <sp:Issuer>
            <wsaw:Address>http://.../DoubleItSTSServiceUT</wsaw:Address>
            <wsaw:Metadata>
              ...
            </wsaw:Metadata>
          </sp:Issuer>
        </sp:IssuedToken>
      </wsp:Policy>
    </sp:ProtectionToken>
    ...
  </wsp:Policy>
</sp:SymmetricBinding>

This SecurityPolicy snippet defines that the communication with the service provider is secured via the SymmetricBinding, i.e. that it is secured via a secret key. The ProtectionToken policy describes how the secret key in turn is conveyed to the service provider in a secure way. In this example, it defines an IssuedToken policy, which is always sent to the recipient (service provider). Once the client sees this policy, it will know that it must contact a Security Token Service (STS) via the WS-Trust protocol to obtain a (issued) token that will convey the symmetric key to the service provider.

The IssuedToken policy has a RequestSecurityTokenTemplate policy, which the client will copy verbatim when contacting the STS for a security token. It describes the token type that is required (a SAML2 Assertion), the KeyType conveyed in the Assertion (Symmetric Key), and the size of the symmetric key (256 bits). It also contains an Issuer policy which describes how the STS may be contacted via a wsa EndpointReferenceType.

3.2) The Security Token Service (STS)

The STS used in this sample is the Metro STS. The port is secured with the following security policy binding:

<sp:AsymmetricBinding>
  <wsp:Policy>
     <sp:InitiatorToken>
       <wsp:Policy>
         <sp:X509Token sp:IncludeToken=".../AlwaysToRecipient">
           <wsp:Policy>
             <sp:WssX509V3Token10 />
           </wsp:Policy>
         </sp:X509Token>
       </wsp:Policy>
       </sp:InitiatorToken>
     <sp:RecipientToken>
       <wsp:Policy>
         <sp:X509Token sp:IncludeToken=".../Never">
           <wsp:Policy>
             <sp:WssX509V3Token10 />
             <sp:RequireIssuerSerialReference />
           </wsp:Policy>
         </sp:X509Token>
       </wsp:Policy>
     </sp:RecipientToken>
      ...
  </wsp:Policy>
</sp:AsymmetricBinding>

This Security Policy defines that the Asymmetric Binding is to be used in communication with the STS, i.e. that the client must sign the request using its private key, and include the corresponding X509 Certificate in the security header of the request, and encrypt the request using the public key of the STS. Authentication is performed on the basis of trust verification of the client's certificate, as the client illustrates proof-of-possession by signing some part of the request.

The WSDL of the STS also contains a "STSConfiguration" policy fragment, which defines that the issued key is encrypted, and lists the service provider endpoints, including the corresponding public keys.

3.3) The client

When the client wants to invoke on the service provider, it parses the security policy described above in the WSDL, and sees that it must first obtain an IssuedToken from a STS before it can construct the service request. The client is configured in spring as follows:

<jaxws:client name="{...}DoubleItPortSAML2" createdFromAPI="true">
  <jaxws:properties>
    <entry key="ws-security.sts.client">
      <bean class="org.apache.cxf.ws.security.trust.STSClient">
        <constructor-arg ref="cxf"/>
        <property name="wsdlLocation" value="DoubleItSTSService.wsdl"/>
        <property name="serviceName" value="{...}DoubleItSTSService"/>
        <property name="endpointName" value="{...}IDoubleItSTS..Port"/>
        <property name="properties">
          <map>
            <entry key="ws-security.signature.username" value="..."/>
            <entry key="ws-security.callback-handler" value="..."/>
            <entry key="ws-security.signature.properties" value="..."/>
            <entry key="ws-security.encryption.properties" value="..."/>
            <entry key="ws-security.encryption.username" value="..."/>
          </map>
        </property>
      </bean>           
    </entry>
  </jaxws:properties>
</jaxws:client>

The STSClient bean contains the configuration required to contact the STS. The client parses the WSDL of the STS, and uses the supplied configuration parameters to construct a request that is secured by the Asymmetric Binding, as discussed above. This request is done via the WS-Trust protocol.

3.4) The STS request

In the SOAP Body of the request is the following information (decrypted):

<wst:RequestSecurityToken>
  <wst:SecondaryParameters>
    <t:TokenType>...#SAMLV2.0</t:TokenType>
    <t:KeyType>.../SymmetricKey</t:KeyType>
    <t:KeySize>256</t:KeySize>
  </wst:SecondaryParameters>
  <wst:RequestType>.../Issue</wst:RequestType>
  <wsp:AppliesTo>
    <wsa:EndpointReference>
    ...
    </wsa:EndpointReference>
  </wsp:AppliesTo>
  ...
  <wst:Entropy>
    <wst:BinarySecret Type=".../Nonce">...</wst:BinarySecret>
  </wst:Entropy>
  <wst:ComputedKeyAlgorithm>.../CK/PSHA1</wst:ComputedKeyAlgorithm></wst:RequestSecurityToken>

The SecondaryParameters element is copied verbatim from the RequestSecurityTokenTemplate defined in the policy of the service provider. The RequestType element defines an "Issue" URI. AppliesTo refers to the address of the service provider. Entropy contains some client-generated entropy (which the STS will combine with its own Entropy to form a symmetric key), using the ComputedKeyAlgorithm URI.

3.5) The STS response

The response from the STS contains the following (decrypted) SOAP Body. It contains the token type of the requested token, the token itself, different ways of referring to the requested token, some entropy that the client can use to recreate the symmetric key, the lifetime of the requested token, etc:

<trust:RequestSecurityTokenResponseCollection>
  <trust:RequestSecurityTokenResponse>
    <trust:TokenType>...#SAMLV2.0</trust:TokenType>
    <trust:RequestedSecurityToken>
      <saml2:Assertion>
      ...
      </saml2:Assertion>
    </trust:RequestedSecurityToken>
    <trust:RequestedAttachedReference>...</trust:RequestedAttachedReference>
    <trust:RequestedUnattachedReference>
    ...
    </trust:RequestedUnattachedReference>
    <wsp:AppliesTo...>...</wsp:AppliesTo>
    <trust:RequestedProofToken>
        <trust:ComputedKey>.../CK/PSHA1</trust:ComputedKey>
    </trust:RequestedProofToken>
    <trust:Entropy>
      <trust:BinarySecret Type=".../Nonce">...</trust:BinarySecret>
    </trust:Entropy>
    <trust:Lifetime>
      <wsu:Created...>...</wsu:Created>
      <wsu:Expires...>...</wsu:Expires>
    </trust:Lifetime>
    <trust:KeySize>256</trust:KeySize>
  </trust:RequestSecurityTokenResponse>
</trust:RequestSecurityTokenResponseCollection>

The requested security token that is returned above is reproduced here. Note that the SubjectConfirmation Method is "holder-of-key", meaning that the client must illustrate proof of possession of the key contained in the EncryptedKey element of the Assertion. The EncryptedKey element is encrypted using the service provider's public key.

<saml2:Assertion ID="..." IssueInstant="..." Version="2.0">
  <saml2:Issuer>DoubleItSTSIssuer</saml2:Issuer>
  <ds:Signature>...</ds:Signature>
  <saml2:Subject>
    <saml2:NameID NameQualifier="...">...</saml2:NameID>
    <saml2:SubjectConfirmation Method="...:cm:holder-of-key">
      <saml2:SubjectConfirmationData>
      ...
      <xenc:EncryptedKey
      ...
      </xenc:EncryptedKey>
      ...
      </saml2:SubjectConfirmationData>
   </saml2:SubjectConfirmation>
 </saml2:Subject>
 <saml2:Conditions NotBefore="..." NotOnOrAfter="..."></saml2:Conditions>
 <saml2:AttributeStatement>
   <saml2:Attribute...>
     <saml2:AttributeValue>authenticated</saml2:AttributeValue>
   </saml2:Attribute>
 </saml2:AttributeStatement>
</saml2:Assertion>

3.5) The Service Provider request

Once the client receives the Issued Token from the STS, it recreates the symmetric key needed to communicate with the service provider, by combining the entropy received from the STS with its own entropy to form the session key. The (decrypted) SAML2 Assertion is inserted into the security header "as is". The symmetric key is referenced in the request via the following structure:

<ds:KeyInfo Id="...">
  <wsse:SecurityTokenReference  wsse11:TokenType="...#SAMLV2.0" >
    <wsse:KeyIdentifier ValueType="...#SAMLID">...</wsse:KeyIdentifier>
  </wsse:SecurityTokenReference>
</ds:KeyInfo>


The Service Provider verifies the signature of the STS on the SAML Assertion, and then decrypts the EncryptedKey fragment using its private key, to obtain the symmetric key used to decrypt/verify the client request. As the confirmation method is "holder-of-key", the Service Provider ensures that the same key was used to sign some portion of the request, thus proving that the client is in possession of the key.

3 comments:

  1. Very interesting example.

    I ran into the following security exception when running the client:

    org.apache.xml.security.encryption.XMLEncryptionException: Illegal key size or default parameters

    Since the policy key size is 256, the default policy files that come with the JDK are limited to 128. It requires patching JDK with Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files from Oracle (http://www.oracle.com/technetwork/java/javase/downloads/index.html). Copying two jar files from the JCE distribution (local_policy.jar and US_export_policy.jar) to $JAVA_HOME/jre/lib/security fixed the problem for me.

    -Arul

    ReplyDelete
  2. Hello,

    First of all, thanks a lot for this article! I am in the process of evaluating WS-Trust and SAML for security purposes and this post is very informative.

    I have attempted to reproduce this scenario using JBOSSWS-CXF and Picketlink as a STS. After several attempts I managed to retrieve a SAML assertion from the STS, and send the request to the service.
    However, the server fails with a "{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}IssuedToken: Assertion fails holder-of-key requirements" error.
    I think this is correct because the service request does not use the secret key for any holder-of-key task.

    I have also found your bug report at CXF, which looks potentially related:
    https://issues.apache.org/jira/browse/CXF-3524

    Is that also my problem, or there might be some additional part that I am doing wrong? I haven't properly tested the provided Talend examples, but in this context I am required to use only the previously mentioned frameworks and servers.

    ReplyDelete
  3. Hi,

    CXF-3524 only apples to when derived keys are used with the symmetric binding. If this corresponds to your use-case, then it sounds like this could be the problem.

    If not, then it could be the case that the client is not proving to the service provider that it knows the key referred to in the assertion. It must do this by either using 2-way TLS for the TransportBinding case, or else signing some portion of the message using the same key.

    If none of the above scenarios sound like the cause, then it might be a bug, so I would suggest creating a JIRA in CXF, attaching the requests, security policy of the endpoint, etc.

    Colm.

    ReplyDelete