how do I use a smart card as KeyStore for client authentication during SSL/TLS? - authentication

have been attempting to use a PKCS#11 token (Smart card) as a KeyStore (not the TrustStore) on the client side for client authentication of a TLS connection. However, the SSL handshake fails with a SSLException with the message:
Unexpectedly, privatekey is not an RSA private key.
This cannot be true because the private key/certificate pairs on the smart card are RSA keys. Am I missing some configuration to use a smart card as a KeyStore for JSSE?
Here are my configuration details:
Firstly, configured the Sun PKCS#11 Provider to work with an 'ActivCard' dll that interfaces with the smart card. The Sun PKCS#11 Provider configuration file just contains the 'name' and 'library' attributes.
The instantiation of the SunPKCS#11 provider looks like this:
java.security.AuthProvider provider =
new sun.security.pkcs11.SunPKCS11.SunPKCS11(<Configuration file>);
Then, the instantiation of a java.security.KeyStore object from the smart card is done using this code below:
KeyStore.ProtectionParameter thePasswordProtection =
new KeyStore.PasswordProtection( null );
KeyStore.Builder theBuilder =
KeyStore.Builder.newInstance( "PKCS11", provider, thePasswordProtection );
java.security.KeyStore theKeyStore = theBuilder.getKeyStore();
Moreover, this instantiated KeyStore is used to make a KeyManagerFactory to be used by JSSE using the code below:
KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.
getInstance( "SunX509", "SunJSSE" );
kmf.init( theKeyStore, <smart card pin> );
This KeyManagerFactory is used to then initialize an SSLContext which is then used to instantiate an SSLSocket.
As per instructions in Oracle's JSSERefGuide for Java 6, this is all I need to do for it to work. Although it is not required to set the below system properties while using the keystores programmatically, I also tried adding the system properties:
javax.net.ssl.keyStoreType to PKCS11,
javax.net.ssl.keyStore to NONE and
javax.net.ssl.keyStoreProvider to the name specified for the Sun PKCS#11 provider in its configuration file.
Any ideas what I am doing wrong here? Any pointers or thoughts would be much appreciated.

Related

What's meaning of client SSL authentication and server SSL authentication in X509TrustManager?

I understand "certificate chain provided by the peer", if certificate provided by the peer is in the X509TrustManager, the certificate is trusted, so is it just need a checkTrusted instead of checkClientTrusted and checkServerTrusted, i don't understand what's the difference? Can anyone explains?
https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/X509TrustManager.html
void checkClientTrusted(X509Certificate[] chain, String authType):
Given the partial or complete certificate chain provided by the peer, build a certificate path to a trusted root and return if it can be validated and is trusted for client SSL authentication based on the authentication type.
void checkServerTrusted(X509Certificate[] chain, String authType):
Given the partial or complete certificate chain provided by the peer, build a certificate path to a trusted root and return if it can be validated and is trusted for server SSL authentication based on the authentication type.
In the early SSL/TLS protocols that existed when JSSE was designed, there was a significant difference between the constraints on and thus validation of server cert (linked to the key_exchange portion of the ciphersuite) versus client cert (mostly independent of key_exchange but controlled by CertReq); see rfc2246 7.4.2 and 7.4.4 (modified by rfc4492 2 and 3). Although authType is a String in both checkServer and checkClient, the values in it and the processing of them in the default TrustManager were significantly different.
TLS1.2, implemented (along with 1.1) by Java 7 and early 8, changed the cert constraints to also use a new signature_algorithms extension (from client) and field (from server) in combination with the prior fields. As a result in Java 7 up the interface you link is functionally replaced by a subtype class https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/X509ExtendedTrustManager.html which in addition to the new constraint checks also moves to the JSSE layer only in checkServer the hostname checking aka endpoint identification formerly done at a higher level like HttpsURLConnection (if at all). The extended class additionally takes an SSLSocket or SSLEngine argument (as applicable) which allows access to the additional information for sigalgs and hostname.
Finally TLS1.3, implemented in java 11 up and backported to 8u261 up, uses only extensions, now two of them, and not the ciphersuite at all, for cert selection and checking. In 1.3 the extended API is still called but the value in authType is meaningless and must not be used, and in at least some cases I have looked at is in fact empty, so the actual (rfc5280) validation is in fact the same for both directions. But as above checkServer does endpoint identification if applicable while checkClient does not.
See an actual difference in https://security.stackexchange.com/questions/158339/ssl-tls-certificate-chain-validation-result-in-invalid-authentication-type-d (note: 2017 was before TLS1.3 existed)
and compare javax.net.ssl.SSLContext to trust everything sslContext.init(keyManagerFactory.getKeyManagers(), ???, new SecureRandom()); .

Make Https request with the netty4-http component of Apache Camel

I exposed a simple REST service with Apache Camel like Spring boot microservice, which creates a request to a service in https, using the netty4-http component.
public class RoutingTest extends RouteBuilder {
#Override
public void configure() throws Exception {
restConfiguration()
.host("localhost")
.port("8080");
rest().post("test")
.route()
.setBody(constant("message=Hello"))
.setHeader(Exchange.HTTP_METHOD, constant(HttpMethod.POST))
.setHeader(Exchange.CONTENT_TYPE, constant("application/x-www-form-urlencoded"))
.to("netty4-http:https://localhost/service/test");
}
}
When i call http://localhost:8080/test, I get 400 Bad Request error when the routing call https://localhost/service/test service.From the logs I read that the request arrives in HTTP instead HTTPS format and I don't understand why:
You're speaking plain HTTP to an SSL-enabled server port. Instead use
the HTTPS scheme to access this URL, please.
If I invoke the service https://localhost/service/test with Postman, it works correctly.
SSL is configured with a Self-signed certificate.
How do I create a correct https request with the netty component in apache camel? The documentation only suggests the replacement of the protocol, at most a few options which however do not work.
UPDATE (SOLVED SEE BELOW)
I updated the call in this way
.to("netty4-http:https://localhost/dpm/idp/oauth/token?ssl=true&sslContextParameters=#sslContextParameters");
The ssl = true parameter is mandatory and I have also configured the bean for SSLContextParameters like this:
#Bean(name = "sslContextParameters")
public static SSLContextParameters sslParameters() throws KeyManagementException, GeneralSecurityException, IOException {
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("C:/myfolder/test.jks");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("jskPassword");
SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(new TrustSelfSignedStrategy());
SSLContext sslcontext = builder.build();
scp.createSSLContext().setDefault(sslcontext);
return scp;
}
I am fighting a bit with the classes that are deprecated. For testing I leave only one method deprecated because I should work with inheritance.
If I understood correctly, I had to generate a JKS file for the trust zone, starting from my self-signed certificates (.crt and .key files). Once done, I added the instructions for the KeyStoreParameters with the password.
It is almost solved, but now I am getting this error when i execute the
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
You probably need to configure a sslContextParameters object that you can use to configure the Netty component for SSL.
I am not sure about the parameter name. The docs say sslContextParameters, but I thought it was sslContextParametersRef.
.to("netty4-http:https://localhost/service/test?sslContextParametersRef=#sslConfig");
The #sslConfig means that Camel can get the object from the registry with the identifier sslConfig. So for example with Spring this would be a Spring managed Bean with ID sslConfig.
The Netty component (not http) also has a parameter ssl=true. No idea if this is also needed for Netty-http. So you will have to test a bit with these different parameters.
By the way the docs of the Netty component have an SSL example with context parameter configuration etc. Have a look at it.
Resolved. Some instructions needed for the self-signed certificate were missing.
Below is the complete bean.
#Bean(name = "sslContextParameters")
public static SSLContextParameters sslParameters() throws KeyManagementException, GeneralSecurityException, IOException {
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("C:/myfolder/test.jks");
ksp.setPassword("jskPassword");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("jskPassword");
SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLContext sslcontext = builder.build();
scp.createSSLContext().setDefault(sslcontext);
// Necessary for the the self-signed server certificate
TrustManagersParameters tmp = new TrustManagersParameters();
tmp.setKeyStore(ksp);
scp.setTrustManagers(tmp);
return scp;
}
As for the test.jks file, I created it with keytool, the tool supplied with the JDK for managing certificates (creation, export and import).
In my case having already created the certificate with OpenSSL, I had to create only the JKS (Java Keystore) file to be imported. For it is necessary to convert the certificate in the P12 file (it should be an archive) and finally in the JKS.
During the operations you will be asked to enter passwords for both files
- openssl pkcs12 -export -in test.crt -inkey test.key -out test.p12
- keytool -importkeystore -srckeystore test.p12 -destkeystore test.jks -srcstoretype pkcs12
- keytool -importkeystore -srckeystore test.jks -destkeystore test.jks -deststoretype pkcs12
here test is the name of my certificate file. The last operation is not mandatory but it is recommended by keytool itself in order to migrate the JKS format, proprietary format if I understand correctly, to the more common PKCS12 format.
The value jskPassword in the code is the password I set when creating the keystore.
I hope it will help.

Is it possible to reuse Java trusted certificates for javax.ws.rs (Resteasy implementation)?

Suppose we need to trust a self-signed SSL certificate. As an example, let's use https://self-signed.badssl.com/.
Since the signer is not a "proper" authority, Java doesn't trust it and refuses to connect to that server. However, after
$ cd $JAVA_HOME/jre/lib/security
$ keytool -import -trustcacerts -alias ... -file ... -keystore cacerts
and restart of the application, the following code works:
new URL ("https://self-signed.badssl.com/").openConnection ().getResponseCode ()
and returns 200 (OK), without throwing an exception. I.e. basic Java way of opening an HTTPS connection now works, since the certificate is now trusted.
However, this doesn't have any visible effect on javax.ws.rs Client (as implemented in Resteasy, at least) and I still get an exception:
javax.ws.rs.ProcessingException: Unable to invoke request
at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:287)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:407)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder.method(ClientInvocationBuilder.java:273)
[...]
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1506)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:535)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:403)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:304)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:611)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:446)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:863)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:283)
... 90 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1488)
... 107 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:146)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 113 more
It seems as Resteasy doesn't take "standard" keystore into account. But I would rather like to have a central (machine-specific) place for additional trusted keys and not bother how exactly application uses them, with URL.openConnection or javax.ws.rs.
Question
Is it possible to make javax.ws.rs Client use the same keystore as "normal" Java HTTPS connection mechanism?
Setting the SSL context when creating the Client instance
In the ClientBuilder API there's a method that allows you to set the SSLContext:
public abstract ClientBuilder sslContext(SSLContext sslContext)
Set the SSL context that will be used when creating secured transport connections to server endpoints from web targets created by the client instance that is using this SSL context. The SSL context is expected to have all the security infrastructure initialized, including the key and trust managers.
Setting a SSL context instance resets any key store or trust store values previously specified.
Parameters:
sslContext - secure socket protocol implementation which acts as a factory for secure socket factories or SSL engines. Must not be null.
Returns:
an updated client builder instance.
Throws:
NullPointerException - in case the sslContext parameter is null.
Assuming you have added the certificate to cacerts trust store, you could use the default SSLContext when creating your Client instance.
Client client = ClientBuilder.newBuilder().sslContext(SSLContext.getDefault()).build();
It should be enough. However, for some reason, the above piece of code does not work with RESTEasy, but does work with Jersey. It's very likely it's a RESTEasy bug.
The standard solution does not work with RESTEasy. What should I do?
The RESTEasy documentation states the following:
Network communication between the client and server is handled in RESTEasy, by default, by HttpClient (4.x) from the Apache HttpComponents project. [...]
RESTEasy and HttpClient make reasonable default decisions so that it is possible to use the client framework without ever referencing HttpClient, but for some applications it may be necessary to drill down into the HttpClient details. [...]
To customize the HttpClient used by RESTEeasy, do the following:
HttpClient httpClient = HttpClientBuilder.create()
.setSslcontext(SSLContext.getDefault())
.build();
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
Client client = new ResteasyClientBuilder().httpEngine(engine).build();
Then you can perform the request:
Response response = client.target("https://self-signed.badssl.com/").request().get();
System.out.println(response.getStatus());
Are there any alternatives to the SSL context?
Instead of using the SSLContext when creating your Client, you could load a KeyStore. To load cacerts trust store, you can do the following:
String filename = System.getProperty("java.home") +
"/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
The cacerts' default password is changeit.
Then create your Client instance using one of the following approaches:
Client client = ClientBuilder.newBuilder().trustStore(keystore).build();
Client client = ClientBuilder.newBuilder().keyStore(keystore, password).build();
The issue is that it doesn't work with RESTEasy, but does work with Jersey.
The solutions mentioned above were tested against the following JAX-RS Client API implementations:
jersey-client (version 2.23.1)
resteasy-client (version 3.0.18.Final)

Bouncy Castle: Creating CMS (a.k.a. PKCS7) certificate?

How do I create a CMS (a.k.a. PKCS7) certificate?
The following bouncy castle code creates a PKCS12 certificate:
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(tbsCert);
v.add(sigAlgId);
v.add(new DERBitString(signature));
X509CertificateObject clientCert = new X509CertificateObject(Certificate.getInstance(new DERSequence(v)));
PKCS12BagAttributeCarrier bagCert = clientCert;
bagCert.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
new DERBMPString("Certificate for IPSec WLAN access"));
bagCert.setBagAttribute(
PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
new SubjectKeyIdentifierStructure(pubKey));
I see there is CMSSignedDataGenerator in the API, but I am not if it's applicable to my case and if so how....
I also don't understand why, if the created certificate is a PKCS12 one, then why do they use PKCS9 variables in order to built it.
There is no such thing as a CMS certificate or PKCS#12 certificate.
CMS is the cryptographic message syntax. It specifies a container format that may contain X5.09 compatible certificates of the signer. PKCS#12 is a container format for cryptographic objects, it is often used to store one or more certificate/private key pairs. PKCS#9 explicitly defines attributes for X5.09 certificates.
You probably just need to build an X5.09 certificate, possibly using PKCS#9 defined attributes. Those certificates should be compatible with both CMS and PKCS#12.

Connecting to a Websphere MQ in Java with SSL/Keystore

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!