SSL Support
The Grinder 3 supports the use of SSL by scripts.
The Grinder 3 implements SSL using the Java Secure Socket
Extension (JSSE) included in the Java run time. When used with
the HTTP Plug-in, this is as simple as using https
instead of http
in URIs. Scripts can obtain a suitable
SSLContext
and hence a SSLSocketFactory
for non-HTTP use cases, and can control the allocation of SSL
sessions to worker threads.
Before we begin
Performance
Simulating multiple SSL sessions on a single test machine may or may not be realistic. A typical browser running on a desktop PC has the benefit of a powerful CPU to run the SSL cryptography. Be careful that your results aren't constrained due to inadequate test client CPU power.
The Grinder's SSL implementation is not secure
To reduce the client side performance overhead, The Grinder deliberately accelerates SSL initialisation by using a random number generator that is seeded with a fixed number. Further, no validation of server certificates is performed. Neither of these hinder SSL communication, but they do make it less secure.
This acceleration affects initialisation time only and should not affect timing information obtained using The Grinder.
Controlling when new SSL sessions are created
By default The Grinder creates a new SSL session for each run
carried out by each worker thread. This is in line with the usual
convention of simulating a user session with a worker thread
executing the part of the script defined by
TestRunner.__call__()
.
Alternatively, scripts may wish to have an SSL session per
worker thread, i.e. for each thread to reuse SSL sessions on
subsequent executions of TestRunner.__call__()
. This
can be done with the
SSLControl.setShareContextBetweenRuns()
method:
from net.grinder.script.Grinder import grinder grinder.SSLControl.shareContextBetweenRuns = 1
This will cause each worker thread to reuse SSL sessions
between runs. SSL sessions will still not be shared between worker
threads. Calling setShareContextBetweenRuns()
affects
all of the worker threads.
Using client certificates
If a server requests or requires a client certificate, The Grinder must have some way of providing one - this involves specifying a key store.
from net.grinder.script.Grinder import grinder class TestRunner: def __call__(self): grinder.SSLControl.setKeyStoreFile("mykeystore.jks", "passphrase")
It is only valid to use setKeyStoreFile
from a
worker thread, and it only affects that worker thread.
There is also a method called setKeyStore
which
takes a java.io.InputStream
which may be useful if
your key store doesn't live on the local file system. Both methods
have an overloaded version that allows the key store type to be
specified, otherwise the default type is used (normally
jks
).
Whenever setKeyStoreFile
,
setKeyStore
, or setKeyManagers
(see
below) is called, the current SSL session for the thread is
discarded. Consequently, you usually want to call these methods at
the beginning of your __call__()
method or from the
TestRunner.__init__()
constructor. Setting the
thread's key store in TestRunner.__init__()
is
especially recommended if you calling
setShareContextBetweenRuns(true)
to share SSL
sessions between runs.
FAQ
The astute reader who is familiar with key stores may have a few questions. Here's a mini FAQ:
-
If I have several suitable certificates in my key store, how does The Grinder chose between them?
The Grinder relies on the JVM's default
KeyManager
implementations. This picks a certificate from the store based on SSL negotiation with the server. If there are several suitable certificates, the only way to control which is used is to provide your ownKeyManager
. -
setKeyStoreFile
has a parameter for the key store password. What about the pass phrase that protects the private key in the key store?The pass phrases for keys must be the same as the key store password. This is a restriction of the default
KeyManager
s. If you don't like this, you can provide your ownKeyManager
. -
Shouldn't I need to specify a set of certificates for trusted Certificate Authorities?
No. The Grinder does not validate certificates received from the server, so does not need a set of CA certificates.
-
Can I use the properties
javax.net.ssl.keyStore
,javax.net.ssl.keyStoreType
, andjavax.net.ssl.keyStorePassword
to specify a global keystore?No. The Grinder does not use these properties, primarily because the JSSE does not provide a way to access its default SSLContext.
Picking a certificate from a key store [Advanced]
Here's an example script that provides its own
X509KeyManager
implementation which controls which
client certificate to use. The example is hard coded to always use
the certificate with the alias myalias
.
from com.sun.net.ssl import KeyManagerFactory,X509KeyManager from java.io import FileInputStream from java.security import KeyStore from jarray import array class MyManager(X509KeyManager): def __init__(self, keyStoreFile, keyStorePassword): keyStore = KeyStore.getInstance("jks") keyStore.load(FileInputStream(keyStoreFile), keyStorePassword) keyManagerFactory = \ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) keyManagerFactory.init(keyStore, keyStorePassword) # Assume we have one key manager. self._delegate = keyManagerFactory.keyManagers[0] def __getattr__(self, a): """Some Python magic to pass on all invocations of methods we don't define on to our delegate.""" if self.__dict__.has_key(a): return self.__dict__[a] else: return getattr(self._delegate, a) def chooseClientAlias(self, keyTypes, issuers): return "myalias" myManager = MyManager("keystore.jks", "password") myManagerArray = array((myManager,), X509KeyManager) class TestRunner: def __call__(self): grinder.SSLControl.setKeyManagers(myManagerArray) # ...
Debugging
When debugging SSL interactions, you may find it useful to set
the following in grinder.properties
.
grinder.jvm.arguments=-Djavax.net.debug=ssl # or -Djavax.net.debug=all