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 transport layer security (TLS) in Apache CXF’s HTTP Web services. This article is also useful for describing the general steps needed to configure NSS as a security provider in a JVM.
Note: At the publishing of this article, PKCS#11 support within CXF’s HTTP/Jetty transports is only available in snapshot releases of 2.4 and 2.3.1. This feature will be available in the next official release. You can gain early access to this capability by using the nightly snapshots of the above releases. This capability will also appear in the next release of FUSE Services Framework.
The examples in this article use snapshots and therefore may stop working between now and the final release of CXF 2.3.1 and 2.4. If something doesn’t work, just post a comment.
What is this FIPS thing?
FIPS, or the Federal Information Processing Standards, is a collection of documents published by the National Institute of Standards and Technology (NIST) providing guidance on range of computer and telecommunications related topics and standards. FIPS publication 140-2, Security Requirements for Cryptographic Modules, deals specifically with cryptography related standards. Along with the requirements dictated by the publication, there is also the Cryptographic Module Validation Program (CMVP) which certifies cryptography modules against the FIPS 140-2 requirements. You can see a list of vendors with validated modules at http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/1401vend.htm. Stated simply, FIPS 140-2 compliant TLS requires using a limited set of algorithms and a validated implementation of said algorithms.
So what is the rub?
This is all fine and well if you have a hardware cryptography device, an application server, and/or a commercial software library that provides a FIPS 140-2 validated cryptography module. But what if, on the off chance, you are working on a project with no budget or you don’t have one of the aforementioned solutions? Therein lies the rub.
Java and NSS to the rescue
Starting in Java 5, several changes to the cryptographic APIs made it possible to have a Java application interact with tokens and algorithms provided through PKCS#11, the Cryptographic Token Interface Standard. For more details about the use of the Sun PKCS#11 provider and how to interact with PKCS#11 tokens/algorithms, refer to the Java PKCS#11 Reference Guide. But JVM PKCS#11 support is only half of the equation. In order to be FIPS 140-2 compliant, we still need a PKCS#11 implementation that has been through the CMVP. This is where Network Security Services (NSS) comes in. NSS is a PKCS#11 compliant cryptographic library from Mozilla that also happens to have passed through the CMVP. NSS is distributed under the GPL, LGPL, and the MPL. You can view the CMVP information for NSS on the NIST Web site.
Putting it all together – Setting up NSS, the JVM, JSSE, and CXF
Quick Links
This section contains a single location for links to the major resources mentioned in the Putting it all together section.
- NSS Source and Binary Download – ftp://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_12_4_RTM/
- NSS Utility Documentation – modutil, certutil, and pk12util
- Example Source Code SVN Location – https://davidvaleri.googlecode.com/svn/projects/examples/cxf-tls-fips/branches/cxf-tls-fips-0.x
- Java PKCS#11 Reference Guide – Java PKCS#11 Reference Guide
Setting up NSS
The first step in putting all the pieces together is to configure NSS. NSS is a native library which means you will either need to compile it from source or download a compiled binary for your platform. The latest version to appear in the NIST CMVP list is 3.12.4. You can find compiled binaries and source for 3.12.4 at ftp://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_12_4_RTM/.
Once you have built the binaries, or extracted them from the downloaded archive, you will want to 1) add the NSS libraries to the LD_LIBRARY_PATH or PATH environment variable depending on your operating system and 2) add the NSS utilities to the PATH environment variable. Once installed, you will use the modutil, certutil, and pk12util utilities to create a database and populate it with key information. Note that NSS can also be used purely as an algorithm provider without a database. Refer to the Java PKCS#11 Reference Guide for more details on alternative usage modes.
The following sequence of commands illustrates how to create the database, enable FIPS compliance, and populate the database with some certificates and keys.
-
modutil -create -dbdir ./client
-
modutil -fips true -dbdir ./client
-
modutil -changepw "NSS FIPS 140-2 Certificate DB" -dbdir ./client
-
certutil -A -d ./client -t "TCu,,TCu" -n "Test CA 1" -i ./pki/test-ca-1.crt
-
pk12util -i ./pki/test-user-1.p12 -n test-user-1 -d ./client
Command 1 creates a new database in the client directory relative to where the command is executed. Command 2 enables FIPS compliant mode in the database. Command 3 changes the password of the FIPS token. Keep in mind that there are password complexity requirements when FIPS mode is enabled and that the utility will simply report that it was unable to set the password when these minimum complexity requirements are not met. Command 4 imports a CA certificate into the database under the alias “Test CA 1″ and sets the trusted usages for the certificate. Command 5 imports a private key and certificate into the database under the alias “test-user-1″. This sequence of commands can be used to create and populate a database for use on a client or server. The resulting database can be seen in the example source code in the src/test/pkcs11/client folder. The password for the database is “Password12345!”.
Setting up the JVM
Once NSS is installed and an NSS database exists for your client/server, the next step is to configure a JVM to use it. Java 1.6 provides great support for NSS and will be used exclusively in this example. One may configure a PKCS#11 provider in the JVM dynamically at runtime or statically in the JVM configuration files. We will explore both approaches.
Before we can create a PKCS#11 provider in the JVM, we must first create a configuration file for the provider. The configuration file is a simple text file containing key-value pairs for the relevant configuration options. There are many options described in the Java PKCS#11 Reference Guide, but this example only needs to use a few. The example configuration below provides a name for the provider, the location of the library files for NSS and the path to the database that was created earlier.
name = nss-client nssLibraryDirectory = /path/to/nss/lib nssSecmodDirectory = /path/to/db/dir
Dynamic configuration is performed using the static utility methods of java.security.Security. The following code fragment from the example source code illustrates how to add the PKCS#11 provider and to reconfigure the Java Secure Socket Extension (JSSE) provider to leverage the PKCS#11 provider for FIPS compliant operations. Note that this code works on the Sun JVM. Your mileage will vary if you use a different JSSE provider in your JVM or a non-Sun JVM.
Provider nss = new sun.security.pkcs11.SunPKCS11(configFile); Security.addProvider(nss); int sunJssePosition = -1; int currentIndex = 0; for (Provider provider : Security.getProviders()) { if ("SunJSSE".equals(provider.getName())) { sunJssePosition = currentIndex + 1; break; } currentIndex++; } Security.removeProvider("SunJSSE"); Provider sunJsse = new com.sun.net.ssl.internal.ssl.Provider(nss); if (sunJssePosition == -1) { Security.addProvider(sunJsse); } else { Security.insertProviderAt(sunJsse, sunJssePosition); }
The constructor on line 1 creates an instance of the PKCS#11 provider using the configuration file specified by the configFile variable. This variable represents the file system path to the NSS configuration file described earlier. Note that the above code must be executed before any network connections using JSSE are established and that the above code requires the correct permissions when a security manager is in place. For these reasons, dynamic configuration should usually be avoided when running inside of a container or framework such as OSGi and JEE. Note that the example that accompanies this article uses dynamic configuration simply to reduce the amount of setup required to run the example.
Static configuration is possible at the JVM level. The $JAVA_HOME/lib/security/java.security file contains definitions of the providers that will be started with the JVM. The file will have a section resembling the following example.
security.provider.1=sun.security.provider.Sun security.provider.2=sun.security.rsa.SunRsaSign security.provider.3=com.sun.net.ssl.internal.ssl.Provider security.provider.4=com.sun.crypto.provider.SunJCE security.provider.5=sun.security.jgss.SunProvider security.provider.6=com.sun.security.sasl.Provider security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI security.provider.8=sun.security.smartcardio.SunPCSC security.provider.9=sun.security.mscapi.SunMSCAPI
To enable the PKCS#11 provider and reconfigure the JSSE provider, change the configuration file to resemble the following.
security.provider.1=sun.security.provider.Sun security.provider.2=sun.security.rsa.SunRsaSign security.provider.3=com.sun.net.ssl.internal.ssl.Provider SunPKCS11-nss-client security.provider.4=com.sun.crypto.provider.SunJCE security.provider.5=sun.security.jgss.SunProvider security.provider.6=com.sun.security.sasl.Provider security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI security.provider.8=sun.security.smartcardio.SunPCSC security.provider.9=sun.security.mscapi.SunMSCAPI security.provider.10=sun.security.pkcs11.SunPKCS11 /path/to/nss/config/file
In the above example, the last line adds the PKCS#11 provider with the appropriate configuration file while the third line configures the JSSE provider to use the PKCS#11 provider for cryptographic operations. The value “SunPKCS11-nss-client” was created by concatenating “SunPKCS11-” with the value of the name property from the PKCS#11 configuration file. In the example configuration file above, the name is nss-client, resulting in the provider name “SunPKCS11-nss-client”.
Configuring the CXF HTTP Transport
For the server-side configuration, we will be using the PKCS#11 provider with the name configuration attribute of “nss-server”. The configuration uses the CXF Jetty Transport’s Spring namespace handler for configuration. More details about this configuration mechanism can be found in the CXF Jetty Transport documentation. The relevant lines are described after the following XML fragment. Click here to view this fragment in context.
<!-- Define the Jetty HTTP server configuration for the CXF bus. --> <httpj:engine-factory bus="cxf"> <httpj:identifiedTLSServerParameters id="secure"> <httpj:tlsServerParameters secureSocketProtocol="TLS"> <sec:clientAuthentication required="true" want="true"/> <!-- Define the keystore for the server identity as a classpath resource. In a production system, these properties would be encrypted/obfuscated using a library such as Jasypt (http://www.jasypt.org/) and Spring's property placeholder capabilities. --> <sec:keyManagers> <sec:keyStore type="PKCS11" provider="SunPKCS11-nss-server" password="Password12345!"/> </sec:keyManagers> <sec:trustManagers> <!-- In a production system, these properties would be encrypted/obfuscated using a library such as Jasypt (http://www.jasypt.org/) and Spring's property placeholder capabilities. --> <sec:keyStore type="PKCS11" provider="SunPKCS11-nss-server" password="Password12345!"/> </sec:trustManagers> <sec:cipherSuitesFilter> <sec:include>SSL_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> <sec:include>SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> <sec:include>TLS_RSA_WITH_AES_128_CBC_SHA</sec:include> <sec:include>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</sec:include> <sec:include>TLS_DHE_RSA_WITH_AES_128_CBC_SHA</sec:include> <sec:include>TLS_RSA_WITH_AES_256_CBC_SHA</sec:include> <sec:include>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</sec:include> <sec:include>TLS_DHE_RSA_WITH_AES_256_CBC_SHA</sec:include> <!-- The suites below have been disabled due to http://bugs.sun.com/view_bug.do?bug_id=6763530 --> <!-- <sec:include>TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_RSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_RSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_anon_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_anon_WITH_AES_256_CBC_SHA</sec:include> --> </sec:cipherSuitesFilter> </httpj:tlsServerParameters> </httpj:identifiedTLSServerParameters> <!-- Define the HTTPS port, crypto configuration, and other properties. --> <httpj:engine port="8443"> <httpj:tlsServerParametersRef id="secure" /> <httpj:threadingParameters minThreads="5" maxThreads="15" /> </httpj:engine> </httpj:engine-factory>
- Line 4 – This line’s secureSocketProtocol attribute identifies TLS as the desired protocol. TLS is the only protocol available when in FIPS mode.
- Line 5 – This line specifies that mutual authentication is required. Not all secure HTTP connections need to use mutual authentication, but this example uses it to demonstrate configuring a key store on the client side as well as on the server side.
- Line 11 – This line specifies the type of key store in use and how to access it for the server’s identity. Typically you use a JKS file, but in this example we are using the NSS database accessed through the PKCS#11 provider configured against the NSS database. Note the provider name identifies the PKCS#11 provider configuration we created for the server NSS database.
- Line 18 – This line specifies the type of key store in use and how to access it for the server’s trust configuration. Typically you use a JKS file, but in this example we are using the NSS database accessed through the PKCS#11 provider configured against the NSS database. Note the provider name identifies the PKCS#11 provider configuration we created for the server NSS database.
- Line 21 – This line and the nested XML content identifies a white list of cipher suites that will be enabled if available. When configured the JSSE is configured against NSS operating in FIPS mode, only FIPS approved cipher suites will be available; however, some cipher suites trigger issues with the PKCS#11 support in the JVM and are disabled for compatibility reasons. This configuration is shown as a white list, but could just as easily be configured as a black list using the <sec:exclude> element.
For the client-side configuration, we will be using the PKCS#11 provider with the name configuration attribute of “nss-client”. The configuration uses the CXF HTTP Transport’s Spring namespace handler for configuration. More details about this configuration mechanism can be found in the CXF HTTP Transport documentation. The relevant lines are described after the following XML fragment. Click here to view this fragment in context.
<!-- Define the HTTP client configuration for all target service invocations. --> <http:conduit name="*.http-conduit"> <!-- Define the SSL info for the client connections. --> <http:tlsClientParameters secureSocketProtocol="TLS"> <!-- Defines the identity info for the client's connection. --> <sec:keyManagers> <!-- In a production system, these properties would be encrypted/obfuscated using a library such as Jasypt (http://www.jasypt.org/) and Spring's property placeholder capabilities. --> <sec:keyStore type="PKCS11" provider="SunPKCS11-nss-client" password="Password12345!" /> </sec:keyManagers> <!-- Defines the trust info for the client's connection. --> <sec:trustManagers> <!-- In a production system, these properties would be encrypted/obfuscated using a library such as Jasypt (http://www.jasypt.org/) and Spring's property placeholder capabilities. --> <sec:keyStore type="PKCS11" provider="SunPKCS11-nss-client" password="Password12345!" /> </sec:trustManagers> <sec:cipherSuitesFilter> <sec:include>SSL_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> <sec:include>SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> <sec:include>TLS_RSA_WITH_AES_128_CBC_SHA</sec:include> <sec:include>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</sec:include> <sec:include>TLS_DHE_RSA_WITH_AES_128_CBC_SHA</sec:include> <sec:include>TLS_RSA_WITH_AES_256_CBC_SHA</sec:include> <sec:include>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</sec:include> <sec:include>TLS_DHE_RSA_WITH_AES_256_CBC_SHA</sec:include> <!-- The suites below have been disabled due to http://bugs.sun.com/view_bug.do?bug_id=6763530 --> <!-- <sec:include>TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_RSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_RSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_anon_WITH_AES_128_CBC_SHA</sec:include> --> <!-- <sec:include>TLS_ECDH_anon_WITH_AES_256_CBC_SHA</sec:include> --> </sec:cipherSuitesFilter> </http:tlsClientParameters> <http:client Connection="Keep-Alive" /> </http:conduit>
- Line 4 – This line’s secureSocketProtocol attribute identifies TLS as the desired protocol. TLS is the only protocol available when in FIPS mode.
- Line 10 – This line specifies the type of key store in use and how to access it for the client’s identity. Typically you use a JKS file, but in this example we are using the NSS database accessed through the PKCS#11 provider configured against the NSS database. Note the provider name identifies the PKCS#11 provider configuration we created for the client NSS database. Note that the key manager configuration is not necessary on clients not using mutual authentication.
- Line 18 – This line specifies the type of key store in use and how to access it for the client’s trust configuration. Typically you use a JKS file, but in this example we are using the NSS database accessed through the PKCS#11 provider configured against the NSS database. Note the provider name identifies the PKCS#11 provider configuration we created for the server NSS database.
- Line 21 – This line and the nested XML content identifies a white list of cipher suites that will be enabled if available. When configured the JSSE is configured against NSS operating in FIPS mode, only FIPS approved cipher suites will be available; however, some cipher suites trigger issues with the PKCS#11 support in the JVM and are disabled for compatibility reasons. This configuration is shown as a white list, but could just as easily be configured as a black list using the <sec:exclude> element.
Running the example code
To run the example code you must have the following:
- Maven 2.2.1
- Java 1.6
- 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-tls-fips/branches/cxf-tls-fips-0.x 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_tls_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?
- 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.
- You may be required to remove additional non-FIPS validated providers from the JVM to meet accreditation guidelines.
- This is just an example and I am not claiming that it will be 100% in-line with your security requirements.
- Line 4 – This line’s secureSocketProtocol attribute identifies TLS as the desired protocol. TLS is the only protocol available when in FIPS mode.
- Line 5 – This line specifies that mutual authentication is required. Not all secure HTTP connections need to use mutual authentication, but this example uses it to demonstrate configuring a key store on the client side as well as on the server side.
- Line 11 – This line specifies the type of key store in use and how to access it for the server’s identity. Typically you use a JKS file, but in this example we are using the NSS database accessed through the PKCS#11 provider configured against the NSS database. Note the provider name identifies the PKCS#11 provider configuration we created for the server NSS database.
- Line 18 – This line specifies the type of key store in use and how to access it for the server’s trust configuration. Typically you use a JKS file, but in this example we are using the NSS database accessed through the PKCS#11 provider configured against the NSS database. Note the provider name identifies the PKCS#11 provider configuration we created for the server NSS database.
- Line 21 – This line and the nested XML content identifies a white list of cipher suites that will be enabled if available. When configured the JSSE is configured against NSS operating in FIPS mode, only FIPS approved cipher suites will be available; however, some cipher suites trigger issues with the PKCS#11 support in the JVM and are disabled for compatibility reasons. This configuration is show as a white list, but could just as easily be configured as a black list using the <sec:exclude> element.
