CXF and 2-way SSL

When interacting with some clients, there is a need to add an extra level of security by applying 2-way SSL between the client and the service endpoint. This interaction is going to require using two keystores (one being the truststore and one being a basic keystore), along with CXF configuration. If you have never done work with a keystore before, no worries, the commands are pretty simple and there are some easy to use tools. My favorite site for commands is below:

The Most Common Java Keytool Keystore Commands

And if I feel lazy and want to interact with keystores via a GUI instead of the command line, I use Keystore Explorer.

The JDK defaults to using cacerts as the start location to put trust certs. The default password for the cacerts is “changeit” incase you want to practice the commands or utilizing a tool to view default certs. For simplifying upgrades to Application Servers, sometimes it is better to create a new jks that is used as the truststore. Just be aware that there might be default certificate authorities in cacerts that you might need to export/import into your new truststore. Then a parameter is added to the startup scripts of WebLogic Server to inform it that the application is using a different truststore from the default.

-Djavax.net.ssl.trustStore=ssl/CA-Trust.jks -Djavax.net.ssl.trustStorePassword=Password

There is other one important item related to Java Keystores. If you are doing more WS* capabilities or interactions with other clients and therefore need to have multiple keys for doing 2-way SSL and Digital Signatures, etc…I have to refer back to documentation about the fact that you cannot have more than one private key in a keystore:

From IBM Documentation: If you are using the default properties to configure SSL (javax.net.ssl.*), the SSL keystore should contain exactly one private key, because there is no way to specify which key will be used.

This becomes more apparent when you realize that the configuration for http:conduit does not let you supply an alias, even though configurations like Digital Signatures do. So if you have two private keys within the same keystore, you may run into an issue where your Digital Signature works but your 2-way SSL has handshake failures because it is scanning the keystore for the most immediate private key that it uses (which was meant for the Digital Signature). So the solution is just to create a separate keystore for each private key.

Once I have the keystores in place and startup scripts for registering our new truststore, then our next task is to figure out what is the necessary configuration for CXF. The best documentation for how to do security with CXF is actually from a vendor that uses CXF as a core product and publishes some documentation on how to use it…Fusesource. There document Fuse Services Framework – Security Guide is hands down the best resource I have used on the subject.

I need to configure an http:conduit in CXF. The one thing to realize as you read about the http:conduit is that if you use a wildcarding feature for http:conduit, it will apply to every service call out that occurs with that war application. So with regards to the previous link to CXF, the following is the example:

1
2
3
4
5
<http-conf:conduit name="*.http-conduit">
  <!-- you can also using the wild card to specify
       the http-conduit that you want to configure -->
    ...
</http-conf:conduit>

The problem with the “*.http-conduit” is that it is a wildcard for any service. So if you have more than one service callout and only one of those service callouts requires 2-way SSL, unfortunately 2-way SSL will be attempted on all the service callouts. The way around this is to use a different name pattern matching to configure a specific service for 2-way SSL (remember this configuration is an low level transport configuration so it is not contained within a bean id configuration for a JaxwsProxyFactoryBean service invocation). So I use other strategies such as:

1
2
3
4
5
6
7
8
9
10
11
<http-conf:conduit name="http://localhost:8080/.*">
  <!-- you can also using the reg-ex URL matching for
       the http-conduit that you want to configure -->
    ...
</http-conf:conduit>

or

<http-conf:conduit name="{http://widgets/widgetvendor.net}widgetSOAPPort.http-conduit">
    ...
</http-conf:conduit>

The reg-ex for URLs is very easy to use and can provide the necessary simplicity especially if the url differs only by an environmentally named DNS. The next step is to add the keyManagers configuration, the trustManagers configuration, and the cipherSuitesFilter configuration. The keyManagers needs the jks file path, the jks password, and the certificate password. The trustManagers just needs the jks file path and the jks password. The last piece is the cipherSuitesFilter configuration which just lists the types of ciphers that are available for inclusion or exclusion. Based on the type of ciphers that are going to be used (possibly for Digital Signatures), you may need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.

The configuration will look like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<http:conduit name="https://.*\.secure.foo.com/service">
    <http:tlsClientParameters disableCNCheck="true" secureSocketProtocol="SSL">
        <sec:keyManagers keyPassword="${secureWebService.keystore.cert.password}">
            <sec:keyStore type="JKS" password="${secureWebService.keystore.password}" file="${secureWebService.keystore.file}" />
        </sec:keyManagers>
        <sec:trustManagers>
            <sec:keyStore type="JKS" password="${truststore.password}"
                file="${truststore.file}" />
        </sec:trustManagers>
        <sec:cipherSuitesFilter>
            <!--
                these filters ensure that a ciphersuite with export-suitable or
                null encryption is used, but exclude anonymous Diffie-Hellman key
                change as this is vulnerable to man-in-the-middle attacks
        -->
            <sec:include>.*_EXPORT_.*</sec:include>
            <sec:include>.*_EXPORT1024_.*</sec:include>
            <sec:include>.*_WITH_DES_.*</sec:include>
            <sec:include>.*_WITH_NULL_.*</sec:include>
            <sec:include>.*_RSA_.*</sec:include>
            <sec:include>.*_SHA.*</sec:include>
            <sec:include>.*_MD5.*</sec:include>
            <sec:exclude>.*_DH_anon_.*</sec:exclude>
        </sec:cipherSuitesFilter>
    </http:tlsClientParameters>
    <http:client AutoRedirect="true" Connection="Keep-Alive" />
</http:conduit>

Resources related to this subject:

Client HTTP Transport (including SSL support)

Forum posts related to this subject:

What is a Pem file and how does it differ from other OpenSSL Generated Key File Formats?
http://cxf.547215.n5.nabble.com/CXF-Support-2-way-SSL-td3414301.html
http://cxf.547215.n5.nabble.com/CXF-2-4-Migration-removal-of-cxf-xml-2way-SSL-errors-td5551465.html

Share and Enjoy