Sending self-issued SAML holder-of-key tokens with asymmetric key using WSS APIs

You can create self-issued SAML tokens with the holder-of-key subject confirmation method and then use the Java™ API for XML-Based Web Services (JAX-WS) programming model and Web Services Security APIs (WSS APIs) to send these tokens with web services request messages.

Before you begin

This task assumes that you are familiar with the JAX-WS programming model, the WSS API interfaces, SAML concepts, and the use of policy sets to configure and administer web services settings. Complete the following actions before you begin this task:
  • Read about sending self-issued SAML bearer tokens by using WSS APIs.
  • Read about sending self-issued SAML sender-vouches tokens by using WSS APIs with message level protection.

About this task

This task focuses on using the asymmetric key that is identified by SAML security tokens to generate a digital signature of selected SOAP message elements in order to satisfy holder-of-key subject confirmation method security requirements. The X.509 certificate of the sender is embedded in the SAML security token. The sender signs selected parts of request message elements by using its corresponding private key and encrypts the request message by using the public key of the recipient. The recipient signs the selected elements of the response message by using the private key of the recipient, and encrypts selected elements of the response message by using the public key of the sender in SAML security tokens. The Web services security policy attached to the web services provider is provided for your reference.

Procedure

  1. Create a SAML security token that contains the holder-of-key subject confirmation method; for example:
    WSSFactory factory = WSSFactory.getInstance();
    // Initialize WSSGenerationContext
    com.ibm.websphere.wssecurity.wssapi.WSSGenerationContext gencont = factory.newWSSGenerationContext();
    // Initialize SAML issuer configuration via custom properties	
    HashMap<Object, Object> customProps = new HashMap<Object,Object>();
    customProps.put(SamlConstants.ISSUER_URI_PROP, "example.com");
    customProps.put(SamlConstants.TTL_PROP, "3600000");
    customProps.put(SamlConstants.KS_PATH_PROP, "keystores/saml-provider.jceks");
    customProps.put(SamlConstants.KS_TYPE_PROP, "JCEKS");
    customProps.put(SamlConstants.KS_PW_PROP, "{xor}MiY2LCwqOi0sKzAtOi8+LCw=");
    customProps.put(SamlConstants.KEY_ALIAS_PROP, "samlissuer");
    customProps.put(SamlConstants.KEY_NAME_PROP, "CN=SAMLIssuer, O=EXAMPLE");
    customProps.put(SamlConstants.KEY_PW_PROP, "{xor}MiY2LCwqOi00OiYvPiws");
    customProps.put(SamlConstants.TS_PATH_PROP, "keystores/saml-provider.jceks");
    customProps.put(SamlConstants.TS_TYPE_PROP, "JCEKS");
    customProps.put(SamlConstants.TS_PW_PROP, "{xor}MiY2LCwqOi0sKzAtOi8+LCw=");  
    gencont.add(customProps); //Add custom properties
    HashMap<Object, Object> map = new HashMap<Object, Object>();
    map.put(SamlConstants.CONFIRMATION_METHOD, "holder-of-key");
    map.put(SamlConstants.Token_REQUEST, "issue");
    map.put(SamlConstants.TOKEN_TYPE, WSSConstants.SAML.SAML20_VALUE_TYPE);
    map.put(SamlConstants.SAML_NAME_IDENTIFIER, "Alice");
    map.put(SamlConstants.SIGNATURE_REQUIRED, "true");
    map.put(SamlConstants.KEY_TYPE, 
            "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey");
    map.put(SamlConstants.SAML_APPLIES_TO, "http://localhost:9080/your_Web_service");
    map.put(SamlConstants.KEY_ALIAS, "soapinitiator" );
    map.put(SamlConstants.KEY_NAME, "CN=SOAPInitator, O=ACME");
    map.put(SamlConstants.KEY_PASSWORD, "myinitatkeypass");
    map.put(SamlConstants.KEY_STORE_PATH, "keystores/initiator.jceks");
    map.put(SamlConstants.KEY_STORE_PASSWORD, "myinitatstorepass");
    map.put(SamlConstants.KEY_STORE_TYPE, "jceks");
    SAMLGenerateCallbackHandler callbackHandler = new SAMLGenerateCallbackHandler(map); 
    SAMLToken samlToken = (SAMLToken) factory.newSecurityToken(SAMLToken.class,
                          callbackHandler, "system.wss.generate.saml");

    The private key of the sender is specified by the SamlConstants.KEY_ALIAS property and is used to sign selected elements of the request message.

  2. Use the WSSGenerationContext object to prepare for request message security header processing; for example:
    gencon.add(samlToken); //this line of code can be omitted
    WSSTimestamp timestamp = factory.newWSSTimestamp();
    gencon.add(timestamp);
    WSSSignature sig = factory.newWSSSignature(samlToken);
    sig.setTokenReference(SecurityToken.REF_KEYID);
    //If the gencon.add(samlToken); line of code is omitted,
    //the previous line of code must be replaced with
    //sig.setTokenReference(SecurityToken.REF_STR);
    
    sig.setSignatureMethod(WSSSignature.RSA_SHA1);
    sig.setCanonicalizationMethod(WSSSignature.EXC_C14N);
    sig.addSignPart(WSSSignature.BODY);
    sig.addSignPart(WSSSignature.TIMESTAMP);
    sig.addSignPart(WSSSignature.ADDRESSING_HEADERS);
    gencon.add(sig);
    X509GenerateCallbackHandler x509callbackHandler2 = new X509GenerateCallbackHandler(
            null, 
            "keystores/initiator.jceks", 
            "jceks",
            "myinitatstorepass".toCharArray(), 
            "soaprecipient",
            null, 
            "", null);
    SecurityToken st2 = factory.newSecurityToken(X509Token.class, x509callbackHandler2);
    WSSEncryption enc = factory.newWSSEncryption(st2);    
    enc.addEncryptPart(WSSEncryption.BODY_CONTENT);
    enc.addEncryptPart(WSSEncryption.SIGNATURE);
    enc.setEncryptionMethod(WSSEncryption.AES256);
    enc.setKeyEncryptionMethod(WSSEncryption.KW_RSA_OAEP);
    gencon.add(enc);

    In this example, encryption uses a 256 bit key size so you must import the Java Cryptography Extension (JCE) policy file. For more information, read about using the unrestricted JCE policy files in the Tuning Web Services Security applications topic.

  3. Create the WSSConsumingContext object to prepare for response message security header processing; for example:
    WSSConsumingContext concont = factory.newWSSConsumingContext();
    
    HashMap<Object, Object> map = new HashMap<Object, Object>();
    
    SAMLConsumerCallbackHandler callbackHandler = new SAMLConsumerCallbackHandler(map); 
    WSSDecryption dec = factory.newWSSDecryption(SAMLToken.class, callbackHandler,
                                "system.wss.consume.saml");
    dec.addAllowedEncryptionMethod(WSSDecryption.AES256);
    dec.addAllowedKeyEncryptionMethod(WSSDecryption.KW_RSA_OAEP);
    dec.encryptKey(false);
    dec.addRequiredDecryptPart(WSSDecryption.BODY_CONTENT); 
    concont.add(dec);
    X509ConsumeCallbackHandler verHandler = new X509ConsumeCallbackHandler(null, 
                "keystores/initiator.jceks",
                "jceks",
                "myinitatstorepass".toCharArray(),
                "soaprecipient",
                null, null);
    WSSVerification ver = factory.newWSSVerification(X509Token.class, verHandler);
    ver.addRequiredVerifyPart(WSSVerification.BODY); 
    concont.add(ver);
  4. Use the JDK keytool utility to generate the saml-provider.jceks, initiator.jceks, and recipient.jceksfiles that are used to test the example code; for example:
    keytool -genkey -alias samlissuer -keystore saml-provider.jceks -dname "CN=SAMLIssuer, O=ACME" -storepass myissuerstorepass -keypass myissuerkeypass
     -storetype jceks -validity 5000 -keyalg RSA -keysize 2048
    
    keytool -genkey -alias soaprecipient -keystore recipient.jceks -dname "CN=SOAPRecipient, O=ACME" -storepass myreciptstorepass -keypass myreciptkeypass
     -storetype jceks -validity 5000 -keyalg RSA -keysize 2048
    
    keytool -genkey -alias soapinitiator -keystore initiator.jceks -dname "CN=SOAPInitator, O=ACME" -storepass myinitatstorepass -keypass myinitatkeypass
     -storetype jceks -validity 5000 -keyalg RSA -keysize 2048
    
    keytool -export -alias samlissuer -file issuerpub.cer -keystore saml-provider.jceks -storepass myissuerstorepass -storetype jceks
    keytool -export -alias soaprecipient -file reciptpub.cer -keystore recipient.jceks -storepass myreciptstorepass -storetype jceks
    keytool -export -alias soapinitiator -file initatpub.cer -keystore initiator.jceks -storepass myinitatstorepass -storetype jceks
    
    keytool -import -alias samlissuer -file issuerpub.cer -keystore initiator.jceks -storepass myissuerstorepass -storetype jceks -keypass myissuerkeypass -noprompt
    keytool -import -alias soaprecipient -file reciptpub.cer -keystore initiator.jceks -storepass myreciptstorepass -storetype jceks -keypass myreciptkeypass -noprompt
    
    keytool -import -alias samlissuer -file issuerpub.cer -keystore recipient.jceks -storepass myreciptstorepass -storetype jceks -keypass myreciptkeypass -noprompt
    keytool -import -alias soapinitiator -file initatpub.cer -keystore initiator.jceks -storepass myinitatstorepass -storetype jceks -keypass myinitatkeypass -noprompt
    
    keytool -import -alias soapinitiator -file initatpub.cer -keystore saml-provider.jceks -storepass myissuerstorepass -storetype jceks -keypass myissuerkeypass -noprompt

Results

You have learned key building blocks to create a web services client application to send a SAML security token in a SOAP message and to use the asymmetric key that is embedded in SAML security in message level protection.

Example

The following example illustrates the web services provider Web services security policy:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
  xmlns:spe="http://www.ibm.com/xmlns/prod/websphere/200605/ws-securitypolicy-ext">
    <wsp:Policy wsu:Id="response:app_encparts">
        <sp:EncryptedElements>
            <sp:XPath>/*[namespace-uri()='http://schemas.xmlsoap.org/soap/envelope/'
             and local-name()='Envelope']/*[namespace-uri()='http://schemas.xmlsoap.org/soap/envelope/'
             and local-name()='Header']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
             and local-name()='Security']/*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#' and local-name()='Signature']</sp:XPath>
            <sp:XPath>/*[namespace-uri()='http://www.w3.org/2003/05/soap-envelope'
             and local-name()='Envelope']/*[namespace-uri()='http://www.w3.org/2003/05/soap-envelope'
             and local-name()='Header']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
             and local-name()='Security']/*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#' and local-name()='Signature']</sp:XPath>
        </sp:EncryptedElements>
        <sp:EncryptedParts>
            <sp:Body/>
        </sp:EncryptedParts>
    </wsp:Policy>
    <wsp:Policy wsu:Id="request:req_enc">
        <sp:EncryptedParts>
            <sp:Body/>
        </sp:EncryptedParts>
    </wsp:Policy>
    <wsp:Policy wsu:Id="request:app_signparts">
        <sp:SignedParts>
            <sp:Body/>
            <sp:Header Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
            <sp:Header Namespace="http://www.w3.org/2005/08/addressing"/>
        </sp:SignedParts>
        <sp:SignedElements>
            <sp:XPath>/*[namespace-uri()='http://schemas.xmlsoap.org/soap/envelope/'
             and local-name()='Envelope']/*[namespace-uri()='http://schemas.xmlsoap.org/soap/envelope/'
             and local-name()='Header']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
             and local-name()='Security']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
             and local-name()='Timestamp']</sp:XPath>
            <sp:XPath>/*[namespace-uri()='http://www.w3.org/2003/05/soap-envelope'
            and local-name()='Envelope']/*[namespace-uri()='http://www.w3.org/2003/05/soap-envelope'
            and local-name()='Header']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
            and local-name()='Security']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
            and local-name()='Timestamp']</sp:XPath>
        </sp:SignedElements>
    </wsp:Policy>
    <wsp:Policy wsu:Id="response:resp_sig">
        <sp:SignedParts>
            <sp:Body/>
        </sp:SignedParts>
    </wsp:Policy>
    <sp:AsymmetricBinding>
        <wsp:Policy>
            <sp:InitiatorToken>
                <wsp:Policy>
                    <spe:CustomToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512/IncludeToken/Always"/>
                        <wsp:Policy>
                            <spe:WssCustomToken localname="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"/>
                        </wsp:Policy>
                    </spe:CustomToken>
                </wsp:Policy>
            </sp:InitiatorToken>
            <sp:AlgorithmSuite>
                <wsp:Policy>
                    <sp:Basic256/>
                </wsp:Policy>
            </sp:AlgorithmSuite>
            <sp:IncludeTimestamp/>
            <sp:RecipientToken>
                <wsp:Policy>
                    <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512/IncludeToken/Always"/>
                        <wsp:Policy>
                            <sp:WssX509V3Token11/>
                        </wsp:Policy>
                    </sp:X509Token>
                <wsp:Policy>
            <sp:RecipientToken>
            <sp:Layout>
                <wsp:Policy>
                    <sp:Strict/>
                </wsp:Policy>
            </sp:Layout>
        <wsp:Policy>
    <sp:AsymmetricBinding>
</wsp:Policy>