为JMS使用特定密钥库

我们要求将SSL客户端证书用于与IBM MQ服务器的JMS连接。 我已经专门针对Websphere MQ提出了一个问题,但后来我了解到这主要是JSSE的工作,可以通过Java系统属性进行配置(例如-Djavax.net.ssl.keyStore= )。

但是由于我们的WildFly 9 AS中已经有应用程序其他部分的活动密钥库,我正在寻找一种方法来为JMS部分启用特定的密钥库 – 这可以做到吗?

是的,在创建与队列管理器的安全连接时,JMS应用程序的MQ类可以使用特定的密钥库和信任库。

默认情况下,JMS的MQ类将使用标准javax.net.ssl系统属性来确定要用作密钥和信任存储的证书存储。 但是,您可以通过构建自己的javax.net.ssl.SSLSocketFactory对象来自定义此对象,该对象在应用程序使用的JMS连接工厂上设置。

有关详细信息,请参阅知识中心:

https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.0.0/com.ibm.mq.dev.doc/q032450_.htm

这通常意味着您必须以编程方式在应用程序代码中构建或更新JMS连接工厂,而不是仅通过管理并更新JNDI定义 – 这有点不幸。

我知道您已声明使用WildFly作为您选择的应用程序服务器,但只是为了您的意识,WebSphere Application Server(WSAS)允许您在JNDI中配置JMS连接工厂并具有单独的SSL / TLS配置(包含证书存储区)信息,密码套件等)可以与JMS资源相关联。 然后,当应用程序使用它创建JMS连接或上下文时,WSAS将负责创建SSLSocketFactory并在JMS连接工厂上正确设置它。

因此,您可以继续通过WSAS管理控制台或wsadmin脚本来管理性地定义资源(JMS和SSL),而无需在应用程序中插入特定逻辑来执行此操作,这显然是首选。

WildFly(和其他JEE应用服务器)可能提供类似的function,但我不知道。

我从未使用过IBM MQ,但我解决了各种应用程序容器和数据库的类似任务。 正如我从文档中看到的那样,可以使用此方法MQConnectionFactory.setSSLSocketFactory()为MQ指定自定义ssl连接工厂。 所以是的,它绝对可以满足您的要求,基本上您的任务是为MQ连接构建专用的ssl套接字工厂。

这是代码片段:

用于生成内存中密钥库和信任库的实用程序类。 Java密钥加载器仅支持开箱即用的pkcs8私钥。 要加载pem键,应使用一些外部库,如BouncyCastle 。 可以使用openssl从pem键生成pkcs8键。

 public class KeystoreGenerator { private KeystoreGenerator() { } public static KeyStore generateTrustStore(CertificateEntry certificateEntry) throws Exception { return generateTrustStore(Collections.singletonList(certificateEntry)); } public static KeyStore generateTrustStore(Collection certificateEntries) throws Exception { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); for (CertificateEntry certificateEntry : certificateEntries) { Certificate certificate = certFactory.generateCertificate(certificateEntry.getCertificate()); keyStore.setCertificateEntry(certificateEntry.getAlias(), certificate); } return keyStore; } public static KeyStore generateKeystore(PrivateKeyCertificateEntry privateKeyCertificateEntry) throws Exception { return generateKeystore(Collections.singletonList(privateKeyCertificateEntry)); } public static KeyStore generateKeystore(Collection privateKeyCertificateEntries) throws Exception { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); for (PrivateKeyCertificateEntry privateKeyCertificateEntry : privateKeyCertificateEntries) { Certificate certificate = certFactory.generateCertificate(privateKeyCertificateEntry.getCertificate()); keyStore.setCertificateEntry(privateKeyCertificateEntry.getAlias(), certificate); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(IOUtils.toByteArray(privateKeyCertificateEntry.getKey())); PrivateKey privateKey = keyFactory.generatePrivate(spec); keyStore.setKeyEntry(privateKeyCertificateEntry.getAlias(), privateKey, privateKeyCertificateEntry.getPassword(), new Certificate[]{certificate}); } return keyStore; } public static class CertificateEntry { private final InputStream certificate; private final String alias; // constructor, getters and setters } public static class PrivateKeyCertificateEntry { private final InputStream key; private final InputStream certificate; private final String alias; private final char[] password; // constructor, getters and setters } } 

下一个代码使用专用密钥库和信任库为MQ创建ssl套接字工厂。 此代码从磁盘加载密钥和证书作为类路径资源。 也可以使用OS环境变量将它们仅存储在内存中,并在客户端应用程序部署期间进行一些额外的工作。

  public SSLSocketFactory generateMqSSLSocketFactory() throws Exception { KeyStore keyStore = KeystoreGenerator.generateKeystore(new KeystoreGenerator.PrivateKeyCertificateEntry( getClass().getResourceAsStream("/keys/mq-client-key.pkcs8"), getClass().getResourceAsStream("/keys/mq-client-certificate.pem"), "mq_client", "changeit".toCharArray() )); // Generate keystore to authorize client on server KeyStore trustStore = KeystoreGenerator.generateTrustStore(new KeystoreGenerator.CertificateEntry( getClass().getResourceAsStream("/keys/mq-server-certificate.pem"), "mq_server")); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, "changeit".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); return sslContext.getSocketFactory(); } 

然后使用MQConnectionFactory.setSSLSocketFactory()方法将此ssl套接字工厂设置为mq连接工厂。 似乎IBM MQ是一个专有库,所以不幸的是我无法测试它,但我想这样的配置应该可行。