Glen Mazza's Weblog

« Using X.509 security... | Main | Using Message-Layer... »

https://web-gmazza.rhcloud.com/blog/date/20170416 Sunday April 16, 2017

Deploying and Using a CXF Security Token Service (STS)

In this tutorial we'll be creating a CXF Security Token Service (STS) and show how to access the STS using a CXF web service client (WSC). The client will authenticate with the STS to obtain SAML tokens that will subsequently be used to authenticate/authorize SOAP requests to a CXF web service provider (WSP) that trusts the STS. Both X.509 and UsernameToken authentication options for the WSC to the STS are covered below. WS-Trust shifts the validation of the client from the web service provider to the STS, meaning that the web service provider would not need to be individually configured to trust each client accessing it. All that is required is that there be a mutual trust relationship between the WSC and the STS.

With WS-Trust, the SAML assertion the WSC receives from the STS is repeatedly used in the WSC's subsequent calls to the WSP until the token expires (5 minutes per CXF default.) However, if (and only if) you are making several calls from the same client, activating WS-SecureConversation between WSC and WSP can result in performance gains. WS-SecureConversation results in the WSC sending the SAML Assertion just once to the WSP to establish identity; afterwards a simpler security context token (SCT) will be passed from the WSC to the WSP. Activating WS-SecureConversation is an easy extra step and will be covered as an option below.

The finished tutorial source code can be obtained from GitHub by using either the download ZIP button or git clone -v git://github.com/gmazza/blog-samples.git command. As usual, we'll rely on the sample DoubleIt web service provider and client as the starting basis for our work. However the group and artifact ID's listed in the poms have been changed to avoid conflicting with the original tutorial, also much of the dependency and plugin configuration information has been centralized into higher-level POMs, so you might wish to follow along with the finished source code. Note the information within this tutorial may inadvertently have errors within it, so be sure to carefully check and test all work before moving to production with any of this information.

Steps involved for hosting a CXF-based STS on Tomcat:

  1. Run the DoubleIt web service and SOAP client. Follow the DoubleIt tutorial and confirm that SOAP calls between the WSC and WSP are working before proceeding. However, tl;dr version: (a) Start Tomcat, (b) mvn clean install from the source code root, (c) mvn tomcat7:redeploy from the war submodule, (d) mvn exec:exec from the client submodule, (e) if not successful, do any missing additional configuration described in that tutorial.

  2. Add an STS submodule to DoubleIt. This will hold our CXF-based STS, deployable as a separate WAR to Tomcat. Steps:

    1. In the DoubleIt folder, create a sts-war directory alongside the currently existing service, war, and client folders. Add the following folders underneath sts-war:
      for Linux:
      mkdir -p sts-war/src/main/java/sts
      mkdir -p sts-war/src/main/resources
      mkdir -p sts-war/src/main/webapp/WEB-INF/wsdl
      mkdir -p sts-war/src/main/webapp/WEB-INF/lib
      
      for Windows:
      mkdir sts-war\src\main\java\sts
      mkdir sts-war\src\main\resources
      mkdir sts-war\src\main\webapp\WEB-INF\wsdl
      mkdir sts-war\src\main\webapp\WEB-INF\lib
      
    2. Add the following pom.xml file to the sts-war directory.
    3. In the parent DoubleIt/pom.xml file, you'll need to add this new sts-war submodule to the module list as well as both WS-Policy dependencies listed in Step #2 of the UsernameToken with CXF tutorial.
  3. Create keystores for the client, service, and STS. First, if you haven't already the JCE unlimited strength policy files will need to be installed in your JRE as this tutorial uses "strong" 256-bit encryption.

    Create the sts, service and client keystores as follows. (newbie) WARNING: The keys configured here and available in the downloadable sample are publicly available and hence of course must not be used in production--also, those you create should have passwords different from the ones given in this public tutorial:

    keytool -genkey -keyalg RSA -sigalg SHA1withRSA -validity 1461 -alias myservicekey -keypass skpass -storepass sspass -keystore serviceKeystore.jks -dname "cn=localhost"
    keytool -genkey -keyalg RSA -sigalg SHA1withRSA -validity 1461 -alias myclientkey  -keypass ckpass -storepass cspass -keystore clientKeystore.jks -dname "cn=clientuser"
    keytool -genkey -keyalg RSA -sigalg SHA1withRSA -validity 1461 -alias mystskey  -keypass stskpass -storepass stsspass -keystore stsKeystore.jks -dname "cn=localhost"
    

    Next, the service keystore will need to have the STS public key added so it trusts it, and vice-versa. The service keystore should not have the client's public key, since the service does not directly trust the client (the reason for our use of an STS). However, the client will need to have the STS' and WSP's certificates added to its truststore, as it relies on symmetric binding to encrypt the SOAP requests it makes to both:

    keytool -export -rfc -keystore clientKeystore.jks -storepass cspass -alias myclientkey -file MyClient.cer
    keytool -export -rfc -keystore serviceKeystore.jks -storepass sspass -alias myservicekey -file MyService.cer
    keytool -export -rfc -keystore stsKeystore.jks -storepass stsspass -alias mystskey -file MySTS.cer
    
    keytool -import -trustcacerts -keystore serviceKeystore.jks -storepass sspass -alias mystskey -file MySTS.cer -noprompt
    keytool -import -trustcacerts -keystore stsKeystore.jks -storepass stsspass -alias myservicekey -file MyService.cer -noprompt
    
    keytool -import -trustcacerts -keystore clientKeystore.jks -storepass cspass -alias myservicekey -file MyService.cer -noprompt
    keytool -import -trustcacerts -keystore clientKeystore.jks -storepass cspass -alias mystskey -file MySTS.cer -noprompt
    

    Further, if you plan on using X.509 authentication of the WSC to the STS (instead of UsernameToken), the former's public key will need to be in the latter's truststore:

    keytool -import -trustcacerts -keystore stsKeystore.jks -storepass stsspass -alias myclientkey -file MyClient.cer -noprompt
    

    For tutorial purposes place the keys in the src/main/resources folders of the client, service, and sts-war submodules, creating those folders where necessary. Also in those folders, place each key's corresponding properties file (one file per folder, alongside its keystore):

    stsKeystore.properties:

    org.apache.ws.security.crypto.merlin.keystore.type=jks
    org.apache.ws.security.crypto.merlin.keystore.password=stsspass
    org.apache.ws.security.crypto.merlin.keystore.alias=mystskey
    org.apache.ws.security.crypto.merlin.keystore.file=stsKeystore.jks
    

    serviceKeystore.properties

    org.apache.ws.security.crypto.merlin.keystore.type=jks
    org.apache.ws.security.crypto.merlin.keystore.password=sspass
    org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
    org.apache.ws.security.crypto.merlin.file=serviceKeystore.jks
    

    clientKeystore.properties:

    org.apache.ws.security.crypto.merlin.keystore.type=jks
    org.apache.ws.security.crypto.merlin.keystore.password=cspass
    org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey
    org.apache.ws.security.crypto.merlin.file=clientKeystore.jks
    
  4. Configure the STS Provider Not just your WSP but also the STS (a WSP itself) will need a WSDL, and these are complex to write. This tutorial's WSDL includes both the UT binding from the CXF STS Sample and the X.509 binding from the Talend STS Sample. The binding here refers to the authentication method the WSC is to use to get the token from the STS, make sure to edit the wsdl:port element's binding attribute to point to the authentication method you wish to use. The DoubleItSTSService.wsdl file should be placed in the sts-war/src/main/webapp/WEB-INF/wsdl folder.

    Next we'll need to provide the necessary configuration information for the above WSDL. Place the following cxf-servlet.xml file in the sts-war/src/main/webapp/WEB-INF folder, and comment out the X.509 authentication configuration block and uncomment the UsernameToken block if you wish to use the latter authentication method:

  5. We'll also of course need a web.xml for the STS war, to be placed in the sts-war/src/main/webapp/WEB-INF folder. Important: If you're using UsernameToken authentication, do a safety check via Wireshark (as explained at the end) to ensure the username and password are being encrypted from the WSC to the STS. By default, CXF will do message-layer encryption with no further action on your part. Alternatively, transport-layer encryption between the STS and WSC will also provide security, the sts-war submodule below has a security-constraint block that can be uncommented to activate SSL.

    web.xml:

    At this stage, it would be good to test that the STS can be deployed. Run mvn clean install from the cxf_sts_tutorial root folder, then mvn tomcat7:redeploy -pl sts-war to deploy the STS on Tomcat. If you're using X.509 authentication, make sure you can view the WSDL list at http://localhost:8080/DoubleItSTS as well as the WSDL itself (linked from that page) before proceeding. For UsernameToken, if you've activated SSL, make sure the above http:// URL isn't accessible while the encrypted https://localhost:8443/DoubleItSTS is.

  6. Configure the web service provider to require the SAML tokens provided by the STS. Steps involved:

    1. Replace the service module's WSDL with the following one which includes the new WS-Policy requirements:

      DoubleIt.wsdl (with policy requirements added):
    2. If you wish to use WS-SecureConversation, use the contents of the DoubleItSecConv.txt in the source code download as your DoubleIt.wsdl, it has the necessary WS-Policy statements to activate it.

    3. We'll need a class to return the private key password when needed by the WSP. Place the following file in the same directory as the WSP's DoubleItPortTypeImpl class:

      ServiceKeystorePasswordCallback.java:

    4. In the war submodule, alter the cxf-servlet.xml to link in the serviceKeystore.properties file to the WSP, as shown here. Be sure to use the ".sct"-suffixed entries instead if activating WS-SecureConversation.

      You may wish to deploy the WSP at this time and ensure its WSDL is available. Run mvn clean install from the cxf_sts_tutorial root folder, then mvn tomcat7:redeploy -pl war to deploy on Tomcat, the WSDL should be viewable at http://localhost:8080/doubleit/services/doubleit?wsdl.

  7. Configure the SOAP client to use the STS to obtain the security token. Steps:

    • In the client package, create the following password callback handler, used to obtain the private key password in the X.509 authentication scenario or the user password if UsernameToken is being used. (The class can be simplified to support just one of the authentication methods if desired.)

      ClientCallbackHandler.java:

      package client;
      
      import java.io.IOException;
      import javax.security.auth.callback.Callback;
      import javax.security.auth.callback.CallbackHandler;
      import javax.security.auth.callback.UnsupportedCallbackException;
      import org.apache.ws.security.WSPasswordCallback;
      
      public class ClientCallbackHandler implements CallbackHandler {
      
          public void handle(Callback[] callbacks) throws IOException,
                  UnsupportedCallbackException {
              for (int i = 0; i < callbacks.length; i++) {
                  if (callbacks[i] instanceof WSPasswordCallback) {
                      WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
                      if (pc.getUsage() == WSPasswordCallback.DECRYPT || 
                          pc.getUsage() == WSPasswordCallback.SIGNATURE) {
                          // typically X.509 auth only
                          if ("myclientkey".equals(pc.getIdentifier())) {
                              pc.setPassword("ckpass");
                          }
                      } else if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN) {
                          // UsernameToken auth only
                          if ("alice".equals(pc.getIdentifier())) {
                              pc.setPassword("clarinet");
                          }
                      }
                  }
              }
          }
      
      }
      
    • In the resources folder, the following cxf.xml file needs to be created. It provides the necessary security configuration information for the WSC's communication with the STS. Comment/uncomment according to the notes given in this file based on whether you're using UT or X.509 authentication between the WSC and the STS, SSL for the STS, and whether you're also activating WS-SecureConversation.

  8. Run the client and test under error situations to make sure authentication is working properly. Run the client as shown in Step #9 of the WSDL-first tutorial and make sure you see the same output.

    The client's cxf.xml activates console-based logging, so if the calls go well you should see two sets of SOAP requests and responses, the first from the WSC to the STS to obtain the token, and the second from the WSC to the WSP to obtain the doubled number. If you're using UT authentication, good to confirm from the response that the password ("clarinet" in this tutorial) is encrypted, further if your using UT with SSL, using Wireshark to see that the SOAP requests and responses are entirely encrypted. For the X.509 authentication method, the logging can help you confirm that the contents within the soap envelope are being encrypted between WSC and the STS. Finally, testing the client again under various invalid circumstances (wrong callback handler passwords, no trust relationship between STS and service or STS and client, no SOAP header sent to service, etc.) can help confirm the STS and WSP are blocking calls that they should be blocking.

    If the client is not returning the expected SOAP response, a good first check would be to make sure the STS and Service WSDLs are visible from a browser as discussed at the end of Steps #4 and #5 above. If so, then checking the CXF error messages returned in the Tomcat error logs and using Wireshark can help pinpoint the problem. If you get the error message "A security error was encountered when verifying the message" while using UT that could indicate the client's supplied password was incorrect, while a "Unexpected EOF in prolog" can happen if the STS is configured to use SSL but the client's cxf.xml is configured to the non-SSL WSDL address. To speed up iterative debugging, note that the mvn clean install tomcat7:redeploy command, if run from the cxf_sts_tutorial root folder, will deploy and undeploy both the STS and the WSP within a single step.

Notes:
  1. This blog entry shows the SOAP messages that were created in order to complete a single client DoubleIt request using the X.509 authentication method.
  2. Check Colm O hEigeartaigh's blog for the latest information on WS-Trust and related technologies.
  3. See this mail post for how to view the security token from the web service client.
  4. This whitepaper (pdf) by Jiandong Guo provides an entry-level introduction to WS-Trust, see also his much more thorough Enterprise Tech Tip.
  5. Talend also provides a advanced CXF STS sample with STS token validation.

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed

Valid HTML! Valid CSS!

This is a personal weblog, I do not speak for my employer.