2
votes

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)

  1. Connect to the queue manager
  2. Open the queue for reading
  3. Get messages on the queue
  4. 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!

1
What is contained in the key.kdb? Does this contain a private key with a label like ibmwebspheremq<user_that_runs_the_php_process>? Does the queue manager channel have SSLCAUTH(REQUIRED) or OPTIONAL?JoshMc
If this is a full client install then it would log client side errors to /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.JoshMc
Wow, all that time I spent looking for the problem and I never saw those logs. It is a certification problem. All the problem details were there all along. Now just have to fix that and it should work.Lunfel
queue manager is v7.0 or v7.5 not sure and client is v8Lunfel
Also note with v8 and later you can use CertificateLabel=anyvalue in the SSL 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

1 Answers

1
votes

With a full IBM MQ client install, client side errors will be logged to the directory: /var/mqm/errors

The errors if any will be logged to the file AMQERR01.LOG (this gets rotated with two other files ending in 02 and 03. If the error is something that MQ is not expecting it may also create a file that ended in .FDC with additional details.


The Queue Manager will log a channel name of ??? if the connection has failed during the channel negotiating prior to the client the sending the channel name. If the IBM MQ the queue manager is using v7.5 or lower the channel name is not exchanged until after the TLS handshake is complete.

With v8 and later clients connecting to v8 and later queue managers, MQ will use TLS SNI to exchange the channel name during TLS negotiation, however I am unsure if MQ has also been enhanced to log this channel name where in prior versions it logged ???. Note that IBM MQ Classes for Java and IBM MQ Classes for JMS client even at v8 and later do not support the SNI feature and do not send the channel name until after the TLS handshake is complete.


Check that your client key.kdb has a private key. There are three ways which MQ uses to identify which private key to use.

  1. With MQ Client v7.5 and earlier the label of the private key must be:

    ibmwebspheremq<user_that_runs_the_php_process>
    
  2. With MQ client v7.5 and earlier an alternative is to set the private key as the default certificate and make sure the following environment variable is set in the environment which executes the program. Note this also works with 8.0.0.7/9.0.0.1 but I prefer the next method over this one.

     AMQ_SSL_ALLOW_DEFAULT_CERT=1
    
  3. With MQ Client v8 and later you can use CertificateLabel=anylabelvalue in the SSL stanza of the mqclient.ini. Example below:

    SSL:
        CertificateLabel=anylabelvalue
    

    The CertificateLabel setting is documented in the IBM MQ v8 KC page "SSL stanza of the client configuration file".

    A template for mqclient.ini can be copied from /var/mqm/mqclient.ini and placed in the same directory where the application executes. The location can also be specified via a environment variable and MQ will look in a few other locations for the file if it the variable is not set and it does not find it in the same directory where the application executes. The various ways in which MQ will look for this file is documented in the IBM MQ v8 KC page "Location of the client configuration file"