Sending self-issued SAML holder-of-key tokens with symmetric 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 symmetric key that is embedded in 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 Web Services Security policy attached to the web services provider is that of the SAML20 HoK Symmetric WSSecurity default policy set that is shipped in WebSphere® Application Server 7.0.0.7 and later releases.

Procedure

  1. Create a SAML security token that contains 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}LCswLTovPiws");
    customProps.put(SamlConstants.KEY_ALIAS_PROP, "samlissuer");
    customProps.put(SamlConstants.KEY_NAME_PROP, "CN=SAMLIssuer, O=EXAMPLE");
    customProps.put(SamlConstants.KEY_PW_PROP, "{xor}NDomLz4sLA==");
    customProps.put(SamlConstants.TS_PATH_PROP, "keystores/saml-provider.jceks");
    customProps.put(SamlConstants.TS_TYPE_PROP, "JCEKS");
    customProps.put(SamlConstants.TS_PW_PROP, "{xor}LCswLTovPiws"); 
    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.SERVICE_ALIAS, "soaprecipient");
    map.put(SamlConstants.KEY_TYPE, 
            "http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey");
    map.put(SamlConstants.SAML_APPLIES_TO, "http://localhost:9080/your_Web_service");
    map.put(RequesterConfiguration.RSTT.ENCRYPTIONALGORITHM,
            "http://www.w3.org/2001/04/xmlenc#aes256-cbc");
    map.put(SamlConstants.KEY_SIZE, "256");
    SAMLGenerateCallbackHandler callbackHandler = new 
            SAMLGenerateCallbackHandler(map); 
    SAMLToken samlToken = (SAMLToken) factory.newSecurityToken(SAMLToken.class,
            callbackHandler, "system.wss.generate.saml");

    The embedded proof key in the SAML security token is encrypted for the target Web service. The public key of the target service that encrypts the proof key is specified by the SamlConstants.SERVICE_ALIAS property which specifies a public certificate in the trust file. The trust file location is specified by a com.ibm.websphere.wssecurity.wssapi.WSSGenerationContext custom property. In this example, you must import the Java Cryptography Extension (JCE) policy file because encryption uses 256 bit key size. For more information, read about using the unrestricted JCE policy files in the Tuning Web Services Security applications topic.

    If you prefer to use derived keys for digital signing and for encryption instead of using symmetric key directly, add the following name-value pair:

    map.put(SamlConstants.REQUIRE_DKT, "true");
  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.setSignatureMethod(WSSSignature.HMAC_SHA1);
    sig.setCanonicalizationMethod(WSSSignature.EXC_C14N);
    sig.addSignPart(WSSSignature.BODY);
    sig.addSignPart(WSSSignature.TIMESTAMP);
    sig.addSignPart(WSSSignature.ADDRESSING_HEADERS);
    sig.setTokenReference(SecurityToken.REF_KEYID);
    //If the gencon.add(samlToken); line of code is omitted, or DerivedKey is used
    //the previous line of code must be replaced with
    //sig.setTokenReference(SecurityToken.REF_STR);
    
    gencon.add(sig);
    
    WSSEncryption enc = factory.newWSSEncryption(samlToken); 
    
    enc.setEncryptionMethod(WSSEncryption.AES256);
    enc.setTokenReference(SecurityToken.REF_KEYID);
    //If the gencon.add(samlToken); line of code is omitted, or DerivedKey is used
    //the previous line of code must be replaced with
    //enc.setTokenReference(SecurityToken.REF_STR);
    enc.encryptKey(false);
    enc.addEncryptPart(WSSEncryption.BODY_CONTENT);
    enc.addEncryptPart(WSSEncryption.SIGNATURE);
    gencon.add(enc);
  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.encryptKey(false);
    dec.addRequiredDecryptPart(WSSDecryption.BODY_CONTENT); 
    
    concont.add(dec);
    
    callbackHandler = new SAMLConsumerCallbackHandler(map);
    WSSVerification ver = factory.newWSSVerification(SAMLToken.class, callbackHandler,
            "system.wss.consume.saml");
    ver.addAllowedSignatureMethod(WSSVerification.HMAC_SHA1);
    ver.addRequiredVerifyPart(WSSVerification.BODY);
    ver.addRequiredVerifyPart(WSSVerification.TIMESTAMP);
    
    concont.add(ver);
  4. Use the JDK keytool utility to generate the saml-provider.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 issuerstorepass
     -keypass issuerkeypass -storetype jceks -validity 5000 -keyalg RSA -keysize 2048
    
    keytool -genkey -alias soaprecipient -keystore recipient.jceks -dname "CN=SOAPRecipient, O=ACME" -storepass reciptstorepass
     -keypass reciptkeypass -storetype jceks -validity 5000 -keyalg RSA -keysize 2048
    
    keytool -export -alias soaprecipient -file reciptpub.cer -keystore recipient.jceks -storepass reciptstorepass -storetype jceks
    
    keytool -import -alias soaprecipient -file reciptpub.cer -keystore saml-provider.jceks -storepass issuerstorepass -storetype jceks
     -keypass issuerkeypass -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 symmetric key that is embedded in SAML security in message level protection.