This is part of a series of blog posts which will cover features of IBM MQ that I happen to be using this week. I spend a lot of time using MQ, and not everything counts as an MQ Little Gem, or a Quirk, but is still interesting to hear about. Read other posts in this series.
Those of you who know me, know that, while I am an MQ expert, I'm not much of a Java expert. I know enough to get by and run some JMS sample programs to connect to MQ, but I don't do much beyond that. So when I came across a behaviour that didn't work as I expected, I wasn't surprised to discover that there was something in the Java world that I didn't know! I also wasn't sure whether someone more skilled in the art would think it obvious, but assuming there are others out there in the MQ world in a similar position to me, I thought I would share my lack of expertise and what I learned in the hope of helping someone else.
Setting the scene
In the past, whenever I have needed to run a TLS protected JMS application, I have both my personal certificate and my signer certificates in the same JKS file - because I come from a 'C' world where that is what you do with your CMS key database file. In the Java world, the JVM is happy to have those in separate keystores. Therefore when you run your application you tell the JVM where to find both the keyStore (containing your personal certificate) and the trustStore (containing various signer certificates) even though they both point to the same file.
Here's an example of such a command:
java -Djavax.net.ssl.trustStore=%CD%\MQGem.jks -Djavax.net.ssl.keyStore=%CD%\MQGem.jks -Djavax.net.ssl.keyStorePassword=passw0rd JmsJndiConsumer -i file:/%CD% -c MQGEM-CCDT -d MQGEM-Q
You'll notice in this invocation that I only specify the password once, for the keyStore. This always made sense to me because it was the same file, so I imagined that the JVM was clever enough to realise that it already had the password for this file, and to use it for the trustStore too. However, this turns out not to be the reason this works (more later).
Trying out PKCS#12
Prompted by Rob Parker's recent blog post, I decided to have a play with PKCS#12 keystores, which included playing with them for JMS applications, even though actually they have been available to use in Java for longer than the equivalent for 'C' MQ applications.
I produced a PKCS#12 keystore containing both my personal certificate and my signer certificate and used it successfully with my 'C' application, then attempted to use the same with my JMS application. After all, producing one keystore resource that can be used by any application regardless of language is one of the benefits of making use of a PKCS#12 keystore.
I invoked the JMS application just as above when I was using the JKS keystore but instead using my PKCS#12 file:
java -Djavax.net.ssl.trustStore=%CD%\MQGem.p12 -Djavax.net.ssl.keyStore=%CD%\MQGem.p12 -Djavax.net.ssl.keyStorePassword=passw0rd JmsJndiConsumer -i file:/%CD% -c MQGEM-CCDT -d MQGEM-Q
This resulted in a failure along these lines:-
com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2397' ('MQRC_JSSE_ERROR').
com.ibm.mq.jmqi.JmqiException: CC=2;RC=2397;AMQ9204: Connection to host 'localhost(1701)' rejected. [1=com.ibm.mq.jmqi.JmqiException[CC=2;RC=2397;AMQ9771: SSL handshake failed. [1=javax.net.ssl.SSLException[java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty],3=localhost/127.0.0.1:1701 (localhost),4=SSLSocket.startHandshake,5=default]],3=localhost(1701)
,4=,5=RemoteTCPConnection.protocolConnect]
com.ibm.mq.jmqi.JmqiException: CC=2;RC=2397;AMQ9771: SSL handshake failed. [1=javax.net.ssl.SSLException[java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty],3=localhost/127.0.0.1:1701 (localhost),4=SSLSocket.startHandshake,5=default]
javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
I learned this meant that it didn't think I had supplied a trustStore, when actually I simply had not supplied the password to the trustStore. When I added in the second password to the invocation thus:-
java -Djavax.net.ssl.trustStore=%CD%\MQGem.p12 -Djavax.net.ssl.keyStore=%CD%\MQGem.p12 -Djavax.net.ssl.trustStorePassword=passw0rd -Djavax.net.ssl.keyStorePassword=passw0rd JmsJndiConsumer -i file:/%CD% -c MQGEM-CCDT -d MQGEM-Q
Then it was happy and the application ran successfully.
I tried running with only one password and supplying it as the trustStorePassword instead of as the keyStorePassword, and now the application ran without sending its personal certificate to the queue manager. In other words, it acted like it didn't think I had supplied a keyStore, when actually I simply had not supplied the password to the keyStore. That meant I saw this error in my queue manager error log, because my channel was configured with SSLCAUTH(REQUIRED)
.
AMQ9637E: During handshake, the remote partner sent no certificate.
EXPLANATION:
The conversation cannot begin because a certificate has not been supplied by
the remote partner.
The channel name is 'MQGEM101.SSL'.
The remote host is 'localhost (127.0.0.1)'.
If this error message is written on the receiving side of the channel, then the
channel attributes 'SSLCAUTH' caused the check to be made.
ACTION:
Look at the key repository on the remote side of this channel, and make sure
the appropriate certificates are present, with correct labels.
So, when you use a PKCS#12 keystore you must supply the password for both keystores that it is playing the role of. If you are using it as both trustStore and keyStore, you must supply the password for both as well even if they are the same file.
What about JKS then?
This led me to wonder what was going on with the same invocation when using JKS? So I created two separate JKS files, one for my trustStore and one for my keyStore and gave them different passwords. I discovered that invoking my application with only the keyStore password, as follows, still worked.
java -Djavax.net.ssl.trustStore=%CD%\MQGemTrust.jks -Djavax.net.ssl.keyStore=%CD%\MQGemKey.jks -Djavax.net.ssl.keyStorePassword=passw0rd JmsJndiConsumer -i file:/%CD% -c MQGEM-CCDT -d MQGEM-Q
Even though the keyStore password was not now the same as the trustStore password. If I supplied an invalid password for the trustStore, then the application failed.
I found that it was possible to list the contents of my JKS keystores without supplying the password. I would use the following command (MQ V9.4.0):
runmqktool -list -keystore MQGemKey.jks
Then when it prompted me for a password, I just ignored it and pressed enter. It would list the contents with a warning banner at the beginning as follows:-
***************** WARNING WARNING WARNING *****************
* The integrity of the information stored in the keystore *
* has NOT been verified! In order to verify its integrity, *
* you must provide the srckeystore password. *
***************** WARNING WARNING WARNING *****************
Keystore type: jks
Keystore provider: IBMJCE
Your keystore contains 1 entry
clientcert, 2/09/2024, keyEntry,
Certificate fingerprint (SHA1): 68:56:7D:49:35:4E:D3:5E:74:8E:95:63:0F:BA:27:EE:B0:5D:4C:9F
Password Assumptions
So it seems my assumption, made years ago, that the JVM was being super helpful and not requiring you to type in the password twice when you were using the same file for both JKS key stores was incorrect. In fact, you don't need to supply a password for the JKS trust store if all you are doing is reading the certificate from it. This behaviour doesn't seem to be true for PKCS#12 key stores however, so get used to providing two passwords, one for the key store and one for the trust store, even if they are the same file.
I created my PKCS#12 key store using runmqakm
because my initial goal was to use it with a 'C' application. Then I took that exact file and used it with my JMS application. If you use different tools to create the PKCS#12 file, you may have different results. I have seen mention of password-less PKCS#12, but I wouldn't want to use that when my personal certificate and key are stored in this PKCS#12 file.
I hope that the error messages I have shown will work as web search hits for anyone else who was running under the same false assumptions as me, and might lead them to the solution sooner. If that happens, this blog post will have done its job. For the rest of you, I hope it was an interesting read.
I would be interested to learn from those with more Java expertise than me, some of the background around these differences.
#Using-MQ-feature
#IBMMQ
#IBMChampion