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-会报错。