用SSL/Keystore在Java中连接到Websphere MQ

16

我想通过Java连接至Websphere 6.0 MQ。我已经有了用于“普通”队列的工作代码,但现在我需要访问一个新的加密队列(密钥库)。我收到了一个名为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);
4个回答

14

Alex Fehners在developerWorks上的教程有些老旧(2005年),但是其中的代码示例应该能够为您工作。

Websphere MQ Java/JMS客户端的SSL配置

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

问题的另一部分是,QMgr可能要求您的应用程序提供证书。换句话说,始终会验证QMgr证书,无论是否需要应用程序进行身份验证都是可选的。如果需要,则称为“相互认证”。如果连接到的通道已配置为SSLCAUTH(REQUIRED),则已启用相互认证,QMgr必须具有应用程序的自签名证书或签署了应用程序证书的CA根证书。希望设置jks文件的人已经为此做好了安排。

假设需要使用双向身份验证,则您的jks除了包含QMgr信任的证书外,还应该有一个代表您的应用程序的私有证书。为了让应用程序获取证书并将其呈现给QMgr,您可以使用 -Djavax.net.ssl.keyStore=<keyStore位置>-Djavax.net.ssl.keyStorePassword=<密码> 参数。请注意这些参数是“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 Hands-On安全实验室,并将在周末在http://t-rob.net上发布脚本和实验指南,敬请关注。

9

使用Oracle JVM(JSSE)的SSL

另请参见“从Oracle Java(非IBM JRE)连接到MQ队列管理器时支持哪些TLS密码规范/密码套件?

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

为了使此功能正常工作,您需要包含最新的MQ Client,其中包含IV66840:WMQ V7 JAVA/JMS:在非IBM Java运行时环境中运行时添加对选定TLS密码规范的支持
http://www-01.ibm.com/support/docview.wss?uid=swg1IV66840
下载
根据您所在的位置,您可能还需要安装Java Cryptography Extension(JCE)无限制权限策略文件8(下载
要使用此功能,您必须使用JVM参数进行配置。
  -Dcom.ibm.mq.cfg.useIBMCipherMappings=false

请注意,Oracle和IBM JVM之间的默认安全实现行为 不同Oracle JSSE参考指南表示:
如果KeyManager[]参数为null,则将为此上下文定义一个空的KeyManager。 IBM JSSE参考指南表示:
如果KeyManager[]参数为null,则将搜索已安装的安全提供程序以获取KeyManagerFactory的优先级最高的实现,然后从中获取适当的KeyManager。
这意味着您必须设置自己的SSL上下文
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());  

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

据我理解,我们使用 SSL 要么通过指定标准的 Java 属性(例如 javax.net.ssl.trustStore),要么通过创建 SSLSocketFactory。如果我正在使用 http://www.ibm.com/developerworks/websphere/library/techarticles/0510_fehners/0510_fehners.html 中提到的标准属性,我是否仍需要升级到上述版本? - Anuj Khandelwal
@AnujKhandelwal,你有什么问题吗?需要配置的密码列表在上面链接的IV66840中列出,请参见http://disablessl3.com/。 - oluies

5

跟着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();
      }
   }
}

5

请注意您使用的JRE版本。我们在使用Sun JDK时遇到了大问题,因为SSL通道上存在一定的加密(TLS_RSA_WITH_AES_128_CBC_SHA),这会影响与IBM MQ的通信。我们使用了X509证书。为了使其正常工作,我们使用IBM JRE,因为它对某些密码套件有更大的支持!


我也面临不能连接到“TLS_RSA_WITH_AES_128_CBC_SHA”,是否仍然没有解决方案,使其能够与Sun JDK无缝工作? - Anuj Khandelwal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接