创建支持安全功能的 OPC UA 客户端

OPC UA 的核心功能之一是安全支持,这意味着我们可以获得加密和签名协议、用户验证和授权支持。

为了实现这一点,每个应用程序实例(程序安装)都需要有自己的Application Instance Certificate 和相应的私钥。

应用程序可以自行生成自签名证书,也可以使用 OPC UA GDS 从证书颁发机构获取证书,或者干脆使用用户手动创建的证书进行配置。

由于Qt OPC UA 目前不支持证书生成或 GDS,本教程将介绍如何使用 OpenSSL 在命令行上生成自签名 OPC UA 证书。

创建新的应用程序证书

为了能生成一个正确的 x509v3 证书(包含 OPC UA 所需的所有扩展名),我们需要先设置一个包含所有必要信息的配置文件。

切记更改subjectsubjectAltName 以匹配您的情况。

subjectAltNameURI 字段中插入应用程序的ApplicationURI ,在subjectAltNameDNS 字段中插入 PC 或设备的主机名,这一点很重要。如果设备不支持主机名,并且使用的是静态 IP,也可以使用IP 字段。未来版本的Qt OPC UA 将能为您生成包含正确信息的证书。目前,您可以使用 OpenSSL 命令行工具创建证书。

示例opcuaviewer.config

[ req ]
default_bits = 2048
default_md = sha256
distinguished_name = subject
req_extensions = req_ext
x509_extensions = req_ext
string_mask = utf8only
prompt = no

[ req_ext ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
subjectAltName = URI:urn:foo.com:The%20Qt%20Company:QtOpcUaViewer,DNS:foo.com
subjectKeyIdentifier = hash
authorityKeyIdentifier=keyid:always,issuer:always

[ subject ]
countryName = DE
stateOrProvinceName = Berlin
localityName = Berlin
organizationName = The Qt Company
commonName = QtOpcUaViewer

使用此配置文件,OpenSSL 可以创建本地使用的匹配证书。

# create a self-signed certificate and private key
openssl req -new -x509  -config opcuaviewer.config -newkey rsa:2048 -keyout opcuaviewer.key -nodes -outform der -out opcuaviewer.der
# install the certificate and key into the application PKI directory
mv opcuaviewer.der /path/to/application/pki/own/certs/opcuaviewer.der
mv opcuaviewer.key /path/to/application/pki/own/private/opcuaviewer.pem
# secure private key file permissions
chmod 600 /path/to/application/pki/own/private/opcuaviewer.pem

必须确保私钥的文件权限,只有 UA 应用程序才能读取它。对于服务(守护进程),建议为此创建专门的非特权用户账户,并让该用户成为密钥的所有者。对于交互式应用程序,密钥应为用户个人所有。对于交互式应用程序,也可以用密码保护密钥。在这种情况下,每次启动应用程序和加载密钥时,用户都需要输入密码。因此,密码保护密钥对于无人值守的应用程序来说不是一个好的解决方案,因为这需要将密码存储在配置文件中。

您可以使用 OpenSSL 转储证书数据,检查证书内容:

        openssl x509 -in opcuaviewer.der -text -noout

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            be:aa:41:79:8a:b0:4f:9a
    Signature Algorithm: sha512WithRSAEncryption
        Issuer: C = DE, ST = Berlin, L = Berlin, O = The Qt Company, CN = QtOpcUaViewer
        Validity
            Not Before: Nov  7 14:38:52 2018 GMT
            Not After : Dec  7 14:38:52 2018 GMT
        Subject: C = DE, ST = Berlin, L = Berlin, O = The Qt Company, CN = QtOpcUaViewer
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    [ skipped ]
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Certificate Sign
            X509v3 Subject Alternative Name:
                URI:urn:foo.com:The%20Qt%20Company:QtOpcUaViewer, DNS:foo.com
            X509v3 Subject Key Identifier:
                B2:E8:5E:34:21:EA:67:CF:61:FC:14:94:18:C1:AD:13:89:83:CA:9B
            X509v3 Authority Key Identifier:
                keyid:B2:E8:5E:34:21:EA:67:CF:61:FC:14:94:18:C1:AD:13:89:83:CA:9B
                DirName:/C=DE/ST=Berlin/L=Berlin/O=The Qt Company/CN=QtOpcUaViewer
                serial:BE:AA:41:79:8A:B0:4F:9A

    Signature Algorithm: sha512WithRSAEncryption
         [ skipped ]

配置 UA 应用程序

为确保上一步创建的证书的安全性,必须

  • 配置正确的应用程序身份
    m_identity = m_pkiConfig.applicationIdentity();
  • 配置 PKI 位置,以便 SDK 可以找到证书、私钥、信任列表等。

    请参阅Qt OPC UA Viewer 示例中的代码:

    voidMainWindow::setupPkiConfiguration() {constQDirpkidir=QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)+ "/pki");if(!pkidir.exists()&& !copyDirRecursively(":/pki",pkidir.path()))        qFatal("Could not set up directory %s!", qUtf8Printable(pkidir.path()));
    
        m_pkiConfig.setClientCertificateFile(pkidir.absoluteFilePath("own/certs/opcuaviewer.der")); m_pkiConfig.setPrivateKeyFile(pkidir.absoluteFilePath("own/private/opcuaviewer.pem"))m_pkiConfig.setPrivateKeyFile(pkidir.absoluteFilePath("own/private/opcuaviewer.pem"));m_pkiConfig.setTrustListDirectory(pkidir.absoluteFilePath("trusted/certs"));m_pkiConfig.m_pkiConfig.setTrustListDirectory(pkidir.absoluteFilePath("trusted/certs"); m_pkiConfig.setRevocationListDirectory(pkidir.absoluteFilePath("trusted/crl")); m_pkiConfig.setIssuerListDirectory(pkidir.absoluteFilePath("issuers/certs"); m_pkiConfig.setIssuerRevocationListDirectory(pkidir.absoluteFilePath("issuers/crl"));constQStringListtoCreate={ m_pkiConfig.issuerListDirectory(),m_pkiConfig.issuerRevocationListDirectory() };for(constQString&dir: toCreate) {if(QDir().mkpath(dir))            qFatal("Could not create directory %s!", qUtf8Printable(dir));
        } }

    在示例中,我们从 Qt 资源系统中提取预先配置好的自有证书和受信任证书到文件系统中的可写位置。其余的签发者(撤销)列表目录则由手动创建。

PKI 文件夹布局

Qt OPC UA 使用以下文件夹布局:

文件夹说明
自己的存储应用程序自身证书的位置。
受信任受信任应用程序证书或受信任 CA 证书的列表。
签发者不受信任但需要用于检查证书签名的 CA 证书列表。
拒绝的应用程序在此存储被拒绝的证书,以便管理员以后可以信任它们。这里必须设置文件的最大数量。如果达到上限,应首先删除最旧的文件。如果没有限制,攻击者可能会用无效连接尝试填满机器硬盘。

owntrustedissuers 文件夹都包含下表定义的子目录结构。

子目录子目录
证书包含 DER 编码的 X.509 v3 证书。文件扩展名为 .der。
私钥包含私钥。文件格式可根据后端具体情况而定。PEM 编码文件的扩展名应为 .pem。PKCS#12 编码文件的扩展名应为 .pfx。根文件名应与证书目录中相应的公钥文件相同。该文件夹仅存在于own 文件夹中。
crl包含 certs 目录中任何 CA 证书的 DER 编码 CRL。文件扩展名为 .crl。

第一个连接

首次连接时,客户端需要信任服务器证书。

客户端应显示证书警告(包含证书详细信息),并提供在信任列表中保存证书的可能性。有关示例,请参阅Qt OPC UA Viewer 示例

客户端接受服务器证书后,可以尝试再次连接。现在,服务器可能会拒绝客户端的证书。通用错误代码BadSecurityChecksFailed 表示这种情况。服务器通常会将拒绝的证书存储在一个特殊的rejected 文件夹中。管理员可以将这些证书移到信任列表中,以信任客户端。这样就可以避免手动将客户证书复制到服务器机器上。

一旦服务器信任了客户端,你就可以安全地连接了。

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.