Mutual two way SSL with JMeter

Using keystore in JMeter load testing scripts

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.:

bash
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:

bash
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.:

bash
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.:

bash
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.:

bash
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.:

bash
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.:

bash
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.:

bash
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.:

bash
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.:

bash
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.

bash
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.:

bash
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.

bash
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:

bash
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.

Start load testing now

It only takes 30 seconds to create an account, and get access to our free-tier to begin load testing without any risk.

Keep reading: related stories
Return to the Flood Blog