Go的crypto哈希需严格遵循hash.Hash接口流程,必须Write后Sum;sha256.Sum是固定数组结构体,取值用sum.Sum(nil)[:];大文件须流式处理防OOM;RSA和HMAC签名前需手动哈希且不可省略Write;MD5/SHA1禁用于安全场景。

Go 的 crypto 包里哈希不是“传个字符串就返回 hex”,必须走 hash.Hash 接口;跳过 Write 直接调 Sum、或误把 Sum 当成纯函数用,签名和校验必然不匹配。
crypto/sha256.Sum 返回的是结构体,不是 []byte
sha256.Sum 类型底层是固定长度数组(如 [32]byte),不能直接当切片传给 hmac.New 或网络传输。常见错误是写 sha256.Sum([]byte("x")) 然后直接 fmt.Println,看到 {[10 20 ...]} 就以为出错了。
- 正确取值方式:调用
sum.Sum(nil)[:]——Sum(nil)返回[]byte,再加切片操作符转成可寻址切片 - 如果只是临时调试,用
hex.EncodeToString(sha256.Sum(nil).Sum(nil)[:])更稳,避免漏掉Sum调用 - 别写
sum[:]:这会报编译错误,因为sum是结构体,不是数组字面量
大文件哈希必须流式处理,不能 os.ReadFile 全读进内存
对几百 MB 的日志或二进制文件调 os.ReadFile 再丢给 sha256.Sum,极易触发 OOM。Go 的哈希设计就是为流式服务的,hash.Hash 实现都支持 Write。
- 最简方式:
io.Copy(hash, file),自动分块、无额外内存开销 - 手动控制时,用
bufio.NewReader配合hash.Write(buf.Bytes()),注意每次Read后检查n,否则最后一块可能丢失 - 别在循环里反复
hash.Reset()后重用同一个hash.Hash实例——它不是线程安全的,并发下会串货
rsa.SignPKCS1v15 和 hmac.New 都要求你先哈希,再传摘要字节
这是签名类操作最统一也最容易翻车的点:rsa.SignPKCS1v15 不自动哈希,hmac.New 也不自动哈希——它们只处理你已经算好的摘要或待签名原始字节,但语义完全不同。
立即学习“go语言免费学习笔记(深入)”;
-
rsa.SignPKCS1v15第四个参数必须是哈希摘要(如sha256.Sum(nil).Sum(nil)[:]),且第三个参数得传crypto.SHA256标识,验证时必须完全一致,否则直接返回crypto.ErrVerification -
hmac.New第二个参数是密钥[]byte,它内部会用密钥派生哈希上下文;待签名数据要靠后续h.Write()输入,不是初始化时传进去的 - 两者都不能省略
Write步骤:漏掉就等于签了个空串;重复Sum不重置状态,可能拼接旧结果
MD5 和 SHA1 已不安全,但 crypto/md5 仍能编译通过
Go 标准库没删 crypto/md5,import 完全合法,go.sum 里也常见它的哈希值。但这不表示它能用在安全场景。
- 对外接口、JWT 签名、密码存储、API 防篡改等,必须用
crypto/sha256或更强算法 - 内部缓存 key、临时文件指纹、开发期快速比对等非安全场景,MD5 可用,但建议加注释说明用途
- 别用
crypto/sha1:已被证明可在现实时间构造碰撞,OpenSSL 默认拒收 SHA1 签名
哈希本身不加密,只是单向摘要;签名是否有效,取决于你传进去的字节是否和服务端完全一致——换行符、JSON 键序、base64 填充、密钥编码(UTF-8 还是 raw bytes)、甚至空格数量,差一个字节,签名就失效。


















