编程 OpenSSL 4.0.0 升级排查:ENGINE 被删、SM2 默认 distid 变更,编译和互验都得先过一遍

2026-10-18 21:08:31

OpenSSL 4.0.0 升级排查:ENGINE 被删、SM2 默认 distid 变更,编译和互验都得先过一遍

OpenSSL 4.0 Final Release 发布于 2026-04-14(openssl-library.org)。这不是 LTS 版本,支持到 2027-05-14;下一个版本是 2026 年 10 月的 OpenSSL 4.1。从 3.x 往 4.0 走,下面这些点在编译期和运行期都会直接触发错误,建议升级前先逐条检查一遍。

不兼容变更

十六进制输出的前导 00: 和 dump 宽度

打印 RSA modulus 这类密钥数据时,如果最高字节 ≥ 0x80,旧版本会在前面多补一个 00:,4.0 去掉了。同时十六进制 dump 的宽度统一了:签名部分是 24 字节(控制在 80 字符以内),其余是 16 字节。

依赖 openssl rsa -text、openssl x509 -text 输出做解析或比对的脚本会受影响。升级前后各跑一次 diff 最直接:

openssl pkey -in key.pem -noout -text > /tmp/pkey-40.txt
openssl x509 -in cert.pem -noout -text > /tmp/x509-40.txt

PBKDF2 与 X.509 校验

PKCS5_PBKDF2_HMAC 在 FIPS provider 下开始强制执行下界检查。参数给得过小的调用会直接失败。

X509_V_FLAG_X509_STRICT 打开后新增了 AKID 校验,对应三个新错误码:

  • 缺少 AKID → X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER
  • AKID 没有任何属性 → X509_V_ERR_EMPTY_AUTHORITY_KEY_IDENTIFIER
  • authorityCertIssuer 与 authorityCertSerialNumber 没有成对出现 → X509_V_ERR_AKID_ISSUER_SERIAL_NOT_PAIRED

CRL 校验流程也追加了若干检查。严格模式下之前能过的证书链,现在可能过不去:

openssl verify -x509_strict -CAfile ca.pem cert.pem

全局清理与 ASN1_STRING

libcrypto 不再通过 atexit() 清理全局分配的数据;OPENSSL_cleanup() 改为在全局析构函数中运行,或者默认根本不运行。依赖进程退出时自动释放、或依赖 atexit 顺序做清理的逻辑要重新审视。

BIO_snprintf() 改用 libc 提供的 snprintf(),不再走内部实现。

ASN1_STRING 已经 opaque。直接访问 ->data、->length 的代码必须换成 getter:

/* 旧 */
s->data; s->length;

/* 新 */
ASN1_STRING_get0_data(s);
ASN1_STRING_length(s);

同时,大量 API 的函数签名加了 const 限定(X509 相关尤其多)。编译时会表现为函数指针类型不匹配、回调签名对不上:

make 2>&1 | grep -E "invalid conversion|discards 'const'|incompatible pointer"

时间比较接口

X509_cmp_time()、X509_cmp_current_time()、X509_cmp_timeframe() 废弃,统一改用 X509_check_certificate_times()。

grep -rn "X509_cmp_time\|X509_cmp_current_time\|X509_cmp_timeframe" .

协议与构建选项的移除

  • 移除了 SSLv2 Client Hello 支持。
  • 移除了 SSLv3 支持(2015 年废弃,1.1.0 起默认关闭)。
  • 移除了 engines 支持。no-engine 构建选项和 OPENSSL_NO_ENGINE 宏现在恒存在,所有 #ifndef OPENSSL_NO_ENGINE 分支都成了死代码:
grep -rn "ENGINE_\|OPENSSL_NO_ENGINE" --include='*.c' --include='*.h' .
  • 默认编译期关闭 RFC 8422 中废弃的 EC 曲线支持,需要时用 enable-tls-deprecated-ec 打开。
  • 默认编译期关闭显式 EC 曲线支持,需要时用 enable-ec_explicit_curves 打开。
  • 移除了 c_rehash 脚本,改用:
openssl rehash /etc/ssl/certs
  • openssl ca 移除了 msie-hack 选项。
  • 移除了 BIO_f_reliable(),且没有替代实现(3.0 起就已经是坏的)。用到它的 BIO 链需要重写:
grep -rn "BIO_f_reliable" .
  • 移除了对自定义 EVP_CIPHER、EVP_MD、EVP_PKEY、EVP_PKEY_ASN1 方法的废弃支持。
  • 移除了废弃的固定 SSL/TLS 版本方法函数。
  • 移除了 ERR_get_state()、ERR_remove_state()、ERR_remove_thread_state(),ERR_STATE 现在恒为 opaque。
  • 构建目标里去掉了 darwin-i386{,-cc} 和 darwin-ppc{,64}{,-cc}。

新特性与行为变更

ECH 与 RFC 8998

新增 Encrypted Client Hello(ECH,RFC 9849),设计文档在 doc/designs/ech-api.md。RFC 8998 支持落地:签名算法 sm2sig_sm3、密钥交换组 curveSM2、后量子组 curveSM2MLKEM768。

另外新增 cSHAKE(SP 800-185)、ML-DSA-MU 摘要算法、SNMP KDF 和 SRTP KDF。

SM2 默认 distid 变了

这是升级时最容易出问题的一处:验证 SM2 签名证书时,默认 distinguishing identifier 从空字符串改成了 ASCII 常量 "1234567812345678"。要和 4.0 之前的版本互相验签,必须显式传空值:

openssl pkeyutl -verify -pubin -inkey sm2-pub.pem -in msg.bin -sigfile sig.der -pkeyopt distid:
openssl dgst -sm3 -verify sm2-pub.pem -signature sig.der -sigopt distid: msg.bin

openssl-pkeyutl 用 -pkeyopt,openssl-dgst 用 -sigopt,值都写成 "distid:"。

签名算法名改用 IANA 名字

SSL_get0_sigalg() 和 SSL_get0_shared_sigalg() 报告 TLS 签名算法名和 codepoint,覆盖对端通告的算法和双方共享的算法,取代只适用于 TLS 1.2 的 SSL_get_sigalgs() / SSL_get_shared_sigalgs()。名字现在统一为 IANA 名,并与 SignatureAlgorithms 配置项一致;之前报告的是 OpenSSL 自己的别名。

s_client/s_server 的输出也随之变化:

Signature Algorithms: mldsa65:mldsa87:mldsa44:ecdsa_secp256r1_sha256:...:rsa_pkcs1_sha512:ecdsa_sha224:rsa_pkcs1_sha224:dsa_sha224:...
openssl s_client -connect example.com:443 </dev/null 2>/dev/null | grep -i "Signature Algorithms"

按旧别名做字符串匹配的日志解析需要同步改。

证书扩展与 SKID/AKID

openssl x509、openssl req、openssl ca 里内置的 SKID / AKID 扩展添加逻辑被移除,改为和其他扩展一样通过配置文件和 CLI 选项处理。subjectKeyIdentifier / authorityKeyIdentifier 的语法有更新,为 keyid 和 issuer 关键字引入了 nonss 限定符。x509 的 "mini-CA" 即使没给 -extfile/-extensions,也会尝试从默认配置文件里找扩展设置;默认配置打不开时静默忽略。OpenSSL 4.0 自带的配置里已经补上了必要的 SKID/AKID。

EC 曲线与 SSL_add1_host

废弃曲线和显式 EC 曲线默认关闭之后,互操作面会收窄。临时恢复编译期支持:

./Configure enable-tls-deprecated-ec enable-ec_explicit_curves

默认 group 列表也变了:在 *X25519MLKEM768 之后的第一个 tuple 追加 SecP256r1MKEM768 和 curveSM2MLKEM768,并插入一个含 curveSM2 的倒数第二 tuple。

SSL_add1_host() / SSL_set1_host() 废弃,改用 SSL_add1_dnsname()、SSL_set1_dnsname()、SSL_add1_ipaddr()、SSL_set1_ipaddr(),配合 X509_VERIFY_PARAM 做多名字校验:

grep -rn "SSL_add1_host\|SSL_set1_host" .

TLS 参数上限

服务端对 TLS 参数加了硬上限:最多关注 16 个 key_share、128 个 supported groups、128 个 sig_algs。客户端如果一次性塞进去过多候选,超出部分会被忽略。

客户端预测 keyshare 的浮动规则也有调整:当一个 tuple 丢掉最后一个标记为发送预测 keyshare 的元素时(默认是 *X25519MLKEM768 和 *X25519),keyshare 会由该 tuple 剩下的第一个元素继承。

其他新增

  • FIPS 自检可用 openssl fipsinstall 的 -defer_tests 选项延后执行。
  • Windows 上支持静态或动态链接 VC runtime。
  • TLS 1.2 支持 RFC 7919 的协商式 FFDHE 密钥交换。
  • openssl s_client / s_server 新增 -expected-rpks,用于指定认证连接时预期的对端公钥。
  • openssl dgst 新增 -hmac-env / -hmac-stdin。
  • openssl pkeyutl 支持 LMS 验签,并新增 LMS SubjectPublicKeyInfo 的编解码器。
  • OSSL_STORE 支持 EVP_SKEY 对象:openssl enc 新增 -skeyuri / -storepass,openssl storeutl 新增 -skeys,同时新增 OSSL_STORE_INFO_SKEY 类型和对应 API。
  • TLS 1.3 新增国密套件 TLS_SM4_GCM_SM3 和 TLS_SM4_CCM_SM3(RFC 8998)。
  • s_server 开启 -verify_return_error 时,服务端验证默认启用。
  • openssl version -c 在 POWER 和 SPARC 上输出 CPU 能力;新增 OPENSSL_ppccap(3) man page。
  • 新增 API:OSSL_ESS_check_signing_certs_ex()、OPENSSL_sk_set_cmp_thunks()、OSSL_PARAM_clear_free()(清理敏感 PARAM)、ASN1_BIT_STRING_get_length() / ASN1_BIT_STRING_set1()、PACKET_msg_start()。
  • SSL_listen_ex() 配合 OSSL_QUIC_method() 创建的 "blank" SSL 对象,可以像 DTLS 那样轮询入站 QUIC 连接。
  • 新增 SSL_CTX_get0_alpn_protos() / SSL_get0_alpn_protos()、SSL_CTX_is_server()。
  • Configure 新增禁用 KDF 算法的选项(hmac-drbg-kdf、kbkdf、krb5kdf、pvkkdf、snmpkdf、sskdf、sshkdf、x942kdf、x963kdf)。被移除的 Configure 选项现在只能用 disable-,对已移除特性使用 enable- 会报错。
复制全文 生成海报 OpenSSL TLS 升级迁移

推荐文章

程序员茄子在线接单