java - 将 Java 客户端 (JMS) 连接到 IBM MQ 时出现问题
问题描述
我正在尝试使用基本上使用以下内容构建的 Java 客户端来使用带有 SSL 的 IBM MQ(版本 8.0.0.8):
- Oracle JKD 8 和 IBM JRE 7(出于测试目的,我每个都有一个客户端)
- 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
MQ 是请求/回复类型。
我设置了正确的证书和所有 MQ 属性,但由于某种原因,连接“断开”并且我在客户端没有收到任何错误,并且我的请求从未得到任何响应,并且一直“永远”运行,从未得到任何响应。我唯一的线索是 MQ 日志中的一条错误消息,上面写着:
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';
奇怪的是发生了 SSL 握手,我的证书被 MQ 接受,但由于某种原因,此后发生了一些事情。我正在尝试同时使用 Oracle JRE 8 和 IBM JRE 7。也许是 MQ 方面的东西(IBM MQ v8.0.0.8)或者我缺少的一些配置。
我已经安装了 JCE Unlimited Policies,所以问题不在于 CipherSpec X CipherSuite。
我正在使用 -Djavax.net.debug=all 并且我可以看到我的证书正在正确使用并且我看不到任何问题......
我在 MQ 团队的联系人告诉我,由于某种原因,我的应用程序正在吊销证书(与 CLR 相关),但我不知道为什么会发生这种情况。
我的Java代码:
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);
}
}
解决方案
由于您使用的是 9.1.0.0,因此com.ibm.mq.allclient.jar
您不需要所有与密钥库相关的代码,例如:
SSLSocketFactory sslSocketFactory = sslContext().getSocketFactory();
//Note that MQEnvironment is used with IBM MQ Classes for Java not IBM MQ Classes for JMS
MQEnvironment.sslSocketFactory = sslSocketFactory;
MQEnvironment.sslCipherSuite = "TLS_RSA_WITH_AES_256_CBC_SHA";
MQEnvironment.sslFipsRequired = false;
mqQueueConnectionFactory.setSSLSocketFactory(sslSocketFactory);
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);
}
}
您可以改为设置以下两个系统属性来替换它,这将适用于 Oracle 和 IBM Java:
System.setProperty("javax.net.ssl.keyStore", "C:\\myplace\\Dev\\Certificates\\MY_KEYSTORE.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
上述设置一直适用于 IBM Java,但对于 Oracle Java,这不适用于旧版本的 MQ。它已在以下 IBM MQ 版本中针对 Oracle java 进行了修复(Base 9.0 和 9.1 具有相同的修复):
Version Maintenance Level v7.1 7.1.0.8 v7.5 7.5.0.7 v8.0 8.0.0.5
IBM Java 和 Oracle Java 具有不同的 CipherSuite 名称,这些名称记录在 IBM MQ v9.1 知识中心页面“ TLS CipherSpecs and CipherSuites in IBM MQ classes for JMS中。
您已TLS_RSA_WITH_AES_256_CBC_SHA
在发布的代码中指定,这将是SSLCIPH
MQ 队列管理器SVRCONN
通道上的值,并将映射到以下 CipherSuites:
- IBM Java:
SSL_RSA_WITH_AES_256_CBC_SHA
- 甲骨文Java:
TLS_RSA_WITH_AES_256_CBC_SHA
与上述相关,如果您使用的是 Oracle Java,则需要设置以下系统属性,以便 MQ JMS 类能够使用 Oracle CipherSuite 名称的正确映射:
System.setProperty("com.ibm.mq.cfg.useIBMCipherMappings", "false");
您提供的错误似乎来自 IBM i MQ 队列管理器,但没有提供足够的信息来诊断问题。
该错误指出以下内容:
An unexpected SSL communications error occurred for a channel, as reported in the preceding messages.
您能否编辑您的问题并提供“先前消息”中的详细信息。
你说
出于某种原因,我的应用程序正在吊销证书(与 CLR 相关的东西)
可能是 MQ 队列管理器正在尝试自己连接到客户端证书的 AuthorityInfoAccess (AIA) 证书扩展中指定的 OCSP 服务器。如果 MQ 无法使用默认配置访问此 OCSP 服务器,则连接将被拒绝。如果您无法更新您的网络以允许连接到 OCSP 服务器,那么您可以禁用此检查,但请注意您将不知道证书是否被吊销。要禁用检查,可以将以下内容添加到队列管理器的qm.ini
文件SSL
节中:
SSL:
OCSPAuthentication=Optional
OCSPCheckExtensions=no
最后一条评论,您在示例代码中列出的TLS_RSA_WITH_AES_256_CBC_SHA
CipherSuite 是 TLS1.0 CipherSuite。就像之前的 SSL 和 TLS1.1 一样,在许多行业中通常不推荐使用。我寻找帖子的参考,“ tls 1.0 end of life ”的谷歌提供了许多参考。
下面引用一个“ TLS 1.0 于 2018 年 6 月 30 日终止生命周期”:
截止日期 PCI 委员会负责确定何时淘汰旧协议。他们最初决定 TLS 1.0 将于 2016 年 6 月 30 日结束生命周期,后来将日期延长至 2018 年 6 月 30 日。最后期限已经过去,所有 Web 服务器、Web 浏览器、Web 软件和电子邮件应用程序都不得不停止对 TLS 1.0 的支持,或违反重要的安全更新。
例如,我建议在我上面链接到的知识中心页面中选择一个列为 TLS1.2 的TLS_RSA_WITH_AES_256_CBC_SHA256
。
推荐阅读
- swift - Swift 5:通过包管理器指定 GRPC 的导入路径
- flutter - 在 Flutter 的列中单击同一页面上的按钮返回一个新的小部件
- c# - 如何将 dbUrl、其他常量设置为 .net core 2.2 项目的环境变量以及如何在我的 .net core 2.2 应用程序中读取这些内容?
- angular - 从活动子路由获取数据
- javascript - 如何使用波浪号版本标签通过 CLI 将 NPM 包降级到较旧的次要版本?
- java - 有没有办法从 HashMap 中获取 HashMap 的 ArrayList 的值列表?
- python - fillna 不填充空值
- node.js - 查询节点 process.send 功能和影响
- swift - 由关节连接的两个物理体之间的拉力
- typescript - Any way to format angle brackets in IDEA coding style?