签名前必须先哈希,不能直接对原始数据签名;go的crypto/rsa和crypto/ecdsa签名函数要求输入为已计算的哈希摘要,而非原始字节,否则会panic或报crypto: requested hash function is not available等错误,实操须用hash.hash统一处理并确保算法与摘要字节严格一致。

签名前必须先哈希,不能直接对原始数据签名
Go 的 crypto/rsa 和 crypto/ecdsa 签名函数(如 SignPKCS1v15、Sign)要求输入是已哈希的摘要,不是原始字节。直接传原文会 panic 或返回错误,常见报错:crypto: requested hash function is not available 或 crypto: hash mismatch。
实操建议:
- 用
hash.Hash接口统一处理哈希,推荐sha256.New()或sha512.New(),避免手动调用Sum(nil)后忘记重置 - 签名时先
hash.Write(data),再用hash.Sum(nil)得到摘要,传给签名函数的hash.Hash.Sum(nil)结果必须和签名时用的哈希算法一致 - ECDSA 签名(
ecdsa.Sign)只接受[]byte摘要,且长度必须 ≤ 曲线位数 / 8(如 P-256 要求 ≤32 字节),超长会静默截断——务必校验摘要长度
私钥加载别用 PEM 解码后硬编码,优先走 crypto/x509 解析
很多人把 PEM 私钥 base64 解码后直接当 []byte 传给 x509.ParsePKCS1PrivateKey,结果报错 x509: failed to parse private key。根本原因是 PEM 块结构(-----BEGIN RSA PRIVATE KEY-----)必须完整保留,不能只取中间 base64 部分。
实操建议:
- 用
pem.Decode([]byte(pemData))解析整个 PEM 块,检查block.Type是否为"RSA PRIVATE KEY"或"EC PRIVATE KEY" - 再用对应函数解析:RSA 用
x509.ParsePKCS1PrivateKey(block.Bytes),ECDSA 用x509.ParseECPrivateKey(block.Bytes) - 私钥文件若带密码(PKCS#8 加密格式),需先用
x509.DecryptPEMBlock解密,否则解析失败
验签失败八成是哈希不一致或公钥格式不对
验签返回 false 却没报错,是最容易误判的情况。常见原因:签名用 sha256,验签用 sha512;或公钥是从证书里提取的 *x509.Certificate.PublicKey,但类型是 interface{},没断言成 *rsa.PublicKey 或 *ecdsa.PublicKey。
实操建议:
- 验签前先确认公钥类型:
pubKey, ok := cert.PublicKey.(*rsa.PublicKey),ECDSA 同理,否则rsa.VerifyPKCS1v15会 panic - 验签用的哈希对象必须和签名时完全一致——不是“同种算法”,而是同一个
hash.Hash实例或相同输出字节 - 从证书提取公钥时,注意
cert.PublicKey是接口,而cert.PublicKeyAlgorithm是枚举值(x509.RSA/x509.ECDSA),不能混用
生产环境必须加盐或使用 deterministic ECDSA
标准 ECDSA 签名含随机数 k,同一消息每次签名结果不同。如果业务依赖签名唯一性(比如去重、幂等),直接用 ecdsa.Sign 会导致验签失败。
实操建议:
- RSA 签名(PKCS#1 v1.5)本身确定性,无需额外处理
- ECDSA 若需确定性,改用
crypto/ecdsa的SignASN1+ 自定义rand.Reader(如固定 seed 的math/rand),但更稳妥的是用golang.org/x/crypto/ecdsa的Sign变体或引入github.com/btcsuite/btcd/btcec/v2的 deterministic 签名 - 所有密钥读取路径必须设权限掩码(
0o600),避免日志打印私钥或被其他进程读取
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











