【问题标题】: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 文件中指定特定标签。来自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.”

它还声明“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(这将与其他两个以 02 和 03 结尾的文件一起旋转。如果错误是 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.ini 的 SSL 节中使用 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
    相关资源
    最近更新 更多