I'd like to connect to a Websphere 6.0 MQ via Java. I have already working code for a "normal" queue, but now I need to access a new queue which is SSL encrypted (keystore). I have been sent a file called something.jks, which I assume is a certificate I need to store somewhere. 开发者_如何转开发I have been searching the net, but I can't find the right information.
This is the code I use for the "normal" queue. I assume I need to set some property, but not sure which one.
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);
Alex Fehners tutorial in developerWorks is a bit old (2005) but has code samples that should work for you.
SSL configuration of the Websphere MQ Java/JMS client
Your Java app will authenticate the QMgr based on its certificate. That means the jks file you were provided must have either the QMgr's self-signed certificate or it will have the root certificate of a Certificate Authority that signed the QMgr's certificate. In either case you point to the file using the -Djavax.net.ssl.trustStore=<location of trustStore>
as noted in the article linked above. If the jks has a password, you will need to specify -Djavax.net.ssl.trustStorePassword=<password>
as well. Authenticating the QMgr with a truststore is always required. The next part may or may not be required.
The other piece of the puzzle is that the QMgr may require your app to present a certificate. In other words, the QMgr cert is always authenticated, whether the app is required to authenticate is optional. If it is then you have what is known as "mutual authentication". If the channel that you connect to has been configured with SSLCAUTH(REQUIRED)
then mutual auth has been enabled and the QMgr must have your application's self-signed cert or a CA root cert that signed your app's cert in its keystore. Hopefully whoever set up your jks file will have arranged for this already.
Assuming mutual auth is required, then your jks will have, in addition to the QMgr's trusted cert, a private cert representing your application. To get the app to fetch the cert and present it to the QMgr, you use the -Djavax.net.ssl.keyStore=<location of keyStore>
and -Djavax.net.ssl.keyStorePassword=<password>
parameters. Note these say key store whereas the previous parms said trust store.
My recommendation is to work with the WMQ administrator to set up and test the SSL connection. The first phase should be to test the channel with SSLCAUTH(OPTIONAL)
. This verifies that the application can resolve and authenticate the QMgr's certificate. Only when you get this working would the WMQ admin then change the channel to SSLCAUTH(REQUIRED)
which tests authentication in the reverse direction.
I would highly recommend that you use the WMQ v7 client for a new application. This is for two reasons: 1) v6 is end-of-life as of Sept 2011; 2) the v7 code has a lot more diagnostic capability built in. The v7 client code is completely compatible with a v6 QMgr and works like the v6 client. You just don't get the v7 functionality. Download the WMQ client code free here:
IBM - MQC7: WebSphere MQ V7.0 Clients
I'm running the WMQ Hands-On Security Lab at IMPACT this year and will be posting the scripts and lab guide over the weekend at http://t-rob.net so check back for that.
Using SSL from the Oracle JVM (JSSE)
See also "What TLS cipherspecs/ciphersuites are supported when connecting from Oracle Java (non-IBM JRE) to MQ queue manager?"
In MQ Client version 8.0.0.2 there is a patch is included to use the TLS with Oracle JVM, this works with lanes answer above
The get this to work you will need the latest MQ Client that contains
IV66840: WMQ V7 JAVA/JMS: ADD SUPPORT FOR SELECTED TLS CIPHERSPECS WHEN
RUNNING IN NON-IBM JAVA RUNTIME ENVIRONMENT
http://www-01.ibm.com/support/docview.wss?uid=swg1IV66840
(download)
Depending on your location you may also need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8 (download)
To use this you have to configured by using the JVM argument:
-Dcom.ibm.mq.cfg.useIBMCipherMappings=false
Note that the default security implementation behaviour differs between Oracle and IBM JVMs :
The Oracle JSSE Reference guide says:
If the KeyManager[] parameter is null, then an empty KeyManager will be defined for this context.
The IBM JSSE Reference guide says:
If the KeyManager[] paramater is null, the installed security providers will be searched for the highest-priority implementation of the KeyManagerFactory, from which an appropriate KeyManager will be obtained.
Which means that you have to setup 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);
And then supply that to the MQ JMS client:
JmsConnectionFactory cf = ...
MQConnectionFactory mqcf = (MQConnectionFactory) cf;
mqcf.setSSLSocketFactory(sslcontext.getSocketFactory());
If using a application server this might be handled by your application server.
Try this code along with T.Robs explanations about the certificate:
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();
}
}
}
Be aware of which JRE you are using. We have had big trouble in using Sun JDK, because of a certain cryptation (TLS_RSA_WITH_AES_128_CBC_SHA) on the SSL channel to the IBM MQ. We used a X509 certeficate. In order to get it working we are using IBM JRE because it has much bigger support for certain cipher suites!
精彩评论