65.9K
CodeProject 正在变化。 阅读更多。
Home

连接到 IBM MQ(使用 SSL)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2020年5月19日

CPOL

7分钟阅读

viewsIcon

44539

成功连接到 IBM MQ 所需的所有项目

引言

在代码中支持 IBM MQ 并非易事。我可以说主要原因是缺少示例、配置非常复杂、缺乏恰当且易懂的文档,最后但同样重要的是,IBM 本身的用户体验非常糟糕。

背景

要理解本技巧,您需要以下条件:

  • 基本的编程知识
  • 已安装 IBM MQ(本文中的信息已在版本 7.5、8.0 和 9.0 上进行测试)。
  • 此代码使用 .NET Framework 4.7.2 进行测试,但我相信此代码也能很好地适用于 4.5 和 3.5。
  • 此代码使用 IBM MQ DLL 版本 8.0.0.5 的 amqmdnet.dllamqmdxcs.dll 进行测试。

Using the Code

配置 - 普通模式

在我们开始编写代码之前,我们需要进行 IBM MQ 的适当配置,最简单有效的方法是通过命令行。有多种配置方法,因此如果您偏好其他方法,也没关系,只要您知道它运行正常即可。此外,下面许多命令可能看起来不必要。

*** 请注意,以下配置不适用于生产服务器。***

在我们开始进行 IBM MQ 配置之前,让我们创建一个新的 Windows 用户,并将其命名为 Mquser该用户必须是“Mqm”组的成员。该组将在安装 IBM MQ 后可用。

创建一个名为 QM 的新队列管理器

crtmqm QM

启动队列管理器

strmqm QM

启动 MQSC 以执行我们队列管理器的命令

runmqsc QM

// Expected Output: 
// 5724-H72 (C) Copyright IBM Corp. 1994, 2011.  ALL RIGHTS RESERVED.
// Starting MQSC for queue manager QM.

创建一个名为 (Queue1) 的新本地队列

DEFINE QLOCAL (QUEUE1)

// Expected Output:      
// 1 : DEFINE QLOCAL (QUEUE1)
// AMQ8006: WebSphere MQ queue created.

禁用 CHLAUTH 规则

ALTER QMGR CHLAUTH (DISABLED)

// Expected Output: 
// 2 : ALTER QMGR CHLAUTH (DISABLED)
// AMQ8005: WebSphere MQ queue manager changed.

创建一个名为 CHANNEL1 的新通道,并将 MCAUSER 的值设置为我们的用户 MQUser

DEFINE CHANNEL (CHANNEL1) CHLTYPE (SVRCONN) TRPTYPE (TCP) MCAUSER('MQUser')

// Expected Output: 
// 3 : DEFINE CHANNEL (CHANNEL1) CHLTYPE (SVRCONN) TRPTYPE (TCP)
// AMQ8014: WebSphere MQ channel created.

创建监听器

DEFINE LISTENER (LISTENER1) TRPTYPE (TCP) CONTROL (QMGR) PORT (1414)

// Expected Output: 
// 4 : DEFINE LISTENER (LISTENER1) TRPTYPE (TCP) CONTROL (QMGR) PORT (1414)
// AMQ8626: WebSphere MQ listener created.

启动我们的监听器

START LISTENER (LISTENER1)

// Expected Output: 
// 5 : START LISTENER (LISTENER1)
// AMQ8021: Request to start WebSphere MQ listener accepted.

最后一个命令,关闭命令

end

// Expected Output: 
// 6 : end
// 6 MQSC commands read.
// No commands have a syntax error.
// All valid MQSC commands were processed.

快速连接测试

使用以下内容,您可以轻松连接到 IBM MQ,请注意以下几点:

  • UTF-8 不是必须的,您可以使用 UTF-16。
  • 许多未在(下面示例中)提及的可选参数是 queueMessage 对象的属性,如有需要。
    • CorrelationId
    • MessageId
    • ReplyToQueueName
  • 代码将输出 IBM MQ 错误代码。您可以在 IBM 网站上找到 完整的错误代码列表
  • 用户名和密码在我们的配置中不是必需的,如果需要,请取消注释用户名/密码赋值。
using IBM.WMQ;
using System;
using System.Collections;
using System.Text;

namespace MQTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string strQueueManagerName = "QM";
            string strChannelName = "CHANNEL1";
            string strQueueName = "QUEUE1";
            string strServerName = "127.0.0.1";
            int intPort = 1414;
            string strMsg = "Hello IBM, this is a message";

            Hashtable queueProperties = new Hashtable
            {
                { MQC.HOST_NAME_PROPERTY, strServerName },
                { MQC.CHANNEL_PROPERTY, strChannelName },
                { MQC.PORT_PROPERTY, intPort },
                { MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED }
            };

            //Set Username
            //MQEnvironment.UserId = "User";

            //Set Passowrd
            //MQEnvironment.Password = "123";

            //Define a Queue Manager
            try
            {
                MQQueueManager myQM = new MQQueueManager
                                      (strQueueManagerName, queueProperties);

                //Define a Message
                MQMessage queueMessage = new MQMessage();
                queueMessage.Format = MQC.MQFMT_STRING;
                queueMessage.CharacterSet = Encoding.UTF8.CodePage;
                queueMessage.Write(Encoding.UTF8.GetBytes(strMsg));

                //Define a Queue
                var queue = myQM.AccessQueue
                    (strQueueName, MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING);
                MQPutMessageOptions queuePutMessageOptions = new MQPutMessageOptions();
                queue.Put(queueMessage, queuePutMessageOptions);
                queue.Close();
                Console.WriteLine("Success");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();
        }
    }
}

配置 - SSL

现在让我们开始复杂的部分 - 配置 IBM MQ 和您的客户端代码之间的安全连接。在开始之前,有许多重要注意事项需要您了解:

  • 有两种方法可以通过 SSL 连接到 IBM MQ,第一种是客户端共享其证书。另一种方法是不共享客户端证书。
  • 此代码通过 SSL 连接到 IBM MQ,使用所谓的“匿名 IBM MQ 连接”。有关更多详细信息,请 在此 处查找。在此模式下,客户端不发送其证书。
  • 在我与 IBM 工作期间,我发现 IBM MQ 程序集中的许多参数并未被使用,甚至无用。我可能错了,但所有证据都表明情况并非如此。在本文中,我将在这些参数旁边加上 (无用) 一词,也许有人会在本文的评论中向我以及大家解释这一点。

要开始 SSL 配置,让我们回到命令提示符

  1. 导航到您队列管理器文件夹下的 SSL 文件夹,这可以在您的安装位置找到,默认情况下是:“C:\Program Files (x86)\IBM\WebSphere MQ\Qmgrs\QM\ssl
    pushd "C:\Program Files (x86)\IBM\WebSphere MQ\Qmgrs\QM\ssl"
  2. 执行命令以创建 SSL 存储库,其名称与您的队列管理器名称匹配,密码为 12345
    runmqckm -keydb -create -db QM.kdb -pw 12345 -type cms -stash 
  3. 让我们创建一个自签名证书,以便用于测试
    runmqckm -cert -create -db QM.kdb -dn "CN=QM,OU=QM,O=SunJ,L=Amman,S=Amman,C=JO" 
    -pw 12345 -label ibmwebspheremqqm -size 2048 -expire 365 -sig_alg SHA256_WITH_RSA 
    注意到以下几点非常重要:
    1. QM.kdb 是我们在步骤 2 中创建的与队列管理器名称匹配的存储库。
    2. 命令中的 -label 必须是 ibmwebspheremq 后跟小写的队列管理器名称 QM
    3. 所有证书参数,如“L=, S=, C=”,都可以根据您的需要进行更改。
    4. 剩余的参数必须保持不变,除非您确切知道自己在做什么。
  4. 提取证书?

    如上所述,我们在这里不提取证书,我们将连接而客户端不发送证书。如果您想使用另一种连接方式,请注意这一点:

    1. IBM MQ 客户端程序集会去查找受信任存储,并仅加载名称为 ibmwebspheremq 后跟运行代码的本地机器用户(小写)的证书。这让我发现 IBM MQ 程序集中的第一个 (无用) 参数,即 MQEnvironment.CertificateLabelMQC.label

      这对我来说非常奇怪……我甚至反编译了 IBM 程序集,看看它是否真的被使用,以下是加载证书的唯一代码。它可能用于未来,但肯定非常令人困惑且具有误导性。

      	RemoteCertificateValidationCallback(true), 
          new LocalCertificateSelectionCallback(this.FixClientCertificate));
          var storeLocation = StoreLocation.LocalMachine;
          X509Store x509Store = new X509Store(StoreName.My, storeLocation);
          x509Store.Open(OpenFlags.OpenExistingOnly);
          X509Certificate2Collection x509Certificate2Collection = 
                                          new X509Certificate2Collection();
          X509Certificate2Enumerator enumerator = 
                                     x509Store.Certificates.GetEnumerator();
          var clientCertName = 
              string.Concat("ibmwebspheremq", Environment.UserName.ToLower());
          while (enumerator.MoveNext())
          {
              X509Certificate2 current = enumerator.Current;
              if (current.FriendlyName.ToLower() != clientCertName)
              {
                   continue;
              }
              x509Certificate2Collection.Add(current);
          }

      有关 的更多详细信息,请参阅 IBM 网站上的IBM WebSphere MQ 客户端

    2. 正如您在 IBM 代码中看到的,它正在查找友好名称来查找证书,如果您使用 runmqckm -cert -extract 命令,这将是空的。我的建议是使用 PowerShell 来验证您的证书友好名称并进行更改。否则,您的代码将无法识别它。
  5. 配置您的队列管理器以使用 SSL
    1. 右键单击您的队列管理器并选择属性
    2. 从左侧菜单中,选择SSL
    3. 在 SSL 存储库中,设置我们在第一步创建的存储库的位置和名称“QM.kdb”,但不要包含扩展名。最终路径将是 C:\Program Files (x86)\IBM\WebSphere MQ\qmgrs\QM\ssl\QM。一个常见的错误是添加扩展名或忘记存储库名称,不幸的是,IBM WebSphere根本不会通知您输入了无效的密钥

    4. 现在单击确定,然后在确认对话框中单击
  6. 最后一步是配置您的通道
    1. 右键单击您的通道并单击属性
    2. 再次,从右侧菜单中,选择SSL
      • 我们需要将 SSL Cipher Specs 值设置为“TLS_RSA_WITH_AES_128_CBC_SHA256”,但在执行此操作之前,它引入了我们的第二个 (无用) 参数“MQEnvironment.SSLCipherSpec”或“MQC.SSL_CIPHER_SPEC_PROPERTY”。

        您可以在代码中将此参数设置为您想要的任何值,它不会产生任何区别。实际上,客户端将使用 Windows 默认的密码套件,该套件在您的组策略中设置,通常默认为 TLS_RSA_WITH_AES_128_CBC_SHA256。如果不是这种情况,您可以通过以下步骤进行配置:

      • 从组策略管理控制台,转到计算机配置 > 管理模板 > 网络 > SSL 配置设置

      • 双击SSL 密码套件顺序,然后单击已启用选项。

      • 右键单击SSL 密码套件框,然后从弹出菜单中选择全选

      • SSL 密码套件中的列表替换为 TLS_RSA_WITH_AES_128_CBC_SHA256

      • 单击确定应用

这比证书标签更奇怪——您可以在 IBM 网站上看到许多示例表明您需要在代码中设置与通道中配置相同的值。但实际上它只是告诉客户端使用 SSL 代码。所以想象一下这是一个名为“使用 SSL”的标志。

您可以尝试在代码和通道 SSL 配置中都将其更改为 TLS_RSA_WITH_AES_256_CBC_SHA256,您将在事件查看器中看到以下错误。

  1. 最后一步,正如我们之前提到的,我们的客户端代码将不会从其端发送证书,因此我们需要将SSL 身份验证值设置为可选

  2. 单击确定

修改代码以支持 SSL

我们的代码将不再起作用,所以让我们添加几行代码来修改我们的代码以支持 SSL,所有我们需要添加的是:

MQEnvironment.SSLCipherSpec = "TLS_RSA_WITH_AES_256_CBC_SHA256";

如前所述,尽管 SSLCipherSpec 设置 Cipher Specs 是无用的,但它作为一个标志来开启 SSL 模式。

最终完整代码

using IBM.WMQ;
using System;
using System.Collections;
using System.Text;

namespace MQTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string strQueueManagerName = "QM";
            string strChannelName = "CHANNEL1";
            string strQueueName = "QUEUE1";
            string strServerName = "127.0.0.1";
            int intPort = 1414;
            string strMsg = "Hello IBM, this is a message";

            //Enable SSL
            MQEnvironment.SSLCipherSpec = "TLS_RSA_WITH_AES_256_CBC_SHA256";

            Hashtable queueProperties = new Hashtable
            {
                { MQC.HOST_NAME_PROPERTY, strServerName },
                { MQC.CHANNEL_PROPERTY, strChannelName },
                { MQC.PORT_PROPERTY, intPort },
                { MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED }
            };

            //Set Username
            //MQEnvironment.UserId = "User";

            //Set Passowrd
            //MQEnvironment.Password = "123";

            //Define a Queue Manager
            try
            {
                MQQueueManager myQM = 
                       new MQQueueManager(strQueueManagerName, queueProperties);

                //Define a Message
                MQMessage queueMessage = new MQMessage();
                queueMessage.Format = MQC.MQFMT_STRING;
                queueMessage.CharacterSet = Encoding.UTF8.CodePage;
                queueMessage.Write(Encoding.UTF8.GetBytes(strMsg));

                //Define a Queue
                var queue = myQM.AccessQueue
                (strQueueName, MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING);
                MQPutMessageOptions queuePutMessageOptions = new MQPutMessageOptions();
                queue.Put(queueMessage, queuePutMessageOptions);
                queue.Close();
                Console.WriteLine("Success");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();
        }
    }
}

关注点

IBM MQ 是领先的消息队列系统之一,可能是最常用的。然而,IBM 在将原本应该非常简单的工作弄得异常漫长且充满试错的困难过程方面做得非常糟糕。

在 IBM MQ WebSphere 缺乏适当的验证、隐藏的属性和可以通过特殊命令行(例如主块规则)查看的规则,以及已发布程序集中未使用的具有误导性的变量和配置之间。

根据我与 IBM 合作时的发现,我相信还有许多其他参数也可以被认为是未使用的。尽管另一方面,所有这些可能是我自己的失误。

历史

  • 2020 年 5 月 19 日:初始版本
© . All rights reserved.