Using NSS for FIPS 140-2 compliant message security in CXF

If you have ever supported a client in the US Government, civil or otherwise, you have probably heard of the FIPS 140-2 compliance requirements for cryptographic operations.  If you haven’t, stick around a little longer and it will eventually ruin your day.  This article discusses how to use free open-source software to provide FIPS 140-2 compliant message-level security in Apache CXF’s SOAP over HTTP Web services.

This article builds on the setup described in my previous article Using NSS for FIPS 140-2 compliant transport security in CXF by adding a WS-Security compliant digital signature over portions of the message as well as using WS-Security to encrypt portions of the message.  Refer to my previous post for details on FIPS and configuring NSS in Java.

These same features are also available in FUSE Services Framework, a freely distributed  enterprise ready distribution of CXF available with various subscription options.

It all worked in the article about NSS, TLS, and FIPS, does it all just work with WS-Security too?

In short, no.  There are a couple of important aspects/features/limitations of the Sun PKCS#11 provider that prevents things from just working with WSS4J and NSS in all cases.  If you are using NSS as the key store, any case where WSS4J needs to resolve a public certificate in the key store that is not paired with a private key causes issues.

The rundown of what works and what doesn’t work is as follows:

  1. Creating signatures with an embedded security token (e.g. an embedded BinarySecurityToken containing an X509 certificate) works
  2. Verifying signatures with an embedded security token (e.g. an embedded BinarySecurityToken containing an X509 certificate) works
  3. Creating signatures with an external security token (e.g. an Issuer Serial reference to an X509 certificate) reference works
  4. Verifying signatures with an external security token reference (e.g. an Issuer Serial reference to a X509 certificate) does not work
  5. Creating encrypted content that uses the public part of an asymmetric key pair to conceal a symmetric key does not work
  6. Verifying encrypted content works

The reason some of the above scenarios don’t work has to do with the Sun PKCS#11 provider and how it interacts with end-user certificates in the NSS database.  That is, certificates that are not paired with a private key and are not a CA certificate.

Item 4 in Appendix B of the PKCS#11 guide states:

Each certificate not part of a private key entry (as the end entity certificate) is checked whether it is trusted. If the CKA_TRUSTED attribute is true, then a KeyStore trusted certificate entry is created with the CKA_LABEL value as the KeyStore alias. If the certificate has no CKA_LABEL, or if multiple certificates share the same CKA_LABEL, then the alias is derived as described above.If the CKA_TRUSTED attribute is not supported then no trusted certificate entries are created.

This statement would indicate that in the ideal case, an alias will be added to the Java KeyStore object for each “trusted” certificate in the NSS database.  In practice, when using the NSS provider in FIPS mode, any end-user certificate that is not paired with a private key in the database and is not a CA certificate, will be excluded from the listed aliases in the Java KeyStore.  Consequently, WSS4J will not be able to perform the needed certificate look up for verification of externally referenced security tokens using the NSS provided key store.  If you need support for this type of token reference, the only option is to extend  WSS4J’s org.apache.ws.security.components.crypto.Merlin class or one of its ancestors to provide an alternate mechanism for resolving external security token references.  This is not a bug in CXF, WSS4J, or NSS.  NSS exposes the trusted end-user certificates to the JVM.  It is the Sun PKCS#11 provider that chooses not to expose the end-user certificates through the created Java KeyStore.

Putting it all together – Setting up NSS, the JVM, WSS4J, and CXF

Quick Links

This section contains a single location for links to the major resources mentioned in the Putting it all together section.

Setting up NSS and setting up the JVM

Read the sections titled Setting up NSS and Setting Up the JVM in my previous post about NSS.

Setting up WSS4J

CXF uses the WSS4J library to provide WS-Security operations over SOAP messages.  WSS4J provides configuration options that allow for the use of custom key store providers as well as algorithm sources.  By default, WSS4J installs the Bouncy Castle provider and the JuiCE provider if they are available on the classpath.  As Bouncy Castle is not a FIPS approved crypto implementation, the first step to achieving compliance is to remove the provider JAR from the class path.  The following XML fragment excludes the Bouncy Castle artifact from the Maven 2 transitive dependency resolution process.

<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-ws-security</artifactId>
  <version>${cxf.version}</version>
  <scope>runtime</scope>
  <exclusions>
    <exclusion>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcprov-jdk15</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Once Bouncy Castle is off the class path, the built-in provider can still provide algorithm implementations for some algorithms used by WSS4J.  If you are not replacing the built-in provider, WSS4J needs to be configured to retrieve its algorithm implementations from a specific provider.  The following code fragment demonstrates how to configure the provider that WSS4J will use.


JCEMapper.setProviderId("SunPKCS11-nss-client");

Line 1 in the above code fragment sets the provider name used by WSS4J when retrieving algorithm implementations.  The provider name specified is derived from the name property in the PKCS#11 configuration file that loads NSS into the JVM.  Refer to my earlier blog post for details on the contents of this file.

The configuration options above direct WSS4J to use the proper provider for retrieving algorithm implementations; however, we still need to configure WSS4J to use the NSS PKCS#11 provider as a key and trust store.  The following XML fragment represents the WSS4J configuration properties needed to use the NSS key store.


<!-- Define a Properties object with the properties required by the
 org.apache.ws.security.components.crypto.Merlin WSS4j Crypto implementation.
 This crypto config is used for signature creation and validation. -->
<util:properties id="signatureProperties">
  <!-- Defines the implementation class. -->
  <prop key="org.apache.ws.security.crypto.provider">org.apache.ws.security.components.crypto.Merlin</prop>
  <!-- Defines the location, on the classpath, of the keystore file.  Also
       takes URL or file path. Not applicable when using PKCS 11. -->
  <!-- <prop key="org.apache.ws.security.crypto.merlin.file"></prop>  -->
  <!-- The type of the keystore pointed to by org.apache.ws.security.crypto.merlin.file. -->
  <prop key="org.apache.ws.security.crypto.merlin.keystore.type">PKCS11</prop>
  <!-- The crypto provider that can load the keystore. -->
  <prop key="org.apache.ws.security.crypto.merlin.keystore.provider">SunPKCS11-nss-client</prop>
  <!-- The password for the keystore file. -->
  <prop key="org.apache.ws.security.crypto.merlin.keystore.password">Password12345!</prop>
  <!-- The alias for the default private key to use.  Not required.
  <prop key="org.apache.ws.security.crypto.merlin.keystore.alias"></prop> -->
  <prop key="org.apache.ws.security.crypto.merlin.cert.provider"></prop>
  <!-- If the JVM cacerts file contents should be loaded into the trust chain. -->
  <prop key="org.apache.ws.security.crypto.merlin.load.cacerts">false</prop>
  <!-- If the JVM cacerts file is used, the password for the file.
  <prop key="org.apache.ws.security.crypto.merlin.cacerts.password"></prop> -->
</util:properties>
  • Line 11 – This line indicates that WSS4J should use a PKCS#11 provider for its key store.
  • Line 13 – This line indicates the PKCS#11 provider name used to provide the key store.  This name matches with the name provided in the PKCS#11 configuration file used to load NSS.
  • Line 15 – This line provides the password needed to access the key store.

Running the example code

To run the example code you must have the following:

  1. Maven 2.2.1
  2. Java 1.6
  3. NSS 3.12.4

To run the example, you must first install or build NSS.  Get the binary or source distribution from the Mozilla FTP server and follow the instructions above for configuring the needed environment variables.

Create an environment variable named “NSS_HOME” and set its value to be the path to the directory containing the NSS libraries.  The example project uses this environment variable to locate your NSS installation directory and generate configuration files for the PKCS#11 provider for you.

Next, checkout the code from https://davidvaleri.googlecode.com/svn/projects/examples/cxf-wss-fips/tags/cxf-wss-fips-1.0 using a subversion client of your choice.

From the checkout folder, use a shell to execute:

 mvn -P server

From the checkout folder, use another shell to execute:

mvn -P client

The server log output is written to the file at target/surefire-reports/valeri.blog.examples.cxf_wss_fips.CustomerServiceRunner.out

Additional Options

  • You can enable JSSE log output by adding -Djsse.debug=all to either of the commands above.
  • You can modify the log output to contain more details by editing the log4j.properties file in src/test/resources.
  • You can access the service WSDL at https://localhost:8443/services/CustomerService?wsdl using a Web browser.  You will want to add the example’s CA certificate to your browser’s trust store and add the client’s identity to your client’s key store.  The .crt and .pk12 files, as well as JKS files that contain these certificates and keys are available in src/test/resources.  The password for these files is “password”.  Note that you should remove the example CA from your browser when finished to avoid a potential security threat.  The example CA key used here is available in the Subversion repository and could be used by anyone to generate additional client or server certificates for malicious purposes.  Leaving the CA in your browser’s trust store leaves you vulnerable.

What else should I know?

  1. If you are this concerned about cryptographic operations, You really should be running a security manager to prevent application code from altering the container JVM’s security configuration.
  2. You may be required to remove additional non-FIPS validated providers from the JVM to meet accreditation guidelines.
  3. This is just an example and I am not claiming that it will be 100% in-line with your security requirements.
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

6 Responses to Using NSS for FIPS 140-2 compliant message security in CXF

  1. Rotem says:

    Hi,
    I’m trying to use a biometric smart card for ws-security signing.
    I followed your example but got the exception: WSSecurityException: application provided null or empty password.
    I know I don’t pass a password, but the application asks me for my fingerprint and I supply it.
    Can it be a version support problem? (I’m using cxf 2.1.4)

    Thanks
    Rotem

    • David Valeri says:

      It could be a version related issue. Can you provide more context around where that exception is coming from? Does the example work with NSS and CXF 2.1.4?

      • Rotem says:

        I upgraded to cxf 2.4.1 (based on wss4j 1.6.1).
        I managed to solve the no password error (just created a callbackhandler setting the password to null).
        I Got a new problem now.
        The error I recieve is that the certificate with alias XXX can’t be found.
        Looking into cxf code, I found that the keystore is null.
        I get an earlier debug message saying “Merlin – The keystore is not loaded as KEY_STORE_FILE is null”.
        I don’t understand why the cxf doesn’t use the provider to create one.
        When I create a provider by myself, I manage to get the certificate from it.

  2. Rotem says:

    Solved it.
    For some reason, Merlin doesn’t like getting an empty keyStoreLocation (although, the keyStore.load method can get a null value in case of physical store).
    The only solution I found was just to extend the Merlin class and override the loadProperties method.
    It’s not the best solution, but it works.

  3. Raj says:

    Hi,

    I am trying to use this for my password encryption and decryption process.

    I need help in setup the NSSdb and environment variables on the Windows machine. I want to perform this operation in my windows computer.

    I really appreciate your help in advance.

    Thanks,
    Raj

Leave a reply to David Valeri Cancel reply