Connecting to IBM MQ is really an annoying task, whether for the configuration side or programming side. In this post, I will go through all the items needed to successfully connect to IBM MQ.
Introduction
Supporting IBM MQ in your code is not an easy task. I can say the main reason for that is the lack of samples, very complicated configurations, lack of proper and easy documentation and last but not the least, a very bad user experience by IBM itself.
Background
To be able to understand this tip, you need the following:
- Basic programming knowledge
- IBM MQ installed (information in this post has been tested on versions 7.5, 8.0 and 9.0).
- This code was tested using .NET Framework 4.7.2, but I am sure the code will work fine with 4.5 and 3.5.
- This code was tested using IBM MQ DLL's version 8.0.0.5 amqmdnet.dll, amqmdxcs.dll.
Using the Code
Configuration - Normal Mode
Before we start our code, we need to start the proper configuration of our IBM MQ, the best and easiest way is through command. There are many ways to configure it, so if you prefer another way, that's Ok, as long as you know it’s working fine. Also, many of the below commands might not seem necessary.
*** Please note that the below configurations are not suitable for production servers. ***
Before We Start With Ibm Mq Configuration, Let's Create a New Windows User and Call It Mquser
. This User Must Be a Member of the Group "Mqm". This Group Will Be Available After Installing Ibm Mq.
Create a New Queue Manager with name QM
:
crtmqm QM
Start Queue Manager:
strmqm QM
Start MQSC to execute commands for our Queue Manager:
runmqsc QM
// Expected Output:
// 5724-H72 (C) Copyright IBM Corp. 1994, 2011. ALL RIGHTS RESERVED.
// Starting MQSC for queue manager QM.
Create a new Local Queue with name (Queue1
):
DEFINE QLOCAL (QUEUE1)
// Expected Output:
// 1 : DEFINE QLOCAL (QUEUE1)
// AMQ8006: WebSphere MQ queue created.
Disable CHLAUTH
rules:
ALTER QMGR CHLAUTH (DISABLED)
// Expected Output:
// 2 : ALTER QMGR CHLAUTH (DISABLED)
// AMQ8005: WebSphere MQ queue manager changed.
Create a new Channel with name CHANNEL1
and set the value of MCAUSER
to our user MQUser
:
DEFINE CHANNEL (CHANNEL1) CHLTYPE (SVRCONN) TRPTYPE (TCP) MCAUSER('MQUser')
// Expected Output:
// 3 : DEFINE CHANNEL (CHANNEL1) CHLTYPE (SVRCONN) TRPTYPE (TCP)
// AMQ8014: WebSphere MQ channel created.
Create a listener:
DEFINE LISTENER (LISTENER1) TRPTYPE (TCP) CONTROL (QMGR) PORT (1414)
// Expected Output:
// 4 : DEFINE LISTENER (LISTENER1) TRPTYPE (TCP) CONTROL (QMGR) PORT (1414)
// AMQ8626: WebSphere MQ listener created.
Start our listener:
START LISTENER (LISTENER1)
// Expected Output:
// 5 : START LISTENER (LISTENER1)
// AMQ8021: Request to start WebSphere MQ listener accepted.
Last command, close command:
end
// Expected Output:
// 6 : end
// 6 MQSC commands read.
// No commands have a syntax error.
// All valid MQSC commands were processed.
Quick Connect Test
Using the below, you can easily connect to IBM MQ, noting the following:
- UTF-8 is not a must, you can use UTF-16.
- Many optional params not mentioned (below sample) are properties of object
queueMessage
if needed:
CorrelationId
MessageId
ReplyToQueueName
- The code will type IBM MQ error code. A full list of the error codes can be found on the IBM website.
- Username and Password in our configuration is not needed, uncomment the username / password assignment if needed.
using IBM.WMQ;
using System;
using System.Collections;
using System.Text;
namespace MQTest
{
class Program
{
static void Main(string[] args)
{
string strQueueManagerName = "QM";
string strChannelName = "CHANNEL1";
string strQueueName = "QUEUE1";
string strServerName = "127.0.0.1";
int intPort = 1414;
string strMsg = "Hello IBM, this is a message";
Hashtable queueProperties = new Hashtable
{
{ MQC.HOST_NAME_PROPERTY, strServerName },
{ MQC.CHANNEL_PROPERTY, strChannelName },
{ MQC.PORT_PROPERTY, intPort },
{ MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED }
};
try
{
MQQueueManager myQM = new MQQueueManager
(strQueueManagerName, queueProperties);
MQMessage queueMessage = new MQMessage();
queueMessage.Format = MQC.MQFMT_STRING;
queueMessage.CharacterSet = Encoding.UTF8.CodePage;
queueMessage.Write(Encoding.UTF8.GetBytes(strMsg));
var queue = myQM.AccessQueue
(strQueueName, MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING);
MQPutMessageOptions queuePutMessageOptions = new MQPutMessageOptions();
queue.Put(queueMessage, queuePutMessageOptions);
queue.Close();
Console.WriteLine("Success");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
}
Configuration - SSL
Now let’s start the complicated part - configuring a secure connection between IBM MQ and your client code. Before we start, there are many important notes that you need to know:
- There are two ways to connect to IBM MQ with SSL, the first one which the client shares is its certificate. And the other way is without client sharing the certificate.
- This code connects to IBM MQ over SSL using what is called "anonymous IBM MQ connection". More details can be found here. In this mode, the client doesn’t send its certificate.
- During my work with IBM, I found that many of the parameters defined inside IBM MQ assemblies are not used and even useless. I might be mistaken but all evidences show otherwise. Through this post, I will add the word (useless) next to each of these parameters, maybe someone will explain this to me and to everyone else in the comments of this article.
To start SSL configuration with SSL, let's go back to the command prompt:
- Navigate to SSL folder under your Queue Manager folder, this can be found under your installation location which by default is: "C:\Program Files (x86)\IBM\WebSphere MQ\Qmgrs\QM\ssl":
pushd "C:\Program Files (x86)\IBM\WebSphere MQ\Qmgrs\QM\ssl"
- Execute command to create the ssl repository with a name matching your Queue manager name and password of
12345
.
runmqckm -keydb -create -db QM.kdb -pw 12345 -type cms -stash
- Let's create a self signed certificate so we can use it for our testing:
runmqckm -cert -create -db QM.kdb -dn "CN=QM,OU=QM,O=SunJ,L=Amman,S=Amman,C=JO"
-pw 12345 -label ibmwebspheremqqm -size 2048 -expire 365 -sig_alg SHA256_WITH_RSA
It's very important to notice the following:
- QM.kdb is the repository that we created in Step 2 which matches the Queue Manager name.
-label
in the command must be ibmwebspheremq
followed by QM
our Queue Manager name in lower case. - All certificate params like '
L=, S=, C=
' can be changed as you wish. - The remaining param must be left as it is, unless you know what exactly you are doing.
- Extract the certificate?
We are not going to extract the certificate here as we mentioned above, we will be connecting without sending a certificate from the client side. A very important note in case you want to use the other way for connecting is:
- IBM MQ client assemblies go to the trusted store and only loads a certificate with the name
ibmwebspheremq
followed by your local machine user that is running the code in lower case. This takes me to the first (useless) parameter in IBM MQ assemblies which is MQEnvironment.CertificateLabel
or MQC.label
.
This was very strange to me... I even decompiled IBM assembly to see if it’s actually used or not and below is the only code that is used to load the certificate. It might be there for future use, but for sure, it’s very confusing and misleading.
RemoteCertificateValidationCallback(true),
new LocalCertificateSelectionCallback(this.FixClientCertificate));
var storeLocation = StoreLocation.LocalMachine;
X509Store x509Store = new X509Store(StoreName.My, storeLocation);
x509Store.Open(OpenFlags.OpenExistingOnly);
X509Certificate2Collection x509Certificate2Collection =
new X509Certificate2Collection();
X509Certificate2Enumerator enumerator =
x509Store.Certificates.GetEnumerator();
var clientCertName =
string.Concat("ibmwebspheremq", Environment.UserName.ToLower());
while (enumerator.MoveNext())
{
X509Certificate2 current = enumerator.Current;
if (current.FriendlyName.ToLower() != clientCertName)
{
continue;
}
x509Certificate2Collection.Add(current);
}
More details about this from IBM website under IBM WebSphere MQ client.
- As you can see in IBM code, it’s looking for the Friendly name to find the certificate, which will be empty if you use the
runmqckm -cert -extract
command. My recommendation is to use PowerShell to verify your license friendly name and change it. Otherwise, your code won’t pick it up.
- Configure your Queue Manager to use SSL:
- Right click your queue manager and choose Properties.
- From the left menu, choose SSL.
- In the SSL repository, set the location and the name of the repository that we created in the first step "QM.kdb" but do not add the extension with it. The final path will be C:\Program Files (x86)\IBM\WebSphere MQ\qmgrs\QM\ssl\QM. A very common mistake is either to add the extension or forget the name of repository, unfortunately IBM WebSphere will not notify that you have put invalid key at all.
- Now click OK, then Yes to the confirmation dialog.
- The final step is configuring your Channel:
- Right click your channel and click Properties.
- Again, from the right menu, choose SSL.
- We need to set the SSL Cipher Specs value to "
TLS_RSA_WITH_AES_128_CBC_SHA256
", but before we do this brings our 2nd (useless) parameter "MQEnvironment.SSLCipherSpec
" or "MQC.SSL_CIPHER_SPEC_PROPERTY
".
You can set this parameter in your code to whatever you want, it won't make any difference. In fact, the client will use windows default cipher specs which is set in your group policy which usually defaults to TLS_RSA_WITH_AES_128_CBC_SHA256
. If this was not the case, you can configure it with the following steps:
-
From the Group Policy Management Console, go to Computer Configuration > Administrative Templates > Network > SSL Configuration Settings.
-
Double-click SSL Cipher Suite Order, and then click the Enabled option.
-
Right-click SSL Cipher Suites box and select Select all from the pop-up menu.
-
Replace the list in the SSL Cipher Suites with TLS_RSA_WITH_AES_128_CBC_SHA256
.
-
Click OK or Apply.
This is even more strange than the certificate label - you can see many of the samples on IBM Website show that you need to set the value in your code as you configured it inside your channel. But what it actually does is it just tells the client to use SSL code. So imagine this is a flag that is called "Use SSL" or not.
You can try to change it to TLS_RSA_WITH_AES_256_CBC_SHA256
in both your code and Channel SSL configuration and you will get the below error inside the event viewer.
- Final step and as we mentioned before, our client code will not send a certificate from its side, so we need to set SSL Authentication value to Optional.
- Click Ok.
Modify Code to Support SSL
Our code won't work anymore, so let's add few more lines to modify our code with the SSL, all that we need to add is:
MQEnvironment.SSLCipherSpec = "TLS_RSA_WITH_AES_256_CBC_SHA256";
As I mentioned before, although the SSLCipherSpec
is useless to set Cipher Specs, it works as a flag to switch on SSL mode.
Full Final Code
using IBM.WMQ;
using System;
using System.Collections;
using System.Text;
namespace MQTest
{
class Program
{
static void Main(string[] args)
{
string strQueueManagerName = "QM";
string strChannelName = "CHANNEL1";
string strQueueName = "QUEUE1";
string strServerName = "127.0.0.1";
int intPort = 1414;
string strMsg = "Hello IBM, this is a message";
MQEnvironment.SSLCipherSpec = "TLS_RSA_WITH_AES_256_CBC_SHA256";
Hashtable queueProperties = new Hashtable
{
{ MQC.HOST_NAME_PROPERTY, strServerName },
{ MQC.CHANNEL_PROPERTY, strChannelName },
{ MQC.PORT_PROPERTY, intPort },
{ MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED }
};
try
{
MQQueueManager myQM =
new MQQueueManager(strQueueManagerName, queueProperties);
MQMessage queueMessage = new MQMessage();
queueMessage.Format = MQC.MQFMT_STRING;
queueMessage.CharacterSet = Encoding.UTF8.CodePage;
queueMessage.Write(Encoding.UTF8.GetBytes(strMsg));
var queue = myQM.AccessQueue
(strQueueName, MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING);
MQPutMessageOptions queuePutMessageOptions = new MQPutMessageOptions();
queue.Put(queueMessage, queuePutMessageOptions);
queue.Close();
Console.WriteLine("Success");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
}
Points of Interest
IBM MQ is one of the leading queuing systems that is created and maybe the most commonly used. However, IBM fails badly by making what's supposed to be a very easy job a really long and hard process of trial and error.
Between the lack of proper validation on IBM MQ WebSphere, hidden properties and rules that can be seen by special command lines "Such as main block rule" to the unused misleading variables and configurations inside the published assemblies.
Based on my findings while working with IBM, I am sure there are many other params that can be considered as unused as well. Although on the other hand, all of these might be a mistake on my side.
History
- 19th May, 2020: Initial version