I'm trying to consume an IBM MQ (version 8.0.0.8) with SSL using a Java client built basically with:
- Oracle JKD 8 and IBM JRE 7 (for testing purposes I have one client for each of them)
- com.ibm.mq.allclient-9.1.0.0.jar
- javax.jms-api-2.0.1.jarspring-jms-4.3.7.RELEASE.jar
- spring-jms-4.3.7.RELEASE.jar
The MQ is a Request/Reply type.
I have the correct certificate and all MQ properties set, but for some reason the connection “drops” and I get no errors on my client side and my requests never get any response and keeps running “forever”, never getting any response. The only clue that I have is an error message in the MQ log that says:
Process(31600.16) User(QMQM) Jobname(JOB_NAME) Host(HOST_NAME) VRMF(8.0.0.8) QMgr(MANAGER_NAME) AMQ9638: SSL communications error for channel 'CHANNEL_NAME'. EXPLANATION: Cause . . . . . : An unexpected SSL communications error occurred for a channel, as reported in the preceding messages. The channel is 'CHANNEL_NAME';
The strange thing is that the SSL Handshake happens, my certificate is accepted by the MQ, but for some reason something happens after this. I’m trying using both Oracle JRE 8 and IBM JRE 7. Maybe is something on MQ side (IBM MQ v8.0.0.8) or some configuration that I’m missing on my side.
I have already installed the JCE Unlimited Policies, so the problem is not CipherSpec X CipherSuite.
I'm using -Djavax.net.debug=all and I can see that my certificate is being used correctly and I can't see any problems there...
My contact point on the MQ Team told me that for some reason my application is revoking the certificate (something related with CLR), but I have no idea why this would happen.
My Java code:
public Message callMQ() {
Message message = null;
try {
MQConnectionFactory factory = mqQueueConnectionFactory();
JMSContext context = factory.createContext();
Destination requestQueue = context.createQueue("queue:///REQUEST_QUEUE");
Destination replyQueue = context.createQueue("queue:///REPLY_QUEUE");
JmsTemplate jmsTemplate = new JmsTemplate(factory);
FIXMLRootInbound inbound = new FIXMLRootInbound();
String xml = XmlUtil.xmlObjectToString(inbound);
message = jmsTemplate.sendAndReceive(requestQueue,
session -> {
Message req = session.createTextMessage(xml);
req.setJMSCorrelationID(UUID.randomUUID().toString());
req.setJMSDestination(requestQueue);
req.setJMSReplyTo(replyQueue);
return req;
});
} catch (Throwable e) {
e.printStackTrace();
}
return message;
}
private MQConnectionFactory mqQueueConnectionFactory() throws NoSuchAlgorithmException, KeyStoreException,
IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, JmsException {
SSLSocketFactory sslSocketFactory = sslContext().getSocketFactory();
MQEnvironment.sslSocketFactory = sslSocketFactory;
MQEnvironment.sslCipherSuite = "TLS_RSA_WITH_AES_256_CBC_SHA";
MQEnvironment.sslFipsRequired = false;
MQConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName(host);
try {
mqQueueConnectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
mqQueueConnectionFactory.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE,
WMQConstants.WMQ_CM_CLIENT);
mqQueueConnectionFactory.setQueueManager(queueManager);
mqQueueConnectionFactory.setSSLCipherSuite("TLS_RSA_WITH_AES_256_CBC_SHA");
mqQueueConnectionFactory.setCCSID(285);
mqQueueConnectionFactory.setChannel(channel);
mqQueueConnectionFactory.setPort(port);
mqQueueConnectionFactory.setSSLSocketFactory(sslSocketFactory);
mqQueueConnectionFactory.setSSLFipsRequired(false);
} catch (Exception e) {
log.error("Error creating MQQueueConnectionFactory.", e);
}
return mqQueueConnectionFactory;
}
private SSLContext sslContext() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
try (InputStream cert = new FileInputStream("C:\\myplace\\Dev\\Certificates\\MY_KEYSTORE.jks")) {
final KeyStore caCertsKeyStore = KeyStore.getInstance("JKS");
caCertsKeyStore.load(cert, "changeit".toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
PKIXRevocationChecker rc = (PKIXRevocationChecker)cpb.getRevocationChecker();
rc.setOptions(EnumSet.of(
PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.ONLY_END_ENTITY,
PKIXRevocationChecker.Option.SOFT_FAIL,
PKIXRevocationChecker.Option.NO_FALLBACK));
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(caCertsKeyStore, new X509CertSelector());
pkixParams.addCertPathChecker(rc);
kmf.init(caCertsKeyStore, "changeit".toCharArray());
tmf.init( new CertPathTrustManagerParameters(pkixParams) );
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
return sslContext;
} catch (Exception e) {
throw new RuntimeException("Exception creating SSLContext", e);
}
}
Since you are using the 9.1.0.0
com.ibm.mq.allclient.jar
you do not need all of the code you have related to the key store, for example:You can instead replace that with setting the following two system properties, this will work with both Oracle and IBM Java:
The above settings have always worked for IBM Java, but with Oracle Java this did not work with older versions of MQ. It was fixed for Oracle java in the following versions of IBM MQ (Base 9.0 and 9.1 have the same fix):
IBM Java and Oracle Java have different CipherSuite names, these are documented in the IBM MQ v9.1 Knowledge center page "TLS CipherSpecs and CipherSuites in IBM MQ classes for JMS.
You have specified
TLS_RSA_WITH_AES_256_CBC_SHA
in the posted code, this would be theSSLCIPH
value on the MQ Queue managerSVRCONN
channel, and would map to the following CipherSuites:SSL_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
Related to the above, if you are using Oracle Java you need to set the following system property so that the MQ JMS classes will be able to use the right mapping of the Oracle CipherSuite name:
The error you provided appears to be from the IBM i MQ Queue manager, but does not provide enough information to diagnose the problem.
The error states the following:
Can you edit your question and provide the details from the "preceding messages".
You stated
It could be that the MQ queue manager is attempting itself to connect to the OCSP server specified in the your client cert's AuthorityInfoAccess (AIA) certificate extension. If MQ can not reach this OCSP server with a default configuration the connection will be denied. If you are unable to update your network to allow connection to the OCSP server then you can disable this check but note that you will not know if a cert is revoked. To disable the checking the following can be added to the queue manager's
qm.ini
fileSSL
stanza:One last comment, the CipherSuite you listed in your example code
TLS_RSA_WITH_AES_256_CBC_SHA
is a TLS1.0 CipherSuite. Like SSL before it this and TLS1.1 are commonly deprecated across many industries. I looked for reference to post and a google of "tls 1.0 end of life" gives many references.Quoting one "TLS 1.0 end-of-life on June 30th, 2018" below:
I would suggest choosing one listed as TLS1.2 in the Knowledge center page I linked to above for example
TLS_RSA_WITH_AES_256_CBC_SHA256
.