Tag Archives: IBM MQ - Page 5

Setup IBM MQ v9 for Java clients over SSL

Time for another IBM MQ example. This time it is for connecting to IBM MQ with a Java client over SSL. I’m going to use self-signed certificates in this example to eliminate any certificate chain problems. The source code for the Java client can be found below. Time to start creating the user to use for this

A. Create a new user that the client can use to connect with. Let’s call him “bob”
* Bob needs to be created on OS level since this is what IBM MQ uses to authorise users
* Bob should NOT be a member of the mqm group since it would make him a privileged user (and privileged users are blocked by default)
* Bob does not need login privileges on OS level (/sbin/nologin)

B. Now we need to grant this user some basic privileges
I am here going to use the setmqaut program but you can also use MQExplorer if you like a GUI more

setmqaut -m MYQM01 -t qmgr -p bob -all +connect +inq
setmqaut -m MYQM01 -t queue -n MYQUEUE -p bob -all +put +get

Connect and inquiry for the queue manager and get and put on our example queue.

C. Time to create the client certificate and place it into a jks
For this I am going to use the ikeycmd program shipped with MQ (<mqserver installation dir>/java/jre64/jre/bin/ikeycmd)
Create client database

ikeycmd -keydb -create -db "client.jks" -pw clientpass -type jks  

Create client certificate

ikeycmd -cert -create -db "client.jks" -pw clientpass -label ibmwebspheremqbob -dn "CN=bob,OU=bob,O=Bobs Company,C=SE" -expire 365  

One thing to note here is the label for the client certificate. This has to be in the form of: ‘ibmwebspheremq’ + ‘client username’ (all lower case)
In our case ‘ibmwebspheremqbob’ since our user is called Bob

To check that the certificate is in the jks

ikeycmd -cert -list -db "client.jks" -pw clientpass 

This will give you the label of the certificates in the jks. In our case: ‘ibmwebpsheremqbob’

D. Let us create the server certificate now
Create server database

ikeycmd -keydb -create -db "/var/mqm/qmgrs/MYQM01/ssl/MYQM01.kdb" -pw serverpass -type cms -expire 1000 -stash  

Create server certificate

ikeycmd -cert -create -db "/var/mqm/qmgrs/MYQM01/ssl/MYQM01.kdb" -pw serverpass -label ibmwebspheremqmyqm01 -dn "CN=MYQM01,OU=ICC,O=GU,C=SE" -expire 1000 -sig_alg SHA256_WITH_RSA  

A word about the certificate label here. Default name for this certificate is in the form of: ‘ibmwebspheremq’ + ‘queue manager name’ (all lower case)
In our case this becomes: ‘ibmwebpsheremqmyqm01’

If you need to change the label (or want to be able to present different certificates on different channels) you can set the CERTLABL property on the queue manager (or channel). The CERTLABL label should then correspond to the label in the .kbd file

To check that the certificate is in the kbd

ikeycmd -cert -list -db "/var/mqm/qmgrs/MYQM01/ssl/MYQM01.kdb" -pw serverpass  

This will give you the label of the certificates in the kbd. In our case: ‘ibmwebpsheremqmyqm01’

E. Time for the certificate exchange between client and server
Extract the public part of the client certificate

ikeycmd -cert -extract -db "client.jks" -pw clientpass -label ibmwebspheremqbob -target client.crt -format ascii  

Add the client cert to the server

ikeycmd -cert -add -db "/var/mqm/qmgrs/MYQM01/ssl/MYQM01.kdb" -pw serverpass -label ibmwebspheremqbob -file client.crt -format ascii  

Check so that both certificates are in the kbd

ikeycmd -cert -list -db "/var/mqm/qmgrs/MYQM01/ssl/MYQM01.kdb" -pw serverpass  

This should yield the result:

ibmwebspheremqbob
ibmwebspheremqmyqm01

F. and now from the server to the client
Extract the public part of the server certificate

ikeycmd -cert -extract -db "/var/mqm/qmgrs/MYQM01/ssl/MYQM01.kdb" -pw serverpass -label ibmwebspheremqmyqm01 -target MYQM01.crt -format ascii  

Add the server certificate to the client jks

ikeycmd -cert -add -db "client.jks" -pw clientpass -label ibmwebspheremqmyqm01 -file MYQM01.crt -format ascii  

Check so that both certificates are in the jks

ikeycmd -cert -list -db "client.jks" -pw clientpass  

This should now yield the result:

ibmwebspheremqmyqm01
ibmwebspheremqbob

Certificates are now done. Time to setup the MQ objects. For this I’m going to use the runmqsc program, eq. runmqsc MYQM01
G. Queue manager needs to know where the kdb file is

ALTER QMGR SSLKEYR('/var/mqm/qmgrs/MYQM01/ssl/MYQM01')  

NOTE: We omit the ‘.kbd’ when setting the path in the queue manager

Another thing we are going to do is to set FIPS to false in this example

 
ALTER QMGR SSLFIPS(NO)  

H. There is also need for a server connection channel for the client to connect to

DEFINE CHANNEL('CLIENTS') CHLTYPE(SVRCONN) TRPTYPE(TCP) SSLCIPH(TLS_RSA_WITH_AES_128_CBC_SHA256) MCAUSER('bob') SSLCAUTH(REQUIRED) REPLACE  

A few words on SSLCIPH and SSLCAUTH. SSLCIPH is the encryption cipher to use. This cipher has to exist both on the MQ server and in the Java installation that the client runs on.
SSLCAUTH(REQUIRED) tells MQ that the client also should present a certificate when connecting, so called two-way certificate exchange.

I. When all MQ objects have been created/changed we need to refresh the SSL cache in MQ

REFRESH SECURITY TYPE(SSL)  

J. Time to test our setup with a little bit of java code
——————————————–
JAVA EXAMPLE CLIENT CODE
——————————————–


import java.io.IOException;
import java.util.Hashtable;

import com.ibm.mq.*;
import com.ibm.mq.constants.MQConstants;


public class MQClients {
    static private String CHANNEL = "CLIENTS";
    static private int    PORT = 1414;
    static private String HOST = "mymqhost.se";
    static private String QMANAGER = "MYQM01";
    static private String QUEUE = "MYQUEUE";
    static private String USER = "bob";
    static private Hashtable<String, Object> props = 
                                       new Hashtable<String, Object>();
    static MQQueueManager qMgr = null;

    static private void putMsgOnQueue(String message) {
        // Disabling IBM cipher suite mapping due to 
        // using Oracle Java and not IBM Java
        System.setProperty("com.ibm.mq.cfg.useIBMCipherMappings", "false");
        // Enabling SSL debug to view the communication
        //System.setProperty("javax.net.debug", "ssl:handshake");

        System.setProperty("javax.net.ssl.trustStore","client.jks");
        System.setProperty("javax.net.ssl.trustStorePassword","clientpass");
        System.setProperty("javax.net.ssl.keyStore","client.jks");
        System.setProperty("javax.net.ssl.keyStorePassword","clientpass");

        props.put(MQConstants.CHANNEL_PROPERTY, CHANNEL);
        props.put(MQConstants.PORT_PROPERTY, PORT);
        props.put(MQConstants.HOST_NAME_PROPERTY, HOST);
        props.put(MQConstants.USER_ID_PROPERTY, USER);
        props.put(MQConstants.PASSWORD_PROPERTY, "secret"); // Bobs OS password
        props.put(MQConstants.SSL_CIPHER_SUITE_PROPERTY, 
                                 "TLS_RSA_WITH_AES_128_CBC_SHA256");

        try {
            qMgr = new MQQueueManager(QMANAGER, props);

            // MQOO_OUTPUT = Open the queue to put messages 
            // MQOO_INPUT_AS_Q_DEF = Using queue-defined defaults
            int openOptions = MQConstants.MQOO_OUTPUT;

            // creating destination
            MQQueue queue = qMgr.accessQueue(QUEUE, openOptions);

            // specify the message options...
            MQPutMessageOptions pmo = new MQPutMessageOptions(); // Default

            // MQPMO_ASYNC_RESPONSE = MQPUT operation is completed without the 
            // application waiting for the queue manager to complete the call
            // Using this option can improve messaging performance, 
            // particularly for applications using client bindings.
            pmo.options = MQConstants.MQPMO_ASYNC_RESPONSE;

            // create message
            MQMessage mqMessage = new MQMessage();

            System.out.println("Writing message to queue: " + QUEUE);
            mqMessage.writeString(message);

            // Put message on queue
            queue.put(mqMessage, pmo);

            // Close queue
            queue.close();

            // Get status
            MQAsyncStatus asyncStatus = qMgr.getAsyncStatus();

            // Print status code (0 = successful)
            System.out.println(asyncStatus.reasonCode);

        } catch (MQException e) {
            System.out.println("The connection to MQ could not be 
                                established." + e.getMessage());

        } catch (IOException e) {
            System.out.println("Error while writing message." + 
                                e.getMessage());
        } finally {
            try {
                qMgr.disconnect();
            } catch (MQException e) {
                System.out.println("The connection could not be closed." + 
                                    e.getMessage());
            }
        }
    }

    static private void getMsgsFromQueue(){
        // Disabling IBM cipher suite mapping due to 
        // using Oracle Java and not IBM Java
        System.setProperty("com.ibm.mq.cfg.useIBMCipherMappings", "false");
        // Enabling SSL debug to view the communication
        //System.setProperty("javax.net.debug", "ssl:handshake");

        System.setProperty("javax.net.ssl.trustStore","client.jks");
        System.setProperty("javax.net.ssl.trustStorePassword","clientpass");
        System.setProperty("javax.net.ssl.keyStore","client.jks");
        System.setProperty("javax.net.ssl.keyStorePassword","clientpass");

        props.put(MQConstants.CHANNEL_PROPERTY, CHANNEL);
        props.put(MQConstants.PORT_PROPERTY, PORT);
        props.put(MQConstants.HOST_NAME_PROPERTY, HOST);
        props.put(MQConstants.USER_ID_PROPERTY, USER);
        props.put(MQConstants.PASSWORD_PROPERTY, "secret"); // Bobs OS password
        props.put(MQConstants.SSL_CIPHER_SUITE_PROPERTY, 
                                 "TLS_RSA_WITH_AES_128_CBC_SHA256");

        try {
            qMgr = new MQQueueManager(QMANAGER, props);

            // MQOO_INPUT_SHARED = Open the queue to read messages
            int openOptions = MQConstants.MQOO_INPUT_SHARED;

            // creating destination
            MQQueue queue = qMgr.accessQueue(QUEUE, openOptions);

            // specify the message options...
            MQGetMessageOptions gmo = new MQGetMessageOptions(); // Default
            gmo.options = MQConstants.MQPMO_ASYNC_RESPONSE;

            // create message
            MQMessage mqMessage = new MQMessage();

            // Get message from queue
            System.out.println("Fetching message from queue: " + QUEUE);
            queue.get(mqMessage, gmo);

            // Get a message from MQMessage
            String message = 
                 mqMessage.readStringOfByteLength(mqMessage.getMessageLength());

            // Display message
            System.out.println(message);

            // Close queue
            queue.close();

            // Get status
            MQAsyncStatus asyncStatus = qMgr.getAsyncStatus();

            // Print status code (0 = successful)
            System.out.println(asyncStatus.reasonCode);

        } catch (MQException e) {
            System.out.println("The connection to MQ could not be 
                                established." + e.getMessage());

        } catch (IOException e) {
            System.out.println("Error while fetching the message." + 
                                e.getMessage());
        } finally {
            try {
                qMgr.disconnect();
            } catch (MQException e) {
                System.out.println("The connection could not be closed." + 
                                    e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
    
        putMsgOnQueue("world");
        getMsgsFromQueue();
    }
}

NOTE: The SSL_CIPHER_SUITE_PROPERTY in the client must be the same cipher that is defined on the channel SSLCIPH property.

A few troubleshooting tips

* IBM MQ and Java does not always agree on naming so the cipher you choose in MQ might have another name in Oracle/Sun/IBM Java - be sure to check the cipher name translation table on IBM site before giving up 

* The error logs for the queue manager can be found in /var/mqm/qmanagers/MYQM01/errors

* Make sure the GSKit is installed in your MQ installation. If not you are in the risk of getting strange errors like "MQRC_UNSUPPORTED_CIPHER_SUITE" even though they are correct

* To enable SSL verbose logging uncomment the line System.setProperty("javax.net.debug", "ssl:handshake") in the example code above

Tested on IBM MQ 9.0.5.0 on Red Hat 7, client using Java version 1.8.0_141 and IBM com.ibm.mq.allclients v9.0.4.0 on OSX 10.13.6

Setup IBM MQ v9 AMQP with Java MQLight client over SSL

This “tutorial” will show how I set up a IBM MQ v9 AMQP channel with two-way self-signed SSL certificate. Since it was quite the pain to get to work I will describe the steps here so that I do not have to figure them out again later. Through this tutorial I will use the iKeyman program (<mqserver>/java/jre64/jre/bin/ikeycmd) for handling keystores and certificates. iKeyman is bundled with IBM MQ v9 (and other versions).

A. We start with creating the keystore and certificate needed for the MQ server.
NOTE: The place for the kdb file can be found (and set) in the queue manager setting SSLKEYR (default: /var/mqm/qmgrs/<queue manager name>/ssl/key)

1. ikeycmd -keydb -create -db key.kdb -type cms -pw mysecret -stash
2. ikeycmd -cert -create -db key.kdb -pw mysecret -label ibmwebspheremqmyqm01 -dn "CN=server,O=IBM,C=US" -size 2048
3. ikeycmd -cert -extract -db key.kdb -pw mysecret -label ibmwebspheremqmyqm01 -target server.arm

A word about the label and the keystore name above.The label the server uses can be found in the CERTLABL setting on the channel OR queue manager depending on your setup. I used used the label on the queue manager for this. Default CERTLABL follows the pattern: ibmwebspheremq<queue manager name> all lower case (labels are case-sensitive)
The keystore name comes from the last part of the SSLKEYR setting. In my case the last part is “key” which gives us that the “kbd” file should be named “key.kbd”

B. Create client SSL key and certificate

1. ikeycmd -keydb -create -db client.jks -type jks -pw mysecret -stash
2. ikeycmd -cert -create -db clinet.jks -pw mysecret -label user -dn "CN=user,O=IBM,C=SE" -size 2048
3. ikeycmd -cert -extract -db client.jks -pw mysecret -label user -target client.arm

C. Exchange certificates between client and server

1. ikeycmd -cert -add -db client.jks -pw mysecret -label server -file server.arm
2. ikeycmd -cert -add -db key.kdb -pw mysecret -label user -file client.arm

D. Time to create a new user that the client can use to connect with
* This user needs to be created on OS level since this is what IBM MQ uses to authorise users
* This user should NOT be a member of the mqm group since it would make him a privileged user and privileged users are blocked by default. It is also a good practise to have users that can only interact with specific objects
* The user DOES NOT need login privileges on OS level (/sbin/nologin)

For this example I create a user:

Login 'client1' 
Password: 'password1'

E. Now we need to grant this user some basic privileges, like connect and pub/sub
I am here going to use the setmqaut program but you can also use MQExplorer if you like a GUI

setmqaut -m MYQM01 -t topic -n TOP.CLIENT1.ACCOUNT.PROCESS.SRV0112 -p client1 -all +pub +sub
setmqaut -m MYQM01 -t qmgr -p client1 -all +connect

Now it is time to set up the AMQP service and the channel to access it

F. Start the AMQP service (using runmqsc <queue manager>)

START SERVICE(SYSTEM.AMQP.SERVICE)

G. Create a AMQP channel to use

DEFINE CHANNEL(AMQP.CLIENT1) CHLTYPE(AMQP) MCAUSER('client1') PORT(9090) SSLCIPH(TLS_RSA_WITH_AES_128_CBC_SHA)

This creates a AMQP enabled channel. The channel allows only one user and for this example I will use port 9090 instead of the default 5672. I have also defined the cipher to use for SSL

H. Start the newly created channel

START CHANNEL(AMQP.CLIENT1)

Time to test the setup. For this I use one of the sample programs in the MQLight bundle, namely the ‘Send’ program. Use the following options

mqlight-samples/send -s "amqps://client1:password1@localhost:9090" -t "Account/" --keystore=client.jks --keystore-passphrase=mysecret --no-verify-name

Explanation
* the -s option is simply the connection string. Note that I am using amqps. The ‘s’ at the end enables SSL protected transport using the cipher set earlier and the certificates created
* the -t options defines that we are publishing to a topic called ‘Account’
* then we simply point out the jks to use
* –no-verify-name is needed because of the self-signed certificate

A few troubleshooting tips

* List user personal and signer certificates in jks
  - ikeycmd -cert -list personal -db client.jks -pw mysecret
  - ikeycmd -cert -list ca -db client.jks -pw mysecret
  - ikeycmd -cert -list personal -db key.kdb -pw mysecret
  - ikeycmd -cert -list ca -db key.kdb -pw mysecret
* Make sure all kdb and jks files have 660 rights
* Make sure to restart the AMQP service if changing chiper
* If you have problem with more users able to access the channel then that one specified in MCUSER, check so that CHLAUTH is enabled on the queue manager
* If passwords are not checked by MQ, check the SYSTEM.DEFAULT.AUTHINFO.IDPWOS setting CHCKCLNT - this needs to be REQUIRED for password checks on unprivileged users

Tested on Red Hat Enterprise Linux Server release 7.5 (Maipo), IBM MQ v9.0.5.0 and MQLight library v1.0.2016062300