This project started off trying to get a java program to use an HSM (external keystore on a USB). This was not well documented, so I have documented my path.
I had set up my keystore on the USB HSM device as described here. This post covers the MQ side of it.
MQ provides some JMS samples.
I could use JmsBrowser using local bindings with the jms.sh script.
java -Djava.library.path=/opt/mqm/java/lib64 JmsBrowser -m QMA -d CP0000
To use a client channel (QMACLIENT) was easy, the program is configured to allow a channel name to be passed as a parameter using the -l option.
java -Djava.library.path=/opt/mqm/java/lib64 JmsBrowser -m QMA -d CP0000 -h localhost -p 1414 -l QMACLIENT
To use a TLS channel (QMAQCLIENTTLS) proved harder
java -Djava.library.path=/opt/mqm/java/lib64 JmsBrowser -m QMA -d CP0000 -h localhost -p 1414 -l QMAQCLIENTTLS
This gave me
CC=2;RC=2397;AMQ9641: Remote CipherSpec error for channel ‘QMAQCLIENTTLS’ to host ”. [3=QMAQCLIENTTLS]
It was hard to find where to specify the CipherSpec. The documentation said “Using the environment variables MQCHLLIB to specify the directory where the table is located, and MQCHLTAB to specify the file name of the table.” I tried this and got the same error.
I collected a trace using the Java option -Dcom.ibm.msg.client.commonservices.trace.status=ON , but the trace showed the cipher spec was null.
I gave up with this program and used an enhanced version of the program. JMS programs can use JNDI as a file repository of configuration information and the program can extract the information. For example you can configure Connection Factories(cf) which are MQ connections, and Q objects – which are queues.
Use JNDI to store parameters
JMS programs can use a JNDI interface to access configuration parameters stored in an external file.
I used this article to get me started.
My JMSAdmin.config had
I used the following commands to define a connection factory called CF1, using the channel, QMACLIENT, and a queue reference called MYQUEUE pointing to queue CP0000.
/opt/mqm/java/bin/JMSAdmin -v -cfg JMSAdmin.config
define cf(CF1) transport(client) channel(QMACLIENT)
define q(MYQUEUE) queue(CP0000)
I used a bash script to run the program, using the JmsJndiBrowser sample (JmsBrowser with JNDI support).
java -Djava.library.path=/opt/mqm/java/lib64 JmsJndiBrowser -i file:///home/colinpaice/mq/JNDI-Directory -c CF1 -d MYQUEUE
This failed with
JMSCMQ0001: IBM MQ call failed with compcode ‘2’ (‘MQCC_FAILED’) reason ‘2400’ (‘MQRC_UNSUPPORTED_CIPHER_SUITE’).
Which was a surprise to me. I looked in the trace, and there was nothing obviously wrong. The channel was not configured for TLS.
I used the following command in the JMS Admin tool, to specify a TLS channel and configure the cipher suite
alter cf(CF1) channel(QMAQCLIENTTLS) SSLCIPHERSUITE(ANY_TLS12)
Note. If I displayed cf(CF1) it gave me SSLCIPHERSUITE(*TLS12).
This gave me unable to find valid certification path to requested target, which was good progress because it meant the cipher spec had been accepted.
I added in the Java parameters to identify the key store and trust store
ks=”$kss $ksp $kst”
ts=”$tss $tsp $tst”
java $ks $ts -Djava.library.path=/opt/mqm/java/lib64 JmsJndiBrowser -i file:///home/colinpaice/mq/JNDI-Directory -c CF1 -d MYQUEUE
and it worked successfully.
Use the pkcs11 HSM external keystore.
I changed the file to specify the external keystore (with format pkcs11) on a USB device rather than a file on disk (format pkcs12).
but this gave
find /java.security.KeyStoreException: pkcs11 not found
java.security.NoSuchAlgorithmException: pkcs11 KeyStore not available
You have to tell Java where to find the pkcs11 code. This is configured in the java.security file. You can find it using find $JAVA_HOME/ -name java.security . This gave me the file name /usr/lib/jvm/java-8-oracle/jre/lib/security/java.security
It had the list of security providers,
and is missing an entry for pkcs11.
You can edit this file, or override it. To override it, create a file like colin.java.properties with
- .10 is the next in sequence after the .9 in the java.security file
- SunPKCS11 says use the Sun module.
- /home/colinpaice/mq/nitrokey.cfg use this configuration file for the pkcs11 device.
Tell Java to use this override file, by using the Java option -Djava.security.properties=/home/colinpaice/mq/colin.java.properties.
The configuration file name for the pkcs11 is /home/colinpaice/mq/nitrokey.cfg with content
name = nitrokey
library = /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
With these parameters the output from the JmsJndiBrowser command was
Initial context found!
No more messages
Success – and it only took me half a day! The hardest part was known what to specify for the security.provider…… parameter.