如何在 PHP 中使用 SSL 身份验证连接到 WebSphere MQ 队列
How to connect to a WebSphere MQ Queue using SSL authentification in PHP
我想从 PHP 应用程序(脚本)使用 SSL 身份验证连接到 WebSphere MQ 队列。
- 队列管理器版本为 7.0 或 7.5
- MQ 客户端版本为 8.0
- PHP 版本为 7.0 (docker)
- 使用 PHP mqseries pecl 扩展 v0.15(带有自定义修复)
到目前为止,我可以在没有 SSL 连接的情况下进行连接。
以下是我目前必须完成的步骤:
- 我已经为 Linux 安装了 WebSphere MQ 客户端 v8(在我的例子中是 CentOS)
- 我已经下载了 mqseries php 扩展的 PECL 0.15 版本。 (作为扩展中的一个小错误,我不得不重新编译它以使其正常工作。我曾经在 MQGET 上遇到分段错误)。
- 我将 mqseries.so 链接到 php 并启用了扩展程序。
我成功了(没有 SSL)
- 连接到队列管理器
- 打开阅读队列
- 获取队列中的消息
- 关闭连接
当我将脚本中的 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
32=]
脚本中的 MQ_SSL_CIPHER_SPEC
与队列管理器上指定的 MQ_CHANNEL_SSL
相同。多次检查。这些不是用于 JMS 连接的 Cipher Suite
已在队列管理器上刷新安全缓存。
在服务器端,我检查了队列管理器的错误日志,但似乎没有看到我的通道名称。我说 "seem" 是因为那里有很多噪音,而且地段中有几个 ???
频道名称。所以我觉得它由于某种原因没有到达队列管理器。
我还使用了在 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 文件没有它的 public 密钥。我们将服务器自己的 public 密钥添加到 .kdb 文件中,使该步骤生效。
正如 JoshMC 的评论中所建议的,对于我们的下一个问题,我们怀疑密钥库文件中的标签是错误的。我们没有指定 .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<user_that_runs_the_php_process>
是否真的影响有效性。
更新 2
终于解决了。正如 JoshMC 所提到的,私钥需要具有特定标签 ibmwebspheremq。我还没有尝试 CertificateLabel。我可能会在下周深入研究。
起初我们使用 ibm 的 GUI (ikeyman) 来生成 .kdb 文件。但是我们发现它有很多错误。例如,它导入两次具有相同标签(从证书自动生成的标签)的私钥。无法编辑标签名称(因此出现连接问题)。为了解决这部分问题,我们使用了命令行工具 ikeycmd
,它基本上在命令行上提供了相同的功能(除去错误)。要使用 运行 命令,您需要 IBM jre(未验证,需要签出)和 运行 为 java com.ibm.gsk.ikeyman.ikeycmd
。从这里开始,IBM 网站上有大量关于如何创建证书、重命名标签、检查详细信息等的文档。非常有趣!
对于完整的 IBM MQ 客户端安装,客户端错误将记录到目录:/var/mqm/errors
如果有任何错误,将记录到文件 AMQERR01.LOG
(这与另外两个以 02
和 03
结尾的文件一起轮换)。如果错误是 MQ 的原因没想到它还可能创建一个以 .FDC
结尾的文件,其中包含更多详细信息。
如果在客户端发送通道名称之前通道协商期间连接失败,队列管理器将记录通道名称 ???
。如果 IBM MQ 队列管理器使用的是 v7.5 或更低版本,则在 TLS 握手完成之前不会交换通道名称。
随着 v8 和更高版本的客户端连接到 v8 和更高版本的队列管理器,MQ 将在 TLS 协商期间使用 TLS SNI 交换通道名称,但是我不确定 MQ 是否也已增强以在之前的位置记录此通道名称它记录的版本 ???
。请注意,用于 Java 的 IBM MQ 类 和用于 JMS 客户端的 IBM MQ 类 即使在 v8 及更高版本中也不支持 SNI 功能,并且在 TLS 握手完成之前不发送通道名称完成。
检查您的客户端 key.kdb
是否有私钥。 MQ 使用三种方式来识别使用哪个私钥。
对于 MQ 客户端 v7.5 和更早版本,私钥的标签必须是:
ibmwebspheremq<user_that_runs_the_php_process>
对于 MQ 客户端 v7.5 及更早版本,另一种方法是将私钥设置为默认证书,并确保在执行程序的环境中设置以下环境变量。请注意,这也适用于 8.0.0.7/9.0.0.1,但我更喜欢下一种方法而不是这种方法。
AMQ_SSL_ALLOW_DEFAULT_CERT=1
使用 MQ Client v8 及更高版本,您可以在 mqclient.ini
的 SSL
节中使用 CertificateLabel=anylabelvalue
。示例如下:
SSL:
CertificateLabel=anylabelvalue
CertificateLabel
设置记录在 IBM MQ v8 KC 页面“SSL
客户端配置文件的节".
mqclient.ini
的模板可以从 /var/mqm/mqclient.ini
复制并放置在应用程序执行的同一目录中。该位置也可以通过环境变量指定,如果未设置该变量并且未在应用程序执行的同一目录中找到该文件,MQ 将在其他几个位置查找该文件。 MQ 查找此文件的各种方式记录在 IBM MQ v8 KC 页面“Location of the client configuration file”
我想从 PHP 应用程序(脚本)使用 SSL 身份验证连接到 WebSphere MQ 队列。
- 队列管理器版本为 7.0 或 7.5
- MQ 客户端版本为 8.0
- PHP 版本为 7.0 (docker)
- 使用 PHP mqseries pecl 扩展 v0.15(带有自定义修复)
到目前为止,我可以在没有 SSL 连接的情况下进行连接。
以下是我目前必须完成的步骤:
- 我已经为 Linux 安装了 WebSphere MQ 客户端 v8(在我的例子中是 CentOS)
- 我已经下载了 mqseries php 扩展的 PECL 0.15 版本。 (作为扩展中的一个小错误,我不得不重新编译它以使其正常工作。我曾经在 MQGET 上遇到分段错误)。
- 我将 mqseries.so 链接到 php 并启用了扩展程序。
我成功了(没有 SSL)
- 连接到队列管理器
- 打开阅读队列
- 获取队列中的消息
- 关闭连接
当我将脚本中的 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
32=]
脚本中的 MQ_SSL_CIPHER_SPEC
与队列管理器上指定的 MQ_CHANNEL_SSL
相同。多次检查。这些不是用于 JMS 连接的 Cipher Suite
已在队列管理器上刷新安全缓存。
在服务器端,我检查了队列管理器的错误日志,但似乎没有看到我的通道名称。我说 "seem" 是因为那里有很多噪音,而且地段中有几个 ???
频道名称。所以我觉得它由于某种原因没有到达队列管理器。
我还使用了在 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 文件没有它的 public 密钥。我们将服务器自己的 public 密钥添加到 .kdb 文件中,使该步骤生效。
正如 JoshMC 的评论中所建议的,对于我们的下一个问题,我们怀疑密钥库文件中的标签是错误的。我们没有指定 .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<user_that_runs_the_php_process>
是否真的影响有效性。
更新 2
终于解决了。正如 JoshMC 所提到的,私钥需要具有特定标签 ibmwebspheremq。我还没有尝试 CertificateLabel。我可能会在下周深入研究。
起初我们使用 ibm 的 GUI (ikeyman) 来生成 .kdb 文件。但是我们发现它有很多错误。例如,它导入两次具有相同标签(从证书自动生成的标签)的私钥。无法编辑标签名称(因此出现连接问题)。为了解决这部分问题,我们使用了命令行工具 ikeycmd
,它基本上在命令行上提供了相同的功能(除去错误)。要使用 运行 命令,您需要 IBM jre(未验证,需要签出)和 运行 为 java com.ibm.gsk.ikeyman.ikeycmd
。从这里开始,IBM 网站上有大量关于如何创建证书、重命名标签、检查详细信息等的文档。非常有趣!
对于完整的 IBM MQ 客户端安装,客户端错误将记录到目录:/var/mqm/errors
如果有任何错误,将记录到文件 AMQERR01.LOG
(这与另外两个以 02
和 03
结尾的文件一起轮换)。如果错误是 MQ 的原因没想到它还可能创建一个以 .FDC
结尾的文件,其中包含更多详细信息。
如果在客户端发送通道名称之前通道协商期间连接失败,队列管理器将记录通道名称 ???
。如果 IBM MQ 队列管理器使用的是 v7.5 或更低版本,则在 TLS 握手完成之前不会交换通道名称。
随着 v8 和更高版本的客户端连接到 v8 和更高版本的队列管理器,MQ 将在 TLS 协商期间使用 TLS SNI 交换通道名称,但是我不确定 MQ 是否也已增强以在之前的位置记录此通道名称它记录的版本 ???
。请注意,用于 Java 的 IBM MQ 类 和用于 JMS 客户端的 IBM MQ 类 即使在 v8 及更高版本中也不支持 SNI 功能,并且在 TLS 握手完成之前不发送通道名称完成。
检查您的客户端 key.kdb
是否有私钥。 MQ 使用三种方式来识别使用哪个私钥。
对于 MQ 客户端 v7.5 和更早版本,私钥的标签必须是:
ibmwebspheremq<user_that_runs_the_php_process>
对于 MQ 客户端 v7.5 及更早版本,另一种方法是将私钥设置为默认证书,并确保在执行程序的环境中设置以下环境变量。请注意,这也适用于 8.0.0.7/9.0.0.1,但我更喜欢下一种方法而不是这种方法。
AMQ_SSL_ALLOW_DEFAULT_CERT=1
使用 MQ Client v8 及更高版本,您可以在
mqclient.ini
的SSL
节中使用CertificateLabel=anylabelvalue
。示例如下:SSL: CertificateLabel=anylabelvalue
CertificateLabel
设置记录在 IBM MQ v8 KC 页面“SSL 客户端配置文件的节".mqclient.ini
的模板可以从/var/mqm/mqclient.ini
复制并放置在应用程序执行的同一目录中。该位置也可以通过环境变量指定,如果未设置该变量并且未在应用程序执行的同一目录中找到该文件,MQ 将在其他几个位置查找该文件。 MQ 查找此文件的各种方式记录在 IBM MQ v8 KC 页面“Location of the client configuration file”