Mutual two way SSL with JMeter

Mutual / Two-Way SSL provides the same things as SSL, with the addition of authentication and non-repudiation of the client authentication, using digital signatures otherwise known as client certificates.

JMeter makes it easy to test multiple client certificates by way of the Keystore Configuration element.

Terminology

The Java Secure Socket Extension (JSSE) enables secure Internet communications, and is used by JMeter.

A keystore is simply a database of key material. Typically a keystore will refer to user's private keys. Various types of keystores are available such as PKCS12 and JKS.

A truststore is a keystore that is used when making decisions about what to trust. Typically a truststore will refer to 3rd party authorities, such as Certificate Authorities.

Using a Truststore

In general, you shouldn't need to use a custom truststore as the HTTP Client 4 implementation will use a TrustAll scheme. This can be seen in JMeter logs when hitting a remote site that uses SSL e.g.:

2014/01/13 14:03:56 INFO  - jmeter.protocol.http.sampler.HTTPHC4Impl: Setting up HTTPS TrustAll scheme  
2014/01/13 14:03:56 INFO  - jmeter.util.JsseSSLManager: Using default SSL protocol: TLS  
2014/01/13 14:03:56 INFO  - jmeter.util.JsseSSLManager: SSL session context: per-thread  
2014/01/13 14:03:56 INFO  - jmeter.util.SSLManager: JmeterKeyStore Location:  type JKS  
2014/01/13 14:03:56 INFO  - jmeter.util.SSLManager: KeyStore created OK  
2014/01/13 14:03:56 WARN  - jmeter.util.SSLManager: Keystore file not found, loading empty keystore  

If you find that JMeter is returning the following error:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:  
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:  
unable to find valid certification path to requested target  

This could be because the target site has changed certificates, or that your current version of Java doesn't recognise the root certificate authority (CA). In this case you can specify a custom truststore which you create using Java's keytool utility.

Creating a Truststore

You will first need to download a copy of the target server's root certificate. An easy way to do this is using Firefox e.g.: Page Info -> Security -> View Certificate -> Details -> Export as X.509 Certificate (PEM)

You can then use the keytool utility to import this into your own truststore e.g.:

keytool -importcert -alias mycert -file floodio.cer -keystore truststore.jks -storepass password  

Next time you use JMeter, simply specify the Java system property javax.net.ssl.trustStore e.g.:

jmeter -Djavax.net.ssl.trustStore=/var/log/flood/files/truststore.jks -t keystore.jmx  

Using a Keystore

You will most likely use a keystore if the target site requires a client certificate which uses Public Key Infrastructure (PKI). The SSL Manager configuration element lets you select a client certificate in either PKCS12 or JKS formatted keystores.

This is fine if you are testing sites with a single key, however if your load scenario involves multiple certificates, then this approach is limited, as it will only select the first key in your key store.

A better approach is to use the Keystore Configuration element.

Creating a Keystore

Lets assume you have created a CA key and certificate for the purpose of signing client certificates e.g.:

openssl genrsa -des3 -out ca.key 4096  
openssl req -new -x509 -days 365 -key ca.key -out ca.crt  

Lets also assume you have created a server key, certificate signing request and certificate e.g.:

openssl genrsa -des3 -out server.key 1024  
openssl req -new -key server.key -out server.csr  
cp server.key server.key.pass  
openssl rsa -in server.key.pass -out server.key  

For the purpose of demonstration we'll self sign the server certificate e.g.:

openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt  

Now we are ready to create the relevant client key and certificate signing request e.g.:

openssl genrsa -des3 -out client.key 1024  
openssl req -new -key client.key -out client.csr  

Using the CSR we can self sign the client certificate using our CA certificate e.g.:

openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt  

We can create a PKCS12 keystore from our client's private key and public certificate e.g.:

openssl pkcs12 -export -name tim -in client.crt -inkey client.key -out tim.p12  

All that is left is to convert this PKCS12 keystore into a JKS keystore.

keytool -importkeystore -destkeystore keystore.jks -srckeystore tim.p12 -srcstoretype pkcs12 -alias tim  

The advantage of doing this is we can repeat this process for each client key that we want to import.

Eventually we build our custom keystore which contains the client keys for which we want to authenticate. e.g.:

keytool -list -v -keystore keystore.jks  
Enter keystore password:

Keystore type: JKS  
Keystore provider: SUN

Your keystore contains 2 entries

Alias name: mikel  
Creation date: 13/01/2014  
Entry type: PrivateKeyEntry  
Certificate chain length: 1  
Certificate[1]:  
Owner: CN=flood.io, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU  
Issuer: CN=ssl.flood.io, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU  
Serial number: 1  
Valid from: Mon Jan 13 13:36:40 EST 2014 until: Tue Jan 13 13:36:40 EST 2015  
Certificate fingerprints:  
   MD5:  47:8A:4C:DB:1D:80:39:58:A0:50:C0:0A:C0:B6:54:9B
   SHA1: 17:55:47:45:1B:C1:B0:FB:CE:B7:9F:FB:55:69:D6:C4:94:77:38:15
   SHA256: 85:1B:85:88:A0:A4:D0:C7:65:87:21:BC:06:83:BD:C7:71:43:CC:CF:84:02:21:42:E3:DD:0A:E9:6C:2C:86:E6
   Signature algorithm name: SHA1withRSA
   Version: 1

*******************************************

Alias name: tim  
Creation date: 13/01/2014  
Entry type: PrivateKeyEntry  
Certificate chain length: 1  
Certificate[1]:  
Owner: CN=flood.io, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU  
Issuer: CN=ssl.flood.io, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU  
Serial number: 1  
Valid from: Mon Jan 13 13:36:40 EST 2014 until: Tue Jan 13 13:36:40 EST 2015  
Certificate fingerprints:  
   MD5:  47:8A:4C:DB:1D:80:39:58:A0:50:C0:0A:C0:B6:54:9B
   SHA1: 17:55:47:45:1B:C1:B0:FB:CE:B7:9F:FB:55:69:D6:C4:94:77:38:15
   SHA256: 85:1B:85:88:A0:A4:D0:C7:65:87:21:BC:06:83:BD:C7:71:43:CC:CF:84:02:21:42:E3:DD:0A:E9:6C:2C:86:E6
   Signature algorithm name: SHA1withRSA
   Version: 1

*******************************************

Using all of the above, we can now reference the truststore (if necessary) and keystore using multiple client certificates in our test plan. Note it is necessary to set https.use.cached.ssl.context to false in order to use multiple certificates.

jmeter -Djavax.net.ssl.trustStore=/var/log/flood/files/truststore.jks -Djavax.net.ssl.keyStore=/var/log/flood/files/keystore.jks -Djavax.net.ssl.keyStorePassword=password -Jhttps.use.cached.ssl.context=false -t keystore.jmx  

Keystore Configuration

The Variable name holding certificate alias can be used to select a different alias from your keystore for each thread / iteration. If using a CSV data set for example, you could have a list of usernames that you want to test. This would then just reference that ${username} variable fed by the CSV data set.

The alias start / end is just a zero based index that refers to the number of keys in your keystore.

If everything is configured correctly you will see something like the following in your JMeter logs:

2014/01/13 14:34:25 INFO  - jmeter.config.KeystoreConfig: Configuring Keystore with (preload:True, startIndex:0, endIndex:1, clientCertAliasVarName:'username')  
2014/01/13 14:34:25 INFO  - jmeter.util.SSLManager: JmeterKeyStore Location: /var/log/flood/files/keystore.jks type JKS  
2014/01/13 14:34:25 INFO  - jmeter.util.SSLManager: KeyStore created OK  
2014/01/13 14:34:25 INFO  - jmeter.util.SSLManager: Total of 2 aliases loaded OK from keystore  

An example of the test plan used in this demonstration can be found here.