OpenSSL 4.0 的十六进制输出变了两处:去掉多余的 00: 前缀,dump 宽度改成 24/16 字节
OpenSSL 4.0 改了两处十六进制打印行为。打印密钥数据(例如 openssl rsa -text 里的 modulus)时,如果最高字节 >= 0x80,不再先多打一个 00:;这个 00 是 DER INTEGER 的正数符号填充,不是模数本身。十六进制 dump 的宽度也被统一:签名每行 24 字节(为了不超过 80 字符),其它每行 16 字节。CHANGES 里明确提醒:解析 openssl 十六进制输出的脚本需要更新。
1. 哪些脚本最容易坏
grep '^00:'或grep -q '^00:'判断模数开头,4.0 下匹配不到。- 按固定列宽
cut -c...、awk '{print $2}'取 hex,宽度从 16 变 24 或行内位置变化后就取错。 - 按行数拼接 hex,例如
tail -n +2 | head -n 8假定 modulus 固定占 8 行;去掉00、换宽度都会改变行数。 - 用
openssl rsa -text抓 modulus,再和另一工具的结果或指纹做字符串比对;一边带00一边不带,直接不相等。 - 解析签名 hex 时按 16 字节/行切;4.0 签名是 24 字节/行。
2. 复现对比
准备两套环境,分别装 3.x 和 4.0,拿同一份测试 key/cert 跑;不要在同一台机器上的同一个 OpenSSL 安装里反复切换版本。测试 key 至少要能触发 00 的情况(RSA modulus 通常就会)。
# 3.x 环境
openssl version
openssl rsa -in test.key -noout -text > rsa-3.txt
openssl x509 -in test.crt -noout -text > cert-3.txt
# 4.0 环境,同样两条
openssl version
openssl rsa -in test.key -noout -text > rsa-4.txt
openssl x509 -in test.crt -noout -text > cert-4.txt
diff -u rsa-3.txt rsa-4.txt
diff -u cert-3.txt cert-4.txt
cert-*.txt 里的 Signature 段能直接看出 16 字节/行与 24 字节/行的差别。先看 modulus 和 Signature 附近的 diff,不要被证书正文的其他行淹没。
3. 正确做法
hex 只用于人看。机器比对改用规范化指纹、DER 字节或签名验证。
# 证书指纹规范化:去掉字段名、空格、冒号,转小写
norm_fp() {
openssl x509 -in "$1" -noout -fingerprint -sha256 \
| sed 's/^[^=]*=//' | tr -d '[:space:]:' | tr 'A-F' 'a-f'
}
# 公钥 DER + sha256
openssl pkey -in test.key -pubout -outform DER -out pub.der
sha256sum pub.der
xxd pub.der | head
openssl dgst -sha256 pub.der # 与 sha256sum pub.der 等价
# 签名不要解析 hex,直接验
openssl pkey -in test.key -pubout -out pub.pem
openssl dgst -sha256 -sign test.key -out sig.bin msg.txt
openssl dgst -sha256 -verify pub.pem -signature sig.bin msg.txt
如果对方只能给 modulus 文本,先把它转成 DER/二进制再比,不要拿带冒号的 hex 字符串直接比。
4. 多行输出解析
确实要从人读输出里抽 hex,要按“冒号分隔的 hex 对”重新聚合,不要按行数。下面只抽 modulus 块:
openssl rsa -in test.key -noout -text \
| awk '
/modulus:/ { inmod=1; next }
/publicExponent:/ { inmod=0 }
inmod && NF {
gsub(/^[ \t]+/, ""); gsub(/[ \t]+$/, ""); print
}
' \
| paste -sd: - \
| sed 's/^:*//; s/:*$//'
不要直接 tr -d ' \n':行尾和下一行行首之间没有冒号,会把两个字节粘成一个。这段结果仍会因 00 有无而不同,只适合展示;跨版本比对回到 DER/指纹。
5. 升级前自检清单
- 记录版本:
openssl version。 - 在脚本/CI 里搜
openssl .* -text、00:、cut -c、head -n、tail -n。 - 用会触发
00的 RSA key 和证书,在 3.x 与 4.0 各跑一次,diffrsa -text与x509 -text输出。 - 证书比对改为规范化指纹;密钥/公钥比对改为 DER 的 sha256;签名改为 verify。
- 删除按行数解析 hex 的逻辑,改为按冒号对聚合;能不用文本就不用。
- 固定 CI 镜像里的 OpenSSL 版本,避免 3.x/4.0 混跑。
4.0 同一批不兼容项还有:ASN1_STRING 变为 opaque、大量 X509 函数加 const、X509_cmp_time 系列弃用并改用 X509_check_certificate_times()、libcrypto 不再用 atexit() 安装 OPENSSL_cleanup、移除 SSLv2 Client Hello/SSLv3/engine/自定义 EVP method family/固定 SSL/TLS 版本的方法函数、c_rehash 改 openssl rehash、默认禁用弃用与显式 EC 曲线。