+

Search Tips   |   Advanced Search

Sending self-issued SAML sender-vouches tokens using WSS APIs with message level protection

We can create self-issued SAML tokens with the sender-vouches subject confirmation method and use the JAX-WS programming model and Web Services Security APIs (WSS APIs) to send these tokens with web services request messages with message level protection.

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.

We can protect SOAP request messages and SAML tokens using the Web Services Security programming interface to satisfy the sender-vouches subject confirmation method validation requirements with message level protection. Using the programming interfaces in web services client is an alternative approach to using policy set and binding configuration.

We can create a self-issued SAML token and then send the SAML token in web services request messages from a web services client. The web services application client used in this task is a modified version of the client code contained in the JaxWSServicesSamples sample application that is available for download. Code snippets from the sample are described in the procedure section, and a complete, ready-to-use web services client sample is provided in the Example section.

This product does not provide a default policy set that requires SAML tokens with sender-vouches subject confirmation method. Read about configuring client and provider bindings for the SAML sender-vouches token to learn more about how to create a Web Services Security policy to require SAML tokens with sender-vouches subject confirmation and how to create a custom binding configuration. We must attach the policy and binding to the web services provider. The code sample described in this task assumes that the web services provider policy requires that both the SAML tokens and the message bodies are digitally signed by using an X.509 security token.

  1. Identify and obtain the web services client to use to invoke a web services provider.

    Use this client to insert SAML tokens in SOAP request messages programmatically using WSS APIs.

    The web services client used in this procedure is a modified version of the client code contained in the JaxWSServicesSamples web services sample application.

    To obtain and modify the sample web services client to add the Web Services Security API to pass SAML sender-vouches tokens in SOAP request messages programmatically using WSS APIs...

    1. Download the JaxWSServicesSamples sample application. The JaxWSServicesSamples sample is not installed by default.

    2. Obtain the JaxWSServicesSamples client code.

      For example purposes, this procedure uses a modified version of the Echo thin client sample included in the JaxWSServicesSamples sample. The web services Echo thin client sample file, SampleClient.java, is located in the src\SampleClientSei\src\com\ibm\was\wssample\sei\cli directory. The sample class file is included in the WSSampleClientSei.jar file.

      The JaxWSServicesSamples.ear enterprise application and supporting Java archives (JAR) files are located in the installableApps directory within the JaxWSServicesSamples sample application.

    3. Deploy the JaxWSServicesSamples.ear file onto the application server. After you deploy the JaxWSServicesSamples.ear file, you are ready to test the sample web services client code against the sample application.

    Instead of using the web services client sample, we can choose to add the code snippets to pass SAML tokens in SOAP request messages programmatically using WSS APIs in our own web services client application. The example in this procedure uses a JAX-WS Web services thin client; however, we can also use a managed client.

  2. Use the CallService() method to specify the Web services security configuration parameters required to invoke a target Web services provider using a self-issued SAML token.

    The CallService() method sets configuration parameters required by the Web Services Security runtime environment via the com.ibm.websphere.wssecurity.wssapi.WSSGenerationContext custom property to generate a self-issued SAMLToken.

    The following code snippet illustrates using the CallService() method to set the SamlConstants.SAML_SELF_ISSUER_CONFIG system property:

    public static void main(String[] args) {
      SampleSamlSVClient sample = new SampleSamlSVClient();
      sample.CallService();
    }
    
    /**
     * CallService Parms were already read. Now call the service proxy classes  * 
     */
    void CallService() {
      String response = "ERROR!:";
      try {
        System.setProperty("java.security.auth.login.config", " profile_root/properties/wsjaas.conf");

    Read about configuring a SAML token during token creation for more information about how we can specify configuration properties to control how the token is configured.

  3. Add the Thin Client for JAX-WS JAR file to the class path. Add app_server_root/runtimes/com.ibm.jaxws.thinclient_8.5.0.jar file to the class path. See the testing web services-enabled clients information for more information about adding this JAR file to the class path.

  4. Create the self-issued SAML token. The following code snippet illustrates creating the SAML token:
    // Create SAMLToken
    HashMap<Object, Object> map = new HashMap<Object, Object>();
    map.put(SamlConstants.CONFIRMATION_METHOD, "sender-vouches");
    map.put(SamlConstants.TOKEN_TYPE, WSSConstants.SAML.SAML20_VALUE_TYPE);
    map.put(SamlConstants.SAML_NAME_IDENTIFIER, "Alice");
    map.put(SamlConstants.SIGNATURE_REQUIRED, "true"); 
    SAMLGenerateCallbackHandler callbackHandler = new SAMLGenerateCallbackHandler(map);
    SecurityToken samlToken = factory.newSecurityToken(SAMLToken.class, callbackHandler, "system.wss.generate.saml");
    System.out.println("SAMLToken id = " + samlToken.getId());

    1. Use the WSSFactory newSecurityToken method to specify how to create the SAML token.

      Specify the following method to create the SAML token:

        WSSFactory newSecurityToken(SAMLToken.class, callbackHandler, "system.wss.generate.saml")

      Creating a SAML token requires the Java security permission wssapi.SAMLTokenFactory.newSAMLToken. Add the following policy statement to the Java security policy file or the application client was.policy file:

        permission java.security.SecurityPermission "wssapi.SAMLTokenFactory.newSAMLToken

      The SAMLToken.class parameter specifies the type of security token to create.

      The callbackHandler object contains parameters that define the characteristics of the SAMLToken that you are creating. This object points to a SAMLGenerateCallbackHandler object that specifies the following configuration parameters described in the following table:

      properties. This table describes the configuration parameters for the SAMLGenerateCallbackHandler object using the sender-vouches
      Property Description Required
      SamlConstants.CONFIRMATION_METHOD Specifies to use the sender-vouches confirmation method. Yes
      SamlConstants.TOKEN_TYPE

      Uses the constant value, WSSConstants.SAML.SAML20_VALUE_TYPE to specify a SAML 2.0 token type.

      When a web services client has policy set attachments, this property is not used by Web Services Security runtime environment. In this scenario, specify the token value type by the valueType attribute of the tokenGenerator binding configuration.

      The example in this procedure uses a SAML 2.0 token; however, we can also use the WSSConstants.SAML.SAML11_VALUE_TYPE value.

      Yes
      SamlConstants.SAML_NAME_IDENTIFIER

      User identity such as myname as the NameID value in the SAMLToken.

      If we do not define this parameter when using the Thin Client for JAX-WS, the NameID value does not contain useful information.

      For a web services managed client, such a Java EE application making a web services request invocation, the Web Services Security runtime environment tries to extract user security information from the security context. Similarly, if we do not define this parameter for a managed web services client, the NameID value contains an UNAUTHENTICATED name identifier.

      This property is not used if the web services client has policy set attachments. Read about sending SAML tokens to learn more about sending the SAML token identity and attributes.

      No
      SamlConstants.SIGNATURE_REQUIRED

      Whether the issuer is required to digitally sign the SAML token.

      A true value specifies that issuer is required to digitally sign the SAML token. Default.

      No

      The system.wss.generate.saml parameter specifies to use a JAAS login configuration and specifies the login module that is invoked to create the SAML token. Specify a JVM property to define a JAAS configuration file containing the required JAAS login configuration; for example:

        Djava.security.auth.login.config= profile_root/properties/wsjaas.conf

      Alternatively, we can specify a JAAS login configuration file using a Java system property in the sample client code; for example:

        System.setProperty("java.security.auth.login.config", " profile_root/properties/wsjaas.conf");

    2. Obtain the token identifier of the created SAML token.

      Use the following statement as a simple test for the SAML token created:

        System.out.println("SAMLToken id = " + samlToken.getId())

  5. Add the SAML token to the SOAP security header of web services request messages.

    1. Initialize the web services client and configure the SOAPAction properties. The following code example illustrates these actions:
      // Initialize web services client EchoService12PortProxy echo = new EchoService12PortProxy();
      echo._getDescriptor().setEndpoint(endpointURL);
      
      // Configure SOAPAction properties BindingProvider bp = (BindingProvider) (echo._getDescriptor().getProxy());
      Map<String, Object> requestContext = bp.getRequestContext();
      requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL);
      requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
      requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, "echoOperation");
      
      // Initialize WSSGenerationContext
      WSSGenerationContext gencont = factory.newWSSGenerationContext();
      gencont.add(samlToken); 

    2. Initialize the WSSGenerationContext. The following code snippet illustrates the use of the gencont.object of the WSSGenerationContext type to initialize a generation context to enable you to insert the SAMLToken into a web services request message:
      // Initialize WSSGenerationContext
      WSSGenerationContext gencont = factory.newWSSGenerationContext();
      gencont.add(samlToken); 

      Specifically, the gencont.add(samlToken) method call specifies to put the SAML token into a request message. This operation requires the client code to have the following Java 2 Security permission:

        "permission javax.security.auth.AuthPermission "modifyPrivateCredentials"

  6. Add an X.509 token for message protection.

    This sample code uses the dsig-sender.ks key file and the SOAPRequester sample key. We must not use the sample key in a production environment. The following code snippet illustrates adding an X.509 token for message protection:

    // Add an X.509 Token for message protection
    X509GenerateCallbackHandler x509callbackHandler = new X509GenerateCallbackHandler(
            null, " profile_root/etc/ws-security/samples/dsig-sender.ks", 
            "JKS", 
            "client".toCharArray(), 
            "soaprequester", 
            "client".toCharArray(), 
            "CN=SOAPRequester, OU=TRL, O=IBM, ST=Kanagawa, C=JP", null); 
    
    SecurityToken x509 = factory.newSecurityToken(X509Token.class, x509callbackHandler, "system.wss.generate.x509");
    
    WSSSignature sig = factory.newWSSSignature(x509);
    sig.setSignatureMethod(WSSSignature.RSA_SHA1);
    
    WSSSignPart sigPart = factory.newWSSSignPart();
    sigPart.setSignPart(samlToken);
    sigPart.addTransform(WSSSignPart.TRANSFORM_STRT10);
    sig.addSignPart(sigPart);
    sig.addSignPart(WSSSignature.BODY);

    1. Create a WSSSignature object with the X509 token. The following line of code creates a WSSSignature object with the X509 token:

        WSSSignature sig = factory.newWSSSignature(x509);

    2. Add the signed part to use for message protection. The following line of code specifies to add WSSSignature.BODY as the signed part:

        sig.addSignPart(WSSSignature.BODY);

    3. Add the timestamp element in the SOAP messages security header. The SAML20 SenderVouches WSHTTPS and SAML11 SenderVouches WSHTTPS policy sets require web services requests and response messages to carry a timestamp element in the SOAP messages Security header. In the following code snippet, the factory.newWSSTimestamp() method call generates the timestamp, and the gencont.add(timestamp) method call adds the timestamp into the request message:
      // Add Timestamp
      WSSTimestamp timestamp = factory.newWSSTimestamp();
      gencont.add(timestamp);
      sig.addSignPart(WSSSignature.TIMESTAMP);
      
      gencont.add(sig);
      
      WSSConsumingContext concont = factory.newWSSConsumingContext();

    4. Configure the verification of the digital signature in the response message.

      A separate WSSSignPart is needed to specify the SecurityTokenReference transformation algorithm that is represented by the WSSSignPart.TRANSFORM_STRT10 attribute. A SAML Token cannot be digitally signed directly. This attribute enables the Web Services Security runtime environment to generate a SecurityTokenReference element to reference the SAMLToken and to digitally sign the SAMLToken using the SecurityTokenReference transformation. The following line of code specifies to use the WSSSignPart.TRANSFORM_STRT10 attribute:

      WSSSignPart sigPart = factory.newWSSSignPart();
      sigPart.setSignPart(samlToken);
      sigPart.addTransform(WSSSignPart.TRANSFORM_STRT10);

    5. Attach the WSSGenerationContext object to the web services RequestContext object. The WSSGenerationContext object now contains all the security information required to format a request message. The gencont.process(requestContext) method call attaches the WSSGenerationContext object to the web services RequestContext object to enable the Web Services Security runtime environment to format the required SOAP security header; for example:
      // Attaches the WSSGenerationContext object to the web services RequestContext object. 
      gencont.process(requestContext);

  7. Use the X.509 token to validate the digital signature and the integrity of the response message. If the provider policy requires the response message to be digitally signed, you must initialize the X.509 token.

    1. A X509ConsumeCallbackHandler object is initialized with a truststore, dsig-receiver.ks, and a certificate path object to validate the provider digital signature. The following line of code is used to initialize the X509ConsumeCallbackHandler object:
      X509ConsumeCallbackHandler callbackHandlerVer = new X509ConsumeCallbackHandler( 
              "profile_root/etc/ws-security/samples/dsig-receiver.ks", "JKS", "server".toCharArray(), certList, java.security.Security.getProvider("IBMCertPath"));

    2. A WSSVerification object is created and the message body is added to the verification object so that the Web Services Security runtime environment validates the digital signature.

      The following line of code is used to initialize the WSSVerification object:

        WSSVerification ver = factory.newWSSVerification(X509Token.class, callbackHandlerVer);

      The WSSConsumingContext object now contains all the security information required to format a request message. The concont.process(requestContext) method call attaches the WSSConsumingContext object to the response method; for example:

      // Attaches the WSSConsumingContext object to the web services RequestContext object. 
      concont.process(requestContext);


Results

You have created a self-issued SAML token with the sender-vouches confirmation method and then sent this token with web services request messages using the JAX-WS programming model and WSS APIs.


Example

The following code sample is a complete, ready-to-use web services client application that demonstrates how to create a self-issued SAML sender-vouches token and send that SAML token in web services request messages. This sample code illustrates the procedure steps described previously.

/**
 * The following source code is sample code created by IBM Corporation.  
 * This sample code is provided to you solely for the purpose of assisting you in the 
 * use of the technology.  The code is provided 'AS IS', without warranty or condition of 
 * any kind.  IBM shall not be liable for any damages arising out of the use of the 
 * sample code, even if IBM has been advised of the possibility of such damages.
 */
package com.ibm.was.wssample.sei.cli;

import com.ibm.was.wssample.sei.echo.EchoService12PortProxy;
import com.ibm.was.wssample.sei.echo.EchoStringInput;
import com.ibm.websphere.wssecurity.callbackhandler.SAMLGenerateCallbackHandler;
import com.ibm.websphere.wssecurity.wssapi.WSSConsumingContext;
import com.ibm.websphere.wssecurity.wssapi.WSSFactory;
import com.ibm.websphere.wssecurity.wssapi.WSSGenerationContext;
import com.ibm.websphere.wssecurity.wssapi.WSSTimestamp;
import com.ibm.websphere.wssecurity.wssapi.token.SAMLToken;
import com.ibm.websphere.wssecurity.wssapi.token.SecurityToken;
import com.ibm.websphere.wssecurity.callbackhandler.X509ConsumeCallbackHandler;
import com.ibm.websphere.wssecurity.callbackhandler.X509GenerateCallbackHandler;
import com.ibm.websphere.wssecurity.wssapi.WSSException;
import com.ibm.websphere.wssecurity.wssapi.signature.WSSSignPart;
import com.ibm.websphere.wssecurity.wssapi.signature.WSSSignature;
import com.ibm.websphere.wssecurity.wssapi.verification.WSSVerification;
import com.ibm.websphere.wssecurity.wssapi.token.X509Token;
import com.ibm.wsspi.wssecurity.core.token.config.WSSConstants;
import com.ibm.wsspi.wssecurity.saml.config.SamlConstants;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;

import javax.xml.ws.BindingProvider;

public class SampleSamlSVClient {
  private String urlHost = "localhost";
  private String urlPort = "9081";
  private static final String CONTEXT_BASE = "/WSSampleSei/";
  private static final String ECHO_CONTEXT12 = CONTEXT_BASE+"EchoService12"; 
  private String message = "HELLO";
  private String uriString = "http://" + urlHost + ":" + urlPort;
  private String endpointURL = uriString + ECHO_CONTEXT12;
  private String input = message;

  /**
   * main()
   * 
   * see printusage() for command-line arguments    * 
   * @param args
   */
  public static void main(String[] args) {
    SampleSamlSVClient sample = new SampleSamlSVClient();
    sample.CallService();
  }

  /**
   * CallService Parms were already read. Now call the service proxy classes. 
   * 
   */
  void CallService() {
    String response = "ERROR!:";
    try {
      System.setProperty("java.security.auth.login.config", " profile_root/properties/wsjaas.conf");

      // Initialize WSSFactory object 
      WSSFactory factory = WSSFactory.getInstance();
      // Initialize WSSGenerationContext
      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 
      // Create SAMLToken 
      HashMap<Object, Object> map = new HashMap<Object, Object>();
      map.put(SamlConstants.CONFIRMATION_METHOD, "sender-vouches");
      map.put(SamlConstants.TOKEN_TYPE, WSSConstants.SAML.SAML20_VALUE_TYPE);
      map.put(SamlConstants.SAML_NAME_IDENTIFIER, "Alice");
      map.put(SamlConstants.SIGNATURE_REQUIRED, "true");
      SAMLGenerateCallbackHandler callbackHandler = new SAMLGenerateCallbackHandler(map);

      SecurityToken samlToken = factory.newSecurityToken(SAMLToken.class, callbackHandler, "system.wss.generate.saml");

      System.out.println("SAMLToken id = " + samlToken.getId());

      // Initialize web services client. 
      EchoService12PortProxy echo = new EchoService12PortProxy();
      echo._getDescriptor().setEndpoint(endpointURL);

      // Configure SOAPAction properties 
      BindingProvider bp = (BindingProvider) (echo._getDescriptor().getProxy());
      Map<String, Object> requestContext = bp.getRequestContext();
      requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL);
      requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
      requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, "echoOperation");

      // Initialize WSSGenerationContext
      WSSGenerationContext gencont = factory.newWSSGenerationContext();
      gencont.add(samlToken);

      // Add X.509 Tokens for message protection
      X509GenerateCallbackHandler x509callbackHandler = new X509GenerateCallbackHandler(
          null, " profile_root/etc/ws-security/samples/dsig-sender.ks", 
          "JKS", 
          "client".toCharArray(), 
          "soaprequester", 
          "client".toCharArray(), 
          "CN=SOAPRequester, OU=TRL, O=IBM, ST=Kanagawa, C=JP", null); 

      SecurityToken x509 = factory.newSecurityToken(X509Token.class, x509callbackHandler, "system.wss.generate.x509");

      WSSSignature sig = factory.newWSSSignature(x509);
      sig.setSignatureMethod(WSSSignature.RSA_SHA1);

      WSSSignPart sigPart = factory.newWSSSignPart();
      sigPart.setSignPart(samlToken);
      sigPart.addTransform(WSSSignPart.TRANSFORM_STRT10);
      sig.addSignPart(sigPart);                 
      sig.addSignPart(WSSSignature.BODY);

      // Add timestamp
      WSSTimestamp timestamp = factory.newWSSTimestamp();
      gencont.add(timestamp);
      sig.addSignPart(WSSSignature.TIMESTAMP);

      gencont.add(sig);

      WSSConsumingContext concont = factory.newWSSConsumingContext();

      // Prepare to consume timestamp in response message       concont.add(WSSConsumingContext.TIMESTAMP); 

      // Prepare to verify digital signature in response message         
      X509Certificate x509cert = null;
      try {
        InputStream is = new FileInputStream(" profile_root/etc/ws-security/samples/intca2.cer");
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        x509cert = (X509Certificate) cf.generateCertificate(is);
      } catch (FileNotFoundException e1) {
        throw new WSSException(e1);
      } catch (CertificateException e2) {
        throw new WSSException(e2);
      }
      Set<Object> eeCerts = new HashSet<Object>();
      eeCerts.add(x509cert);

      java.util.List<CertStore> certList = new java.util.ArrayList<CertStore>();
      CollectionCertStoreParameters certparam = new CollectionCertStoreParameters(eeCerts);

      CertStore cert = null;
      try {
        cert = CertStore.getInstance("Collection", certparam, "IBMCertPath");
      } catch (NoSuchProviderException e1) {
        throw new WSSException(e1);
      } catch (InvalidAlgorithmParameterException e2) {
        throw new WSSException(e2);
      } catch (NoSuchAlgorithmException e3) {
        throw new WSSException(e3);
      }
      if (certList != null) {
        certList.add(cert);
      }

      X509ConsumeCallbackHandler callbackHandlerVer = new X509ConsumeCallbackHandler(
          " profile_root/etc/ws-security/samples/dsig-receiver.ks", "JKS", "server".toCharArray(), certList, java.security.Security.getProvider("IBMCertPath"));

      WSSVerification ver = factory.newWSSVerification(X509Token.class, callbackHandlerVer);

      ver.addRequiredVerifyPart(WSSVerification.BODY);
      concont.add(ver);

      gencont.process(requestContext);
      concont.process(requestContext);

      // Build the input object       EchoStringInput echoParm = 
          new com.ibm.was.wssample.sei.echo.ObjectFactory().createEchoStringInput();
      echoParm.setEchoInput(input);
      System.out.println(">> CLIENT: SEI Echo to " + endpointURL);  

      // Call the service       response = echo.echoOperation(echoParm).getEchoResponse();

      System.out.println(">> CLIENT: SEI Echo invocation complete.");
      System.out.println(">> CLIENT: SEI Echo response is: " + response);
    } catch (Exception e) {
      System.out.println(">> CLIENT: ERROR: SEI Echo EXCEPTION.");
      e.printStackTrace();
    }
  }
}

When this web services client application sample runs correctly, you receive messages like the following messages:

SAMLToken id = _6CDDF0DBF91C044D211271166233407
Retrieving document at 'file: profile_root/.../wsdl/'.
>> CLIENT: SEI Echo to http://localhost:9080/WSSampleSei/EchoService12
>> CLIENT: SEI Echo invocation complete.
>> CLIENT: SEI Echo response is: SOAP12==>>HELLO


Related concepts

  • Access the samples
  • SAML concepts
  • SSL configurations


    Related tasks

  • Secure JAX-WS web services using message-level security
  • Configure client and provider bindings for the SAML sender-vouches token
  • Sending self-issued SAML sender-vouches tokens using WSS APIs with SSL transport protection
  • Configure client and provider bindings for the SAML bearer token
  • Test web services-enabled clients
  • Samples documentation

  • SAML Issuer Config Properties
  • Web Services Security APIs