【问题标题】:Connecting to a Websphere MQ in Java with SSL/Keystore使用 SSL/Keystore 连接到 Java 中的 Websphere MQ
【发布时间】:2011-02-11 03:12:45
【问题描述】:

我想通过 Java 连接到 Websphere 6.0 MQ。我已经为“正常”队列工作了代码,但现在我需要访问一个经过 SSL 加密的新队列(密钥库)。我收到了一个名为 something.jks 的文件,我认为它是我需要存储在某处的证书。我一直在网上搜索,但找不到合适的信息。

这是我用于“正常”队列的代码。我假设我需要设置一些属性,但不确定是哪一个。

MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setChannel(channel_);
connectionFactory.setHostName(hostname_);
connectionFactory.setPort(port_);
connectionFactory.setQueueManager(queueManager_);
connectionFactory.setTransportType(1);
connectionFactory.setSSsetSSLCertStores(arg0)

Connection connection = connectionFactory.createConnection();
connection.setExceptionListener(this);
session_ = connection.createSession(DEFAULT_TRANSACTED, DEFAULT_ACKMODE);
connection.start();

javax.jms.Queue fQueue = session_.createQueue(queue_);
consumer = session_.createConsumer(fQueue);

【问题讨论】:

    标签: ssl keystore ibm-mq


    【解决方案1】:

    developerWorks 中的 Alex Fehners 教程有点旧(2005 年),但有一些代码示例应该适合您。

    SSL configuration of the Websphere MQ Java/JMS client

    您的 Java 应用程序将根据其证书对 QMgr 进行身份验证。这意味着您提供的 jks 文件必须具有 QMgr 的自签名证书,或者它将具有签署 QMgr 证书的证书颁发机构的根证书。在任何一种情况下,您都使用-Djavax.net.ssl.trustStore=<location of trustStore> 指向文件,如上面链接的文章中所述。如果 jks 有密码,您还需要指定 -Djavax.net.ssl.trustStorePassword=<password>始终需要使用信任库对 QMgr 进行身份验证。下一部分可能需要也可能不需要。

    另一个难题是 QMgr 可能要求您的应用提供证书。换句话说,QMgr 证书总是经过身份验证,应用程序是否需要进行身份验证是可选的。如果是,那么您就有了所谓的“相互身份验证”。如果您连接到的通道已配置为SSLCAUTH(REQUIRED),则相互身份验证已启用,并且 QMgr 必须在其密钥库中具有您的应用程序的自签名证书或签署您的应用程序证书的 CA 根证书。希望设置您的 jks 文件的人已经安排好了。

    假设需要相互身份验证,那么除了 QMgr 的受信任证书之外,您的 jks 还将拥有一个代表您的应用程序的私有证书。要让应用程序获取证书并将其呈现给 QMgr,请使用 -Djavax.net.ssl.keyStore=<location of keyStore>-Djavax.net.ssl.keyStorePassword=<password> 参数。注意这些说 key 存储,而之前的参数说 trust 存储。

    我的建议是与 WMQ 管理员一起设置和测试 SSL 连接。第一阶段应该是使用SSLCAUTH(OPTIONAL) 测试频道。这验证了应用程序可以解析和验证 QMgr 的证书。只有当您完成这项工作时,WMQ 管理员才会将通道更改为 SSLCAUTH(REQUIRED),以测试反向身份验证。

    强烈建议您将 WMQ v7 客户端用于新应用程序。这有两个原因:1) v6 已于 2011 年 9 月终止; 2) v7 代码内置了更多诊断功能。v7 客户端代码与 v6 QMgr 完全兼容,并且与 v6 客户端一样工作。您只是没有获得 v7 功能。在此处免费下载 WMQ 客户端代码:

    IBM - MQC7: WebSphere MQ V7.0 Clients

    我今年在 IMPACT 运行 WMQ 动手安全实验室,并将在周末通过 http://t-rob.net 发布脚本和实验室指南,请回来查看。

    【讨论】:

      【解决方案2】:

      从 Oracle JVM (JSSE) 使用 SSL

      另见“What TLS cipherspecs/ciphersuites are supported when connecting from Oracle Java (non-IBM JRE) to MQ queue manager?

      在 MQ 客户端版本 8.0.0.2 中包含一个补丁以将 TLS 与 Oracle JVM 一起使用,这适用于上面的 lanes 答案

      要让它工作,你需要最新的 MQ 客户端,它包含 IV66840: WMQ V7 JAVA/JMS: 添加对选定 TLS 密码规范的支持 在非 IBM JAVA 运行时环境中运行
      http://www-01.ibm.com/support/docview.wss?uid=swg1IV66840
      (download)

      根据您所在的位置,您可能还需要安装 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8 (download)

      要使用它,您必须使用 JVM 参数进行配置:

        -Dcom.ibm.mq.cfg.useIBMCipherMappings=false
      

      注意Oracle和IBM JVM之间的默认安全实现行为differs

      Oracle JSSE Reference guide 说:

      如果 KeyManager[] 参数为空,那么一个空的 KeyManager 将 为这个上下文定义。

      IBM JSSE Reference guide 说:

      如果 KeyManager[] 参数为空,则安装的安全 提供者将被搜索最高优先级的实现 KeyManagerFactory,一个合适的 KeyManager 将从那里得到 获得。

      这意味着你必须设置your own ssl context

      SSLContext  sslcontext = SSLContext.getInstance("TLS");
      String  keyStore = System.getProperty("javax.net.ssl.keyStore");
      String  keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
      String  keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword","");
      KeyManager[]    kms = null;
      if (keyStore != null)
      {
          KeyManagerFactory   kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
          KeyStore    ks = KeyStore.getInstance(keyStoreType);
          if (keyStore != null && !keyStore.equals("NONE")) {
              fs = new FileInputStream(keyStore);
          ks.load(fs, keyStorePassword.toCharArray());
          if (fs != null)
              fs.close();
          char[]  password = null;
          if (keyStorePassword.length() > 0)
              password = keyStorePassword.toCharArray();
          kmf.init(ks,password);
          kms = kmf.getKeyManagers();
      }
      sslcontext.init(kms,null,null);
      

      然后将其提供给 MQ JMS 客户端:

          JmsConnectionFactory cf = ...                                                                     
      
          MQConnectionFactory mqcf = (MQConnectionFactory) cf;              
          mqcf.setSSLSocketFactory(sslcontext.getSocketFactory());  
      

      如果使用应用程序服务器,这可能由您的应用程序服务器处理。

      【讨论】:

      • 据我了解,我们可以通过指定标准 java 属性来使用 ssl,例如。 javax.net.ssl.trustStore。或通过创建 SSLSocketFactory。如果我使用ibm.com/developerworks/websphere/library/techarticles/… 中提到的标准属性,我还需要升级到上面提到的版本吗?
      • @AnujKhandelwal 你有什么问题吗?需要配置的密码列在上面链接的 IV66840 中,请参阅disablessl3.com
      【解决方案3】:

      试试这个代码以及关于证书的 T.Robs 解释:

      import com.ibm.mq.jms.*;
      
      import java.io.FileInputStream;
      import java.io.Console;
      import java.security.*;
      
      import javax.jms.JMSException;
      import javax.jms.QueueConnection;
      import javax.net.ssl.KeyManagerFactory;
      import javax.net.ssl.SSLContext;
      import javax.net.ssl.SSLSocketFactory;
      import javax.net.ssl.TrustManagerFactory;
      
      import com.ibm.mq.jms.MQQueueConnectionFactory;
      
      public class SSLTest {
      
         public static void main(String[] args) {
            System.out.println(System.getProperty("java.home"));
      
            String HOSTNAME = "myhost";
            String QMGRNAME = "MyQMGR";
            String CHANNEL = "MY.SVRCONN";
            String SSLCIPHERSUITE = "TLS_RSA_WITH_AES_256_CBC_SHA";
      
            try {
               Class.forName("com.sun.net.ssl.internal.ssl.Provider");
      
               System.out.println("JSSE is installed correctly!");
      
               Console console = System.console();
               char[] KSPW = console.readPassword("Enter keystore password: ");
      
               // instantiate a KeyStore with type JKS
               KeyStore ks = KeyStore.getInstance("JKS");
               // load the contents of the KeyStore
               ks.load(new FileInputStream("/home/hudo/hugo.jks"), KSPW);
               System.out.println("Number of keys on JKS: "
                     + Integer.toString(ks.size()));
      
               // Create a keystore object for the truststore
               KeyStore trustStore = KeyStore.getInstance("JKS");
               // Open our file and read the truststore (no password)
               trustStore.load(new FileInputStream("/home/xwgztu2/xwgztu2.jks"), null);
      
               // Create a default trust and key manager
               TrustManagerFactory trustManagerFactory =
                 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
               KeyManagerFactory keyManagerFactory =
                 KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
      
               // Initialise the managers
               trustManagerFactory.init(trustStore);
               keyManagerFactory.init(ks,KSPW);
      
               // Get an SSL context.
               // Note: not all providers support all CipherSuites. But the
               // "SSL_RSA_WITH_3DES_EDE_CBC_SHA" CipherSuite is supported on both SunJSSE
               // and IBMJSSE2 providers
      
               // Accessing available algorithm/protocol in the SunJSSE provider
               // see http://java.sun.com/javase/6/docs/technotes/guides/security/SunProviders.html
               SSLContext sslContext = SSLContext.getInstance("SSLv3");
      
               // Acessing available algorithm/protocol in the IBMJSSE2 provider
               // see http://www.ibm.com/developerworks/java/jdk/security/142/secguides/jsse2docs/JSSE2RefGuide.html
               // SSLContext sslContext = SSLContext.getInstance("SSL_TLS");
                System.out.println("SSLContext provider: " +
                                  sslContext.getProvider().toString());
      
               // Initialise our SSL context from the key/trust managers
               sslContext.init(keyManagerFactory.getKeyManagers(),
                               trustManagerFactory.getTrustManagers(), null);
      
               // Get an SSLSocketFactory to pass to WMQ
               SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
      
               // Create default MQ connection factory
               MQQueueConnectionFactory factory = new MQQueueConnectionFactory();
      
               // Customize the factory
               factory.setSSLSocketFactory(sslSocketFactory);
               // Use javac SSLTest.java -Xlint:deprecation
               factory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
               factory.setQueueManager(QMGRNAME);
               factory.setHostName(HOSTNAME);
               factory.setChannel(CHANNEL);
               factory.setPort(1414);
               factory.setSSLFipsRequired(false);
               factory.setSSLCipherSuite(SSLCIPHERSUITE);
      
               QueueConnection connection = null;
               connection = factory.createQueueConnection("",""); //empty user, pass to avoid MQJMS2013 messages
               connection.start();
               System.out.println("JMS SSL client connection started!");
               connection.close();
      
            } catch (JMSException ex) {
               ex.printStackTrace();
            } catch (Exception ex){
               ex.printStackTrace();
            }
         }
      }
      

      【讨论】:

        【解决方案4】:

        注意您使用的是哪个 JRE。我们在使用 Sun JDK 时遇到了很大的麻烦,因为 IBM MQ 的 SSL 通道上存在某种加密 (TLS_RSA_WITH_AES_128_CBC_SHA)。我们使用了 X509 证书。为了让它工作,我们使用 IBM JRE,因为它对某些密码套件有更大的支持!

        【讨论】:

        • 我也面临无法连接“TLS_RSA_WITH_AES_128_CBC_SHA”的问题,是否仍然没有解决方案使其与 Sun JDK 无缝协作?
        猜你喜欢
        • 1970-01-01
        • 2012-09-10
        • 2012-03-10
        • 1970-01-01
        • 1970-01-01
        • 2018-02-15
        • 1970-01-01
        • 1970-01-01
        • 2018-11-13
        相关资源
        最近更新 更多