综合 OpenSSL 4.0 的十六进制输出变了两处:去掉多余的 00: 前缀,dump 宽度改成 24/16 字节

2026-11-06 21:07:07

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 各跑一次,diff rsa -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 曲线。

复制全文 生成海报 OpenSSL Shell 运维 证书

推荐文章

程序员茄子在线接单