证书指纹(Certificate Fingerprint,也称 Thumbprint)是证书的唯一标识符。通过对证书内容进行哈希计算得出,广泛应用于证书 pinning、自动化监控、证书比对等场景。本文详细介绍 OpenSSL 计算证书指纹的各种方法。

什么是证书指纹?

证书指纹是对整个证书 DER 编码内容进行哈希计算的结果。常见哈希算法:

  • SHA-1:兼容性较好,但因安全原因已不推荐
  • SHA-256:目前推荐的主要算法
  • MD5:仅用于兼容旧系统,强烈不推荐

基本计算方法

使用 x509 命令

1
2
3
4
5
6
7
8
# SHA-256 指纹(推荐)
openssl x509 -noout -fingerprint -sha256 -in certificate.crt

# SHA-1 指纹
openssl x509 -noout -fingerprint -sha1 -in certificate.crt

# MD5 指纹(不推荐)
openssl x509 -noout -fingerprint -md5 -in certificate.crt

实际输出示例:

1
sha256 Fingerprint=71:DA:A0:3E:9E:A4:A8:C3:B0:D8:E9:2B:FD:EB:81:77:71:39:82:93:3E:C6:E1:21:7A:C1:DC:C0:D1:C0:78:F2

使用 dgst 命令

1
2
# 使用 dgst 计算 SHA-256 指纹
openssl dgst -sha256 certificate.crt

输出:

1
SHA256(certificate.crt)= bf2b929d1ba24ae694726ea760a3c2a9faeedb9d38ee5c68b0c6f11c5e2256d5

注意:dgst 命令直接对 PEM 文件内容计算哈希,与 x509 -fingerprint 的计算方式略有不同。前者包含 PEM 头部和尾部,后者仅对证书 DER 编码计算。

提取指纹值的技巧

去除指纹前缀

在脚本中需要提取纯指纹值(去掉 sha256 Fingerprint= 前缀):

1
2
3
# 使用 sed 提取
FINGERPRINT=$(openssl x509 -noout -fingerprint -sha256 -in certificate.crt | sed 's/.*=//')
echo $FINGERPRINT

批量计算多个证书指纹

1
2
3
4
# 遍历目录下所有证书
for cert in *.crt; do
    echo "$cert: $(openssl x509 -noout -fingerprint -sha256 -in "$cert" | sed 's/.*=//')"
done

证书比对实战

判断两个证书是否相同

1
2
3
4
5
6
7
8
9
# 提取两个证书的指纹并比较
CERT1_FP=$(openssl x509 -noout -fingerprint -sha256 -in cert1.crt | sed 's/.*=//')
CERT2_FP=$(openssl x509 -noout -fingerprint -sha256 -in cert2.crt | sed 's/.*=//')

if [ "$CERT1_FP" = "$CERT2_FP" ]; then
    echo "两个证书相同"
else
    echo "证书不同"
fi

验证证书是否匹配私钥

通过比较证书和私钥的公钥指纹来验证匹配性:

1
2
3
4
5
6
7
8
# 提取证书中的公钥指纹
CERT_PUBKEY_FP=$(openssl x509 -in certificate.crt -pubkey -noout | openssl dgst -sha256)

# 提取私钥中的公钥指纹
KEY_PUBKEY_FP=$(openssl pkey -in private.key -pubout -outform DER | openssl dgst -sha256)

echo "证书公钥指纹: $CERT_PUBKEY_FP"
echo "私钥公钥指纹: $KEY_PUBKEY_FP"

SPKI 指纹:公钥独立标识

SPKI(Subject Public Key Info)指纹仅基于公钥计算,不受证书有效期、签名算法等因素影响。适合用于公钥固定场景:

1
2
# 计算 SPKI 指纹
openssl x509 -in certificate.crt -pubkey -noout | openssl dgst -sha256

输出:

1
SHA2-256(stdin)= 654875f2bccf6a00beaca4e4a464422118a564032d372ebbc79fd06ff05a1340

常见应用场景

1. 证书 Pinning

在应用代码中硬编码证书指纹,验证服务器证书是否可信:

1
2
// JavaScript 示例
const expectedFingerprint = "71:DA:A0:3E:9E:A4:A8:C3:B0:D8:E9:2B:FD:EB:81:77:71:39:82:93:3E:C6:E1:21:7A:C1:DC:C0:D1:C0:78:F2";

2. 证书过期监控

在监控脚本中记录指纹以便精确识别证书:

1
2
3
4
5
6
7
8
9
#!/bin/bash
# 监控证书变化
CURRENT_FP=$(openssl x509 -noout -fingerprint -sha256 -in /etc/ssl/certs/server.crt | sed 's/.*=//')
SAVED_FP=$(cat /var/lib/cert-fingerprint.txt 2>/dev/null)

if [ "$CURRENT_FP" != "$SAVED_FP" ]; then
    echo "证书已更新!新指纹: $CURRENT_FP" | mail -s "证书变更告警" admin@example.com
    echo "$CURRENT_FP" > /var/lib/cert-fingerprint.txt
fi

3. 自动化部署校验

部署前验证证书链中各证书的指纹:

1
2
3
4
5
# 验证证书链未被篡改
EXPECTED_ROOT_FP="AA:BB:CC:DD:..."
ACTUAL_ROOT_FP=$(openssl x509 -noout -fingerprint -sha256 -in root-ca.crt | sed 's/.*=//')

[ "$EXPECTED_ROOT_FP" = "$ACTUAL_ROOT_FP" ] || { echo "根证书不匹配!"; exit 1; }

各算法指纹对比

算法 用途 安全性 兼容性
SHA-256 证书指纹、Pinning 优秀
SHA-1 旧系统兼容 优秀
MD5 遗留系统 良好

注意事项

  1. 指纹不是证书唯一标识:证书重新签发后指纹会变化
  2. 公钥指纹更稳定:更换证书但保留相同公钥时,SPKI 指纹不变
  3. 注意格式:指纹通常使用冒号分隔的十六进制字符串
  4. 存储安全:在配置文件中存储指纹时注意访问权限

参考来源