java - 无法使用 Spring Boot 连接到具有多个 SSL 证书的多个 IBM MQ 通道
问题描述
无法连接到具有多个证书的多个IBM MQ
通道,并且SSL
Spring Boot
pom.xml
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>mq-jms-spring-boot-starter</artifactId>
<version>2.0.8</version>
</dependency>
注意:使用 sslcontext-kickstart api 和更多文档可以在这里找到
https://github.com/Hakky54/sslcontext-kickstart
MulticertApplication.java
public class MulticertApplication implements CommandLineRunner {
@Autowired
private SSLContextService sslContextService;
public static void main(String[] args) {
SpringApplication.run(MulticertApplication.class, args);
}
@Override
public void run(String... args) throws JMSException {
// Both WORKS !!
testConnectionWithIndividualSSL("APP1");
log.info("APP1 CONNECTION SUCCESS WITH INDIVIDUAL SSL !!");
testConnectionWithIndividualSSL("APP2");
log.info("APP2 CONNECTION SUCCESS WITH INDIVIDUAL SSL !!");
// Does NOT WORK !!
SSLSocketFactory sslSocketFactory = sslContextService.getCombinedSSLSocketFactory();
testConnectionWithCombinedSSL(sslSocketFactory, "APP1");
log.info("APP1 CONNECTION SUCCESS WITH COMBINED SSL !!");
testConnectionWithCombinedSSL(sslSocketFactory, "APP2");
log.info("APP2 CONNECTION SUCCESS WITH COMBINED SSL !!");
}
private void testConnectionWithIndividualSSL(String app) throws JMSException {
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName("MQHOST.company.net");
mqQueueConnectionFactory.setPort(1414);
mqQueueConnectionFactory.setTransportType(1);
mqQueueConnectionFactory.setQueueManager("MQHOST");
if (app.equalsIgnoreCase("APP1")) {
mqQueueConnectionFactory.setChannel("APP1.SVRCONN.TLS");
} else {
mqQueueConnectionFactory.setChannel("APP2.SVRCONN.TLS");
}
mqQueueConnectionFactory.setSSLCipherSuite("TLS_RSA_WITH_AES_256_CBC_SHA256");
mqQueueConnectionFactory.setSSLFipsRequired(false);
mqQueueConnectionFactory.setSSLSocketFactory(sslContextService.getSSLSocketFactory(app));
MQQueueConnection mqQueueConnection = (MQQueueConnection) mqQueueConnectionFactory.createQueueConnection();
mqQueueConnection.start();
mqQueueConnection.stop();
mqQueueConnection.close();
}
private void testConnectionWithCombinedSSL(SSLSocketFactory sslSocketFactory, String app) throws JMSException {
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName("MQHOST.company.net");
mqQueueConnectionFactory.setPort(1414);
mqQueueConnectionFactory.setTransportType(1);
mqQueueConnectionFactory.setQueueManager("MQHOST");
if (app.equalsIgnoreCase("APP1")) {
mqQueueConnectionFactory.setChannel("APP1.SVRCONN.TLS");
} else {
mqQueueConnectionFactory.setChannel("APP2.SVRCONN.TLS");
}
mqQueueConnectionFactory.setSSLCipherSuite("TLS_RSA_WITH_AES_256_CBC_SHA256");
mqQueueConnectionFactory.setSSLFipsRequired(false);
mqQueueConnectionFactory.setSSLSocketFactory(sslSocketFactory);
MQQueueConnection mqQueueConnection = (MQQueueConnection) mqQueueConnectionFactory.createQueueConnection();
mqQueueConnection.start();
mqQueueConnection.stop();
mqQueueConnection.close();
}
}
SSLContextService.java
@Service
public class SSLContextService {
private static final String APP1_JKS_PWD = "abc";
private static final String APP2_JKS_PWD = "def";
public SSLSocketFactory getSSLSocketFactory(String app) {
log.info("app: " + app);
if (app.equalsIgnoreCase("APP1")) {
return SSLFactory.builder()
.withIdentityMaterial("app1.jks", APP1_JKS_PWD.toCharArray())
.withTrustMaterial("app1.jks", APP1_JKS_PWD.toCharArray())
.build().getSslContext().getSocketFactory();
} else if (app.equalsIgnoreCase("APP2")) {
return SSLFactory.builder()
.withIdentityMaterial("app2.jks", APP2_JKS_PWD.toCharArray())
.withTrustMaterial("app2.jks", APP2_JKS_PWD.toCharArray())
.build().getSslContext().getSocketFactory();
}
return null;
}
public SSLSocketFactory getCombinedSSLSocketFactory() {
return SSLFactory.builder()
.withIdentityMaterial("app1.jks", APP1_JKS_PWD.toCharArray())
.withIdentityMaterial("app2.jks", APP2_JKS_PWD.toCharArray())
.withTrustMaterial("app1.jks", APP1_JKS_PWD.toCharArray())
.withTrustMaterial("app2.jks", APP2_JKS_PWD.toCharArray())
.build().getSslContext().getSocketFactory();
}
}
错误日志:
2021-09-30 17:55:51.892 INFO 62988 --- [ restartedMain] c.e.multicert.MulticertApplication : Started MulticertApplication in 3.414 seconds (JVM running for 4.448)
2021-09-30 17:55:51.899 INFO 62988 --- [ restartedMain] c.e.multicert.service.SSLContextService : app: APP1
2021-09-30 17:55:52.513 INFO 62988 --- [ restartedMain] c.e.multicert.MulticertApplication : APP1 CONNECTION SUCCESS WITH INDIVIDUAL SSL !!
2021-09-30 17:55:52.514 INFO 62988 --- [ restartedMain] c.e.multicert.service.SSLContextService : app: APP2
2021-09-30 17:55:52.555 INFO 62988 --- [ restartedMain] c.e.multicert.MulticertApplication : APP2 CONNECTION SUCCESS WITH INDIVIDUAL SSL !!
2021-09-30 17:55:52.597 INFO 62988 --- [ restartedMain] c.e.multicert.MulticertApplication : APP1 CONNECTION SUCCESS WITH COMBINED SSL !!
2021-09-30 17:55:52.641 INFO 62988 --- [ restartedMain] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-09-30 17:55:52.663 ERROR 62988 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:807) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:788) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:333) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) [spring-boot-2.4.2.jar:2.4.2]
at com.example.multicert.MulticertApplication.main(MulticertApplication.java:23) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.4.2.jar:2.4.2]
Caused by: com.ibm.msg.client.jms.DetailedJMSSecurityException: JMSWMQ2013: The security authentication was not valid that was supplied for QueueManager 'MQHOST' with connection mode 'Client' and host name 'MQHOST.company.net(1414)'.
at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:531) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:215) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.msg.client.wmq.internal.WMQConnection.<init>(WMQConnection.java:424) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.msg.client.wmq.factories.WMQConnectionFactory.createV7ProviderConnection(WMQConnectionFactory.java:8475) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.msg.client.wmq.factories.WMQConnectionFactory.createProviderConnection(WMQConnectionFactory.java:7815) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl._createConnection(JmsConnectionFactoryImpl.java:303) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl.createConnection(JmsConnectionFactoryImpl.java:236) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.mq.jms.MQConnectionFactory.createCommonConnection(MQConnectionFactory.java:6016) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.ibm.mq.jms.MQQueueConnectionFactory.createQueueConnection(MQQueueConnectionFactory.java:111) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
at com.example.multicert.MulticertApplication.testConnectionWithCombinedSSL(MulticertApplication.java:76) [classes/:na]
at com.example.multicert.MulticertApplication.run(MulticertApplication.java:38) [classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:804) [spring-boot-2.4.2.jar:2.4.2]
... 10 common frames omitted
Caused by: com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2035' ('MQRC_NOT_AUTHORIZED').
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:203) ~[com.ibm.mq.allclient-9.1.0.0.jar:9.1.0.0 - p910-L180705]
... 20 common frames omitted
解决方案
看起来底层 KeyManager 无法将正确的客户端证书发送到 ibm mq 服务器。这将在使用具有相同密钥算法的多个密钥库时发生。在这种情况下,它只会获取第一个客户端证书。
所以我建议使用 SSLFactory 的附加选项来正确地将客户端证书路由到正确的 ibm mq 服务器。因此,以下设置应该可以解决问题:
public SSLSocketFactory getCombinedSSLSocketFactory() {
return SSLFactory.builder()
.withIdentityMaterial("app1.jks", APP1_JKS_PWD.toCharArray())
.withIdentityMaterial("app2.jks", APP2_JKS_PWD.toCharArray())
.withTrustMaterial("app1.jks", APP1_JKS_PWD.toCharArray())
.withTrustMaterial("app2.jks", APP2_JKS_PWD.toCharArray())
.withClientIdentityRoute("client-alias-one", "https://[MQHOST-ONE.company.net]:1414/")
.withClientIdentityRoute("client-alias-two", "https://[MQHOST-TWO.company.net]:8463/")
.build().getSslContext().getSocketFactory();
}
我从来没有用 ibm mq 试过这个,所以我不确定这是否可行。所以这只是一个假设。使用传统设置(基本的客户端和服务器通信),同时使用多个密钥库,它将起作用。所以我很好奇这个解决方案是否适合你。
对于withClientIdentityRoute
您需要传递在密钥库文件中使用的证书的别名作为第一个参数。第二个参数应该是您的 ibm mq 服务器的 url。
请随时在此处详细阅读:将身份材料路由到特定主机
您可以尝试一下并在这里分享您的结果吗?
推荐阅读
- node.js - 在 Visual Studio 中构建 Cordova 项目时出错
- stackdriver - 用于 OracleDB 的 Google Stackdriver 数据库代理?
- c# - 如何在 GetResponse 中检查 HttpStatusCode.OK
- javascript - Safari 调用 decodeAudioData 错误,在 Chrome 工作的地方出现 null
- ios - Apple 说“您的 iOS 分发证书即将到期”我需要做什么
- angular - 添加推送通知的 Ionic 3 问题
- javascript - 在谷歌地图javascript api中的两个特定标记上放置标签
- asp.net-mvc - 从 ZipCode 中检索 Lat Long
- c++ - 在 C++ 中获取类名有哪些好的技巧?
- sql - 蜂巢:选择所有列排除两个