I want to connect to a WebSphere MQ Queue using SSL authentification from a PHP application (script).
- Queue Manager Version is 7.0 or 7.5
- MQ Client Version is 8.0
- PHP Version is 7.0 (docker)
- Using PHP mqseries pecl extension v0.15 (with custom fixes)
So far I was able to connect without SSL connection.
Here are the steps I have to done so far:
- I have installed the WebSphere MQ Client v8 for Linux (CentOS in my case)
- I have downloaded the PECL 0.15 version of the mqseries php extension. (There as a small bug in the extension, I had to recompile it to make it work properly. I used to get a segmentation fault on MQGET).
- I linked the the mqseries.so to php and enabled the extension.
I succesfully (without SSL)
- Connect to the queue manager
- Open the queue for reading
- Get messages on the queue
- Close the connection
When I set the USE_SSL
in my script to true, I get the error code 2393 that means "An MQCONN or MQCONNX call was issued with SSL configuration options specified, but an error occurred during the initialization of the SSL environment.
" This error message is very generic and does not help me pin point where is the problem.
The MQ_KEYSTORE
is set as /path/to/my/key
and my filename is key.kdb
and has at the same level key.sth
as suggested by this documentation
The MQ_SSL_CIPHER_SPEC
is the same in the script than specified by on the queue manager for the specified MQ_CHANNEL_SSL
. Checked multiple times. These are NOT the Cipher Suite used with JMS connections
The security cache has been refreshed on the queue manager.
On the server side, I checked the error logs for the queue manager and didn't seem to see my channel name. I say "seem" because there was a lot of noise and there were a few ???
channel name in the lot. So I feel like it did not reach the queue manager for some reason.
I also used the "amqssslc
" command found in the MQ Client installation bin folder to test my ssl config. I get the same error than using the PHP script.
I also used WireShark to sniff packets on the corresponding MQ_PORT
. The content of the packages contained certificate information. So there is something that looks like a SSL hand shake going on.
I am now out of ideas as of how to debug the case. Does anyone has an idea of what to check next? Is there connection logs on my MQ Client installation that I should check?
Here is an example of a connection using SSL in PHP
Here is a simplified version of my MQ script (I removed the outputs). Some of the constants are not disclosed for security purposes.
All MQSERIES_* constants are defined in the extension
All MQ_* are hardcoded parameters to test my script but their definition does not appear in the script excerpt.
<?php
// Constants defined here
$options = [
'Version' => MQSERIES_MQCNO_VERSION_4,
'Options' => MQSERIES_MQCNO_STANDARD_BINDING,
'MQCD' => [
'Version' => 7,
'ChannelName' => MQ_CHANNEL,
'ConnectionName' => sprintf('%s(%s)', MQ_HOST, MQ_PORT),
'TransportType' => MQSERIES_MQXPT_TCP,
]
];
if (USE_SSL) {
$options['MQSCO'] = [
'KeyRepository' => MQ_KEYSTORE
];
$options['MQCD']['ChannelName'] = MQ_CHANNEL_SSL;
$options['MQCD']['SSLCipherSpec'] = MQ_SSL_CIPHER_SPEC;
}
mqseries_connx(MQ_QUEUE_MANAGER, $options, $conn, $comp_code, $reason );
$mqods2 = [
'ObjectType' => MQSERIES_MQOT_Q,
'ObjectName' => MQ_QUEUE
];
mqseries_open($conn, $mqods2, MQSERIES_MQOO_INPUT_AS_Q_DEF | MQSERIES_MQOO_FAIL_IF_QUIESCING, $obj, $comp_code, $reason);
$gmd = [];
$gmo = [
'Options' => MQSERIES_MQGMO_FAIL_IF_QUIESCING | MQSERIES_MQGMO_WAIT, 'WaitInterval' => 3000
];
$msg = "";
$data_length = "";
for ($i = 0; $i < 1000; $i++) {
mqseries_get($conn, $obj, $gmd, $gmo, 10000, $msg, $data_length, $comp_code, $reason);
if ($reason === 2033) {
printf("No more messages to process\n");
break;
}
// Business logic
}
mqseries_disc($conn, $comp_code, $reason);
?>
UPDATE
Using the client side logs that I didn't know existed. We finally found out that our server was not using a proper certificate. The server's certificate was actually self-signed, thus not granting us access because our .kdb file didn't have it's public key. We added the server's own public key to the .kdb file making that step work out.
As suggested in the comments from JoshMC, for our next problem, we suspect the label is wrong in the keystore file. We did not specify a specific label from the .kdb file. From the docs "For queue managers and clients respectively, the following sources are searched in sequence for a non-empty value. The first non-empty value determines the certificate label. The certificate label must exist in the key repository. If no matching certificate in the correct case and format is found that matches a label, an error occurs and the SSL or TLS handshake fails.
"
And it also states that "The certificate label cannot contain spaces.
".
I will try again tomorrow with proper label naming and sending a specific label name. I will try to see if the name convention ibmwebspheremq<user_that_runs_the_php_process>
actually impacts the validity.
UPDATE 2
It finally worked out. As mentioned by JoshMC, the private key needs to have the specific label ibmwebspheremq. I didn't try the CertificateLabel yet. I might dig into that next week.
At first we were using the GUI (ikeyman) from ibm to generate the .kdb file. But we found out it had quite a few bugs in it. For instance, it imports twice the private key with the same label (auto-generated label from the certificate). Editing the label name was not possible (hence the connection problems). To solve that part we used the command line tool ikeycmd
that basically offers the same features on the command line (minus the bugs). To run the command, you need the IBM jre (Not verified, needs to be checked out) and run it as java com.ibm.gsk.ikeyman.ikeycmd
. From here, there is a whole world of documentation on IBM web site about how to create a certificate, rename labels, check details, etc. Lots and lots of fun!
ibmwebspheremq<user_that_runs_the_php_process>
? Does the queue manager channel haveSSLCAUTH(REQUIRED)
orOPTIONAL
? – JoshMc/var/mqm/errors/AMQERR01.LOG
and possibly/var/mqm/errors/*.FDC
check those locations as well. The qmgr can log channel name of???
if the connection has failed prior to the client negotiating the channel. You don't say what version of MQ the queue manager is using but if it is 7.5 and less the channel name is not exchanged until after the TLS handshake is complete. With v8 and later client to v8 and later server they can use TLS SNI to exchange the channel name during TLS negotiation, but MQ may not log this. – JoshMcCertificateLabel=anyvalue
in theSSL
stanza of the mqclient.ini. A template for this can be copied from /var/mqm/mqclient.ini and placed in the directory where the application executes, the location can also be specified via a environment variable, or placed in the home directory. This is documented in the IBM MQ v8 KC page "SSL stanza of the client configuration file". – JoshMc