【问题标题】:How to connect to a WebSphere MQ Queue using SSL authentification in PHP如何在 PHP 中使用 SSL 认证连接到 WebSphere MQ 队列
【发布时间】:2018-02-15 11:55:00
【问题描述】:

我想通过 PHP 应用程序(脚本)使用 SSL 认证连接到 WebSphere MQ 队列。

  • 队列管理器版本为 7.0 或 7.5
  • MQ 客户端版本为 8.0
  • PHP 版本为 7.0 (docker)
  • 使用 PHP mqseries pecl 扩展 v0.15(带有自定义修复)

到目前为止,我能够在没有 SSL 连接的情况下进行连接。

到目前为止,我必须完成以下步骤:

  • 我已经安装了适用于 Linux 的 WebSphere MQ Client v8(在我的例子中是 CentOS)
  • 我已经下载了 mqseries php 扩展的 PECL 0.15 版本。 (作为扩展中的一个小错误,我不得不重新编译它以使其正常工作。我曾经在 MQGET 上遇到分段错误。
  • 我将 mqseries.so 链接到 php 并启用了扩展。

我成功了(没有 SSL)

  1. 连接到队列管理器
  2. 打开阅读队列
  3. 获取队列中的消息
  4. 关闭连接

当我将脚本中的 USE_SSL 设置为 true 时,我收到错误代码 2393,意思是“An MQCONN or MQCONNX call was issued with SSL configuration options specified, but an error occurred during the initialization of the SSL environment.”此错误消息非常笼统,无法帮助我确定问题出在哪里。

MQ_KEYSTORE 设置为/path/to/my/key,我的文件名是key.kdb,与documentation 建议的级别相同key.sth

脚本中的MQ_SSL_CIPHER_SPEC 与指定MQ_CHANNEL_SSL 在队列管理器上指定的相同。检查了多次。这些不是用于 JMS 连接的Cipher Suite

队列管理器上的安全缓存已刷新。

在服务器端,我检查了队列管理器的错误日志,但似乎没有看到我的通道名称。我说“似乎”是因为有很多噪音,并且有几个??? 频道名称。所以我觉得由于某种原因它没有到达队列管理器。

我还使用了 MQ 客户端安装 bin 文件夹中的“amqssslc”命令来测试我的 ssl 配置。我得到与使用 PHP 脚本相同的错误。

我还使用 WireShark 在相应的MQ_PORT 上嗅探数据包。包的内容包含证书信息。所以有些事情看起来像是 SSL 握手。

我现在对如何调试案例一无所知。有谁知道接下来要检查什么?我应该检查我的 MQ 客户端安装上的连接日志吗?

这是connection using SSL in PHP的示例

这是我的 MQ 脚本的简化版本(我删除了输出)。出于安全目的,某些常量未公开。

所有 MQSERIES_* 常量都在扩展中定义

所有 MQ_* 都是用于测试我的脚本的硬编码参数,但它们的定义没有出现在脚本摘录中。

<?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);

?>

更新

使用我不知道存在的客户端日志。我们终于发现我们的服务器没有使用正确的证书。服务器的证书实际上是自签名的,因此没有授予我们访问权限,因为我们的 .kdb 文件没有它的公钥。我们将服务器自己的公钥添加到 .kdb 文件中,使该步骤顺利进行。

正如 JoshMC 的 cmets 中所建议的,对于我们的下一个问题,我们怀疑密钥库文件中的标签错误。我们没有从 .kdb 文件中指定特定标签。来自docsFor 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.

它还声明“The certificate label cannot contain spaces.”。

我将在明天再次尝试使用正确的标签命名并发送特定的标签名称。我将尝试查看名称约定 ibmwebspheremq&lt;user_that_runs_the_php_process&gt; 是否真的会影响有效性。

更新 2

终于成功了。正如 JoshMC 所提到的,私钥需要具有特定的标签 ibmwebspheremq。我还没有尝试过 CertificateLabel。下周我可能会深入研究。

起初我们使用 ibm 的 GUI (ikeyman) 来生成 .kdb 文件。但是我们发现它有很多错误。例如,它导入两次具有相同标签的私钥(从证书自动生成的标签)。无法编辑标签名称(因此存在连接问题)。为了解决这部分问题,我们使用了命令行工具ikeycmd,它基本上在命令行上提供了相同的功能(减去错误)。要运行该命令,您需要 IBM jre(未验证,需要签出)并将其作为 java com.ibm.gsk.ikeyman.ikeycmd 运行。从这里开始,IBM 网站上有大量关于如何创建证书、重命名标签、检查详细信息等的文档。非常有趣!

【问题讨论】:

  • key.kdb 中包含什么?这是否包含带有ibmwebspheremq&lt;user_that_runs_the_php_process&gt; 之类标签的私钥?队列管理器通道是否有SSLCAUTH(REQUIRED)OPTIONAL
  • 如果这是一个完整的客户端安装,那么它会将客户端错误记录到/var/mqm/errors/AMQERR01.LOG 并且可能/var/mqm/errors/*.FDC 也会检查这些位置。如果在客户端协商通道之前连接失败,qmgr 可以记录通道名称???。您没有说队列管理器使用的是哪个版本的 MQ,但如果它是 7.5 或更低版本,则在 TLS 握手完成之前不会交换通道名称。使用 v8 和更高版本的客户端到 v8 和更高版本的服务器,他们可以在 TLS 协商期间使用 TLS SNI 交换通道名称,但 MQ 可能不会记录这一点。
  • 哇,我花了那么多时间寻找问题,但我从未见过这些日志。这是一个认证问题。所有的问题细节一直都在那里。现在只需要解决这个问题,它应该可以工作。
  • 队列管理器是 v7.0 还是 v7.5 不确定,客户端是 v8
  • 另请注意,对于 v8 及更高版本,您可以在 mqclient.ini 的 SSL 节中使用 CertificateLabel=anyvalue。可以从 /var/mqm/mqclient.ini 复制一个模板并放置在应用程序执行的目录中,也可以通过环境变量指定位置,或放置在主目录中。这记录在 IBM MQ v8 KC 页面“SSL stanza of the client configuration file”中。

标签: php ssl ibm-mq


【解决方案1】:

使用完整的 IBM MQ 客户端安装,客户端错误将记录到目录中:/var/mqm/errors

如果有任何错误,将记录到文件 AMQERR01.LOG(这将与其他两个以 0203 结尾的文件一起旋转。如果错误是 MQ 没有预料到的,它也可能会创建一个文件以.FDC 结尾并附有更多详细信息。


如果在客户端发送通道名称之前的通道协商期间连接失败,队列管理器将记录通道名称???。如果 IBM MQ 队列管理器使用 v7.5 或更低版本,则在 TLS 握手完成之前不会交换通道名称。

随着 v8 和更高版本的客户端连接到 v8 和更高版本的队列管理器,MQ 将在 TLS 协商期间使用 TLS SNI 来交换通道名称,但是我不确定 MQ 是否也已增强以记录此通道名称,而在之前的版本中它登录???。请注意,IBM MQ Classes for Java 和 IBM MQ Classes for JMS client 即使在 v8 和更高版本中也不支持 SNI 功能,并且在 TLS 握手完成之前不会发送通道名称。


检查您的客户key.kdb 是否有私钥。 MQ 使用三种方式来识别要使用的私钥。

  1. 对于 MQ Client v7.5 及更早版本,私钥的标签必须是:

    ibmwebspheremq<user_that_runs_the_php_process>
    
  2. 对于 MQ 客户端 v7.5 及更早版本,另一种方法是将私钥设置为默认证书,并确保在执行程序的环境中设置了以下环境变量。请注意,这也适用于 8.0.0.7/9.0.0.1,但我更喜欢下一种方法。

     AMQ_SSL_ALLOW_DEFAULT_CERT=1
    
  3. 对于 MQ Client v8 及更高版本,您可以在 mqclient.iniSSL 节中使用 CertificateLabel=anylabelvalue。示例如下:

    SSL:
        CertificateLabel=anylabelvalue
    

    CertificateLabel 设置记录在 IBM MQ v8 KC 页面“SSL stanza of the client configuration file”中。

    mqclient.ini 的模板可以从/var/mqm/mqclient.ini 复制并放置在应用程序执行的同一目录中。该位置也可以通过环境变量指定,如果未设置该变量并且在应用程序执行的同一目录中找不到该文件,MQ 将在其他几个位置查找该文件。 IBM MQ v8 KC 页面“Location of the client configuration file”中记录了 MQ 查找此文件的各种方式

【讨论】:

    猜你喜欢
    • 2017-04-30
    • 2012-09-10
    • 2011-02-11
    • 1970-01-01
    • 2012-01-25
    • 2012-10-03
    • 2012-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多