简体   繁体   English

为 JMS 使用特定的密钥库

[英]Use specific keystore for JMS

We have the requirement to use SSL client certificate for a JMS connection to an IBM MQ server.我们需要将 SSL 客户端证书用于与 IBM MQ 服务器的 JMS 连接。 I already asked a question specifically for Websphere MQ but then I learned that this is mainly the job of JSSE and can be configured via Java System Properties (eg -Djavax.net.ssl.keyStore=<location of keyStore> ).我已经专门针对 Websphere MQ提出了一个问题,但后来我了解到这主要是 JSSE 的工作,可以通过 Java 系统属性(例如-Djavax.net.ssl.keyStore=<location of keyStore> )进行配置。

But since there are already active keystores for other parts of the application within our WildFly 9 AS, I'm looking for a way to enable a specific keystore just for the JMS part - can this be done?但是由于在我们的 WildFly 9 AS 中已经有应用程序其他部分的活动密钥库,我正在寻找一种方法来为 JMS 部分启用特定的密钥库 - 这可以做到吗?

Yes it is possible for an MQ classes for JMS application to use a specific keystore and truststore when creating secure connections to a queue manager. 是的,在创建到队列管理器的安全连接时,JMS应用程序的MQ类可以使用特定的密钥库和信任库。

By default, the MQ classes for JMS will use the standard javax.net.ssl System Properties to determine which certificate store to use as the key and trust stores. 默认情况下,JMS的MQ类将使用标准javax.net.ssl系统属性来确定要用作密钥和信任存储的证书存储。 However, you can customise this by building your own javax.net.ssl.SSLSocketFactory object that gets set on the JMS Connection Factory used by your application. 但是,您可以通过构建自己的javax.net.ssl.SSLSocketFactory对象来自定义此对象,该对象在应用程序使用的JMS连接工厂上设置。

See the Knowledge Center for further details: 有关详细信息,请参阅知识中心:

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

This typically means you have to programmatically build or update a JMS Connection Factory within application code, rather than via administration only and updating a JNDI definition - which is somewhat unfortunate. 这通常意味着您必须以编程方式在应用程序代码中构建或更新JMS连接工厂,而不是仅通过管理并更新JNDI定义 - 这有点不幸。

I know you have stated you are using WildFly as your application server of choice, but just for your awareness, WebSphere Application Server (WSAS) allows you to configure a JMS Connection Factory within JNDI and have a separate SSL/TLS configuration (containing certificate store information, Cipher Suites etc) that can be associated with the JMS resources. 我知道您已声明使用WildFly作为您选择的应用程序服务器,但只是为了您的意识,WebSphere Application Server(WSAS)允许您在JNDI中配置JMS连接工厂并具有单独的SSL / TLS配置(包含证书存储区)信息,密码套件等)可以与JMS资源相关联。 WSAS will then take care of creating the SSLSocketFactory and setting it appropriately on the JMS Connection Factory when an application uses it to create a JMS Connection or Context. 然后,当应用程序使用它创建JMS连接或上下文时,WSAS将负责创建SSLSocketFactory并在JMS连接工厂上正确设置它。

As such, you continue to define your resources (JMS and SSL) administratively via the WSAS Administration Console or wsadmin scripting without having to insert specific logic within the application to do this, which is obviously preferred. 因此,您可以通过WSAS管理控制台或wsadmin脚本继续以管理方式定义资源(JMS和SSL),而无需在应用程序中插入特定逻辑来执行此操作,这显然是首选。

WildFly (and other JEE app servers) might offer similar functionality, but I do not know. WildFly(和其他JEE应用服务器)可能提供类似的功能,但我不知道。

I've never worked with IBM MQ but i solved the similar task for various application containers and databases. 我从未使用过IBM MQ,但我解决了各种应用程序容器和数据库的类似任务。 As i can see from documentation it's possible to specify custom ssl connection factory for MQ using this method MQConnectionFactory.setSSLSocketFactory() . 正如我从文档中看到的那样,可以使用此方法MQConnectionFactory.setSSLSocketFactory()为MQ指定自定义ssl连接工厂。 So yes, it's definitely possible to address your requirements and basically your task is to build a dedicated ssl socket factory for MQ connections. 所以是的,它绝对可以满足您的要求,基本上您的任务是为MQ连接构建专用的ssl套接字工厂。

Here is code snippets for this: 这是代码片段:

Utility class for generating in-memory keystores and truststores. 用于生成内存中密钥库和信任库的实用程序类。 Java keyloader supports only pkcs8 private keys out of the box. Java密钥加载器仅支持开箱即用的pkcs8私钥。 To load pem keys some external library like BouncyCastle should be used. 要加载pem键,应使用一些外部库,如BouncyCastle It's possible to generate pkcs8 keys from pem keys using openssl. 可以使用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<CertificateEntry> 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<PrivateKeyCertificateEntry> 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
  }
}

The next code creates ssl socket factory for MQ using dedicated keystore and truststore. 下一个代码使用专用密钥库和信任库为MQ创建ssl套接字工厂。 This code loads keys and certificates from disk as class path resources. 此代码从磁盘加载密钥和证书作为类路径资源。 It's also possible to store them only in memory using OS environment variables and some extra effort during client application deployment. 也可以使用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();
  }

And then set this ssl socket factory to mq connection factory using MQConnectionFactory.setSSLSocketFactory() method. 然后使用MQConnectionFactory.setSSLSocketFactory()方法将此ssl套接字工厂设置为mq连接工厂。 Seems that IBM MQ is a proprietary library so unfortunately i can't test it, but i guess such configuration should work. 似乎IBM MQ是一个专有库,所以不幸的是我无法测试它,但我想这样的配置应该可行。

This maybe a little too late, but may help others.这可能有点太晚了,但可能会帮助其他人。 I was able to get it to work using JmsFactoryFactory,MQConnectionFactory, JKS trustStore and keyStore generated from a Certificate with the following code:我能够使用从证书生成的 JmsFactoryFactory、MQConnectionFactory、JKS trustStore 和 keyStore 使其工作,代码如下:

      try {

        JmsFactoryFactory factoryFactory = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
        jmsConnectionFactory = (MQConnectionFactory) factoryFactory.createConnectionFactory();
        SSLContext sslContext = createSSlContext();
        setSSLSystemProperties();
        jmsConnectionFactory.setSSLSocketFactory(sslContext.getSocketFactory());

        // Set the properties
        jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_HOST_NAME, hostName);
        jmsConnectionFactory.setIntProperty(WMQConstants.WMQ_PORT, port);
        jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_CHANNEL, channel);
        jmsConnectionFactory.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
        jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, APPLICATION_NAME);
        jmsConnectionFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, false);
        jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, cipherSuite);

        return jmsConnectionFactory;

    } catch (JMSException ex) {}

SSL Context SSL上下文

    private SSLContext createSSlContext() throws NoSuchAlgorithmException {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    try {
        // Load KeyStore
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(toInputStream(keyStorePath), keyStorePassword.toCharArray());

        // Load TrustStore
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(toInputStream(trustStorePath), trustStorePassword.toCharArray());

        // Set KeyManger from keyStore
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keyStore, keyStorePassword.toCharArray());

        // Set TrustManager from trustStore
        TrustManagerFactory trustFact = TrustManagerFactory.getInstance("SunX509");
        trustFact.init(trustStore);

        // Set Context to TLS and initialize it
        sslContext.init(kmf.getKeyManagers(), trustFact.getTrustManagers(), null);

        return sslContext;
    } catch (Exception ex) {
        LOG.error("Unable to load the SSL Config", ex);
    }

    return sslContext;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM