证书生成与配置 (C)
前置阅读
- 安全模式 / Token 概览请看 Security 加密.
- 创建 Session 时如何传 PFX 请看 配置与创建.
1. 生成客户端 PFX
C SDK 不内置 PFX 生成器, 用 OpenSSL 命令行:
Bash / Linux / macOS
openssl req -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \
-subj "/CN=MyClient/O=Darra/C=CN" \
-addext "subjectAltName=URI:urn:my-company:my-client"
openssl pkcs12 -export -out client.pfx -inkey key.pem -in cert.pem \
-password pass:123456
openssl x509 -in cert.pem -outform DER -out client.der
Windows PowerShell
$cert = New-SelfSignedCertificate `
-Subject "CN=MyClient,O=Darra,C=CN" `
-TextExtension "2.5.29.17={text}URI=urn:my-company:my-client" `
-KeyAlgorithm RSA -KeyLength 2048 -NotAfter (Get-Date).AddYears(1) `
-CertStoreLocation "Cert:\CurrentUser\My"
$pwd = ConvertTo-SecureString -String "123456" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath C:\certs\client.pfx -Password $pwd
Export-Certificate -Cert $cert -FilePath C:\certs\client.der
client.pfx 含私钥, 给客户端用; client.der 是公钥, 拷贝到服务端.
2. 服务端导入信任
把客户端公钥 (.der 或 .pem) 拷到服务端 trusted/ 目录:
| Server | 路径 |
|---|---|
| Darra SimulatorServer | <simulator>/pki/trusted/certs/ |
| open62541 example | <server>/pki/trusted/certs/ |
| UaExpert | 设置 → Certificates → Trust |
3. C 端构造时传 PFX
DarraUa_SessionConfig cfg;
DarraUa_SessionConfig_Init(&cfg);
cfg.endpoint_url = "opc.tcp://server:4840";
cfg.security_mode = DARRA_UA_MSG_SECURITY_MODE_SIGN_ENCRYPT;
cfg.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256";
cfg.user_token_type = DARRA_UA_USER_TOKEN_USERNAME;
cfg.username = "operator";
cfg.password = "secret";
cfg.client_cert_path = "C:/certs/client.pfx";
cfg.client_key_path = "123456"; /* PFX 密码 */
cfg.server_cert_path = NULL; /* NULL = TOFU */
DarraUa_SessionHandle h = DARRA_UA_INVALID_SESSION_HANDLE;
DarraUa_Session_Create(&cfg, &h);
DarraUa_Session_Connect(h);
路径分隔符
Windows 下用 / 或 \\ 都可, 不要用单 \ (会被 C 当转义符).
4. 服务端证书 TOFU
第一次连接时, 客户端会信任服务端证书并保存指纹. 之后每次连接校验指纹一致, 不一致 (中间人) 拒绝.
如果不想 TOFU, 显式传服务端证书 DER 路径:
cfg.server_cert_path = "C:/certs/server.der";
服务端证书会被严格比对.
故障排查
| StatusCode | 原因 | 解决 |
|---|---|---|
BadCertificateUntrusted | 服务端没信任客户端 | 把 client.der 拷到服务端 trusted/certs/ |
BadCertificateInvalid | 证书过期 / 未生效 | 重新生成 |
BadCertificateUriInvalid | 证书 SAN.URI 与 ApplicationUri 不匹配 | 重新生成时确保 applicationUri 一致 |
BadCertificateHostNameInvalid | 证书 SAN.DNS 与连接的 host 不匹配 | 生成时加 subjectAltName=DNS:server.example.com |
BadSecurityChecksFailed | 通用安全校验失败 | 看服务端日志查具体原因 |
BadSecurityModeRejected | 服务端不支持该 SecurityMode | GetEndpoints 看服务端实际支持哪些 |
BadSecurityPolicyRejected | 服务端不支持 Basic256Sha256 | 服务端配置开启该 Policy |
SAN URI 重要性
OPC UA 校验客户端证书 SAN 中必须有 URI: 字段, 且与 ApplicationUri 一致. 这是防伪造的关键, 必须正确设置. 用 OpenSSL 检查:
openssl x509 -in cert.pem -text -noout | grep -A1 "Subject Alternative Name"
输出应该有:
X509v3 Subject Alternative Name:
URI:urn:my-company:my-client
时间同步 (NTP)
加密模式下双方校验时间戳, 偏差超过 ~10 分钟会被拒绝. 生产环境必须配 NTP, 时间偏差控制在秒级.
# Windows
w32tm /resync
# Linux
sudo timedatectl set-ntp true
高级: 直接调 X509 / RSA / SHA / AES API
<darra_opcua/darra_opcua_security.h> 提供底层加密 API, Stack 内部已用. 一般业务无需直接调用, 仅在需要"用 SDK 内置加密做业务自己的签名"时使用.