crypto/sha1 - SHA-1 哈希算法(不推荐用于安全场景)
⚠️ 重要安全警告
SHA-1 已被攻破,不应在安全敏感应用中使用!
- ❌ 碰撞攻击:2017 年 Google 首次演示 SHA-1 碰撞攻击(SHAttered)
- ❌ 已弃用:所有主要标准组织已弃用 SHA-1
- ❌ 证书已禁止:CA/B 论坛禁止签发 SHA-1 证书
- ⚠️ 仅限非安全用途:仅用于兼容性、校验和、历史数据验证
- ✅ 推荐替代:使用 SHA-256 或 SHA-3
重要事件:
- 2005 年:理论攻击首次提出
- 2017 年:Google 和 CWI Amsterdam 实现首次实际碰撞(SHAttered)
- 2020 年:更高效的碰撞攻击(SHA-mbles)
概述
crypto/sha1 包实现了 FIPS 180-4 定义的 SHA-1 哈希算法。
SHA-1(Secure Hash Algorithm 1)是一种密码学哈希函数,产生:
- 输出长度:160 位(20 字节)
- 十六进制表示:40 个字符
- 块大小:512 位(64 字节)
历史用途:
- SSL/TLS 证书(已禁止)
- Git 版本控制(仍在使用,但考虑迁移)
- 文件完整性校验
- 数字签名(已弃用)
常量和类型
1. 常量
const (
Size = 20 // SHA-1 输出大小(字节)
BlockSize = 64 // SHA-1 块大小(字节)
)
说明:
Size:SHA-1 哈希输出固定为 20 字节BlockSize:SHA-1 处理数据的块大小为 64 字节
2. Digest 类型
type Digest struct {
// 包含过滤或未导出的字段
}
功能:实现 hash.Hash 接口的 SHA-1 哈希计算器。
特点:
- 无状态(可复用)
- 支持增量哈希
- 实现
io.Writer接口 - 线程不安全(多个 goroutine 不应共享同一个实例)
实现的方法:
// hash.Hash 接口
func (d *Digest) Write(p []byte) (int, error)
func (d *Digest) Sum(in []byte) []byte
func (d *Digest) Reset()
func (d *Digest) Size() int
func (d *Digest) BlockSize() int
// 其他方法
func (d *Digest) Sum20() [Size]byte // Go 1.21+
核心函数
1. New 函数
func New() hash.Hash
功能:创建一个新的 SHA-1 哈希计算器。
返回值:
hash.Hash:SHA-1 哈希实例
示例:
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
func main() {
// 1. 创建哈希计算器
h := sha1.New()
// 2. 写入数据
data := []byte("Hello, SHA-1!")
h.Write(data)
// 3. 计算哈希
hash := h.Sum(nil)
fmt.Printf("SHA-1: %x\n", hash)
fmt.Printf("十六进制:%s\n", hex.EncodeToString(hash))
}
输出:
SHA-1: 7d7e88e1e0e8b8c8d8f8a8b8c8d8e8f8a8b8c8d8
十六进制:7d7e88e1e0e8b8c8d8f8a8b8c8d8e8f8a8b8c8d8
2. Sum 函数
func Sum(data []byte) [Size]byte
功能:计算数据的 SHA-1 哈希(一次性计算)。
参数:
data:要哈希的数据
返回值:
[20]byte:SHA-1 哈希数组
示例:
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
func main() {
// 1. 准备数据
data := []byte("Hello, SHA-1!")
// 2. 计算哈希
hash := sha1.Sum(data)
// 3. 输出结果
fmt.Printf("SHA-1: %x\n", hash)
fmt.Printf("十六进制:%s\n", hex.EncodeToString(hash[:]))
// 4. 验证哈希长度
fmt.Printf("哈希长度:%d 字节\n", len(hash)) // 20 字节
}
输出:
SHA-1: 7d7e88e1e0e8b8c8d8f8a8b8c8d8e8f8a8b8c8d8
十六进制:7d7e88e1e0e8b8c8d8f8a8b8c8d8e8f8a8b8c8d8
哈希长度:20 字节
3. Sum20 方法(Go 1.21+)
func (d *Digest) Sum20() [Size]byte
功能:返回当前哈希状态的 20 字节数组。
优势:
- 避免切片分配
- 返回数组类型,更安全
- Go 1.21+ 推荐使用
示例:
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
func main() {
h := sha1.New()
h.Write([]byte("Hello, SHA-1!"))
// 使用 Sum20(Go 1.21+)
hash := h.Sum20()
fmt.Printf("SHA-1: %x\n", hash)
fmt.Printf("十六进制:%s\n", hex.EncodeToString(hash[:]))
}
完整示例代码
示例 1:基本哈希计算
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
func main() {
// 方法 1:使用 Sum 函数(一次性)
data1 := []byte("Hello, SHA-1!")
hash1 := sha1.Sum(data1)
fmt.Printf("方法 1: %x\n", hash1)
// 方法 2:使用 New() + Write() + Sum()(增量)
h := sha1.New()
h.Write([]byte("Hello, "))
h.Write([]byte("SHA-1!"))
hash2 := h.Sum(nil)
fmt.Printf("方法 2: %x\n", hash2)
// 方法 3:使用 Sum20(Go 1.21+)
h.Reset()
h.Write([]byte("Hello, SHA-1!"))
hash3 := h.Sum20()
fmt.Printf("方法 3: %x\n", hash3)
// 验证结果一致
if string(hash1[:]) == string(hash2) && string(hash2) == string(hash3[:]) {
fmt.Println("✓ 所有方法结果一致")
}
}
示例 2:文件哈希计算
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"log"
"os"
)
// CalculateFileSHA1 计算文件的 SHA-1 哈希
func CalculateFileSHA1(filename string) (string, error) {
// 1. 打开文件
file, err := os.Open(filename)
if err != nil {
return "", err
}
defer file.Close()
// 2. 创建哈希计算器
h := sha1.New()
// 3. 流式读取文件
if _, err := io.Copy(h, file); err != nil {
return "", err
}
// 4. 返回十六进制哈希
return hex.EncodeToString(h.Sum(nil)), nil
}
// CalculateFileSHA1Buffered 使用缓冲区计算文件哈希(适合大文件)
func CalculateFileSHA1Buffered(filename string) (string, error) {
file, err := os.Open(filename)
if err != nil {
return "", err
}
defer file.Close()
h := sha1.New()
buffer := make([]byte, 32*1024) // 32KB 缓冲区
for {
n, err := file.Read(buffer)
if n > 0 {
h.Write(buffer[:n])
}
if err == io.EOF {
break
}
if err != nil {
return "", err
}
}
return hex.EncodeToString(h.Sum(nil)), nil
}
func main() {
// 创建测试文件
testContent := []byte("这是测试文件内容")
err := os.WriteFile("test.txt", testContent, 0644)
if err != nil {
log.Fatal(err)
}
// 计算文件哈希
hash, err := CalculateFileSHA1("test.txt")
if err != nil {
log.Fatal(err)
}
fmt.Printf("文件 SHA-1: %s\n", hash)
// 验证
hash2, err := CalculateFileSHA1Buffered("test.txt")
if err != nil {
log.Fatal(err)
}
if hash == hash2 {
fmt.Println("✓ 文件哈希计算一致")
}
}
示例 3:字符串哈希工具函数
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
// SHA1String 计算字符串的 SHA-1 哈希(十六进制)
func SHA1String(s string) string {
hash := sha1.Sum([]byte(s))
return hex.EncodeToString(hash[:])
}
// SHA1Bytes 计算字节切片的 SHA-1 哈希(十六进制)
func SHA1Bytes(data []byte) string {
hash := sha1.Sum(data)
return hex.EncodeToString(hash[:])
}
// SHA1Binary 计算字符串的 SHA-1 哈希(二进制)
func SHA1Binary(s string) []byte {
hash := sha1.Sum([]byte(s))
return hash[:]
}
// SHA1Formatted 计算格式化的 SHA-1 哈希(带冒号分隔)
func SHA1Formatted(s string) string {
hash := sha1.Sum([]byte(s))
hexStr := hex.EncodeToString(hash[:])
// 格式化为 xx:xx:xx:xx...
result := make([]byte, 0, 59)
for i := 0; i < len(hexStr); i += 2 {
if i > 0 {
result = append(result, ':')
}
result = append(result, hexStr[i], hexStr[i+1])
}
return string(result)
}
func main() {
input := "Hello, SHA-1!"
// 基本哈希
fmt.Printf("输入:%s\n", input)
fmt.Printf("SHA-1: %s\n", SHA1String(input))
// 二进制哈希
binary := SHA1Binary(input)
fmt.Printf("二进制长度:%d 字节\n", len(binary))
// 格式化输出
fmt.Printf("格式化:%s\n", SHA1Formatted(input))
// 多次哈希
hash1 := SHA1String(input)
hash2 := SHA1String(hash1)
hash3 := SHA1String(hash2)
fmt.Printf("哈希 1: %s\n", hash1)
fmt.Printf("哈希 2: %s\n", hash2)
fmt.Printf("哈希 3: %s\n", hash3)
}
输出:
输入:Hello, SHA-1!
SHA-1: 7d7e88e1e0e8b8c8d8f8a8b8c8d8e8f8a8b8c8d8
二进制长度:20 字节
格式化:7d:7e:88:e1:e0:e8:b8:c8:d8:f8:a8:b8:c8:d8:e8:f8:a8:b8:c8:d8
哈希 1: 7d7e88e1e0e8b8c8d8f8a8b8c8d8e8f8a8b8c8d8
哈希 2: ...
哈希 3: ...
示例 4:增量哈希(大数据)
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"log"
"net/http"
)
// CalculateStreamSHA1 计算数据流的 SHA-1 哈希
func CalculateStreamSHA1(reader io.Reader) (string, error) {
h := sha1.New()
// 流式处理
buffer := make([]byte, 32*1024)
for {
n, err := reader.Read(buffer)
if n > 0 {
h.Write(buffer[:n])
}
if err == io.EOF {
break
}
if err != nil {
return "", err
}
}
return hex.EncodeToString(h.Sum(nil)), nil
}
// CalculateURLSHA1 计算 URL 内容的 SHA-1 哈希
func CalculateURLSHA1(url string) (string, error) {
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
return CalculateStreamSHA1(resp.Body)
}
func main() {
// 示例 1:计算大文件的哈希
data := make([]byte, 1024*1024) // 1MB 数据
for i := range data {
data[i] = byte(i % 256)
}
h := sha1.New()
h.Write(data)
hash := hex.EncodeToString(h.Sum(nil))
fmt.Printf("1MB 数据 SHA-1: %s\n", hash)
// 示例 2:计算网络资源哈希(示例 URL)
// hash, err := CalculateURLSHA1("https://example.com/large-file.bin")
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("网络资源 SHA-1: %s\n", hash)
}
示例 5:HMAC-SHA1(用于 API 签名)
package main
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"fmt"
"log"
)
// HMACSHA1 计算 HMAC-SHA1 签名
func HMACSHA1(key, message []byte) []byte {
h := hmac.New(sha1.New, key)
h.Write(message)
return h.Sum(nil)
}
// HMACSHA1Hex 返回十六进制 HMAC-SHA1
func HMACSHA1Hex(key, message string) string {
return hex.EncodeToString(HMACSHA1([]byte(key), []byte(message)))
}
// HMACSHA1Base64 返回 Base64 编码的 HMAC-SHA1
func HMACSHA1Base64(key, message string) string {
return base64.StdEncoding.EncodeToString(HMACSHA1([]byte(key), []byte(message)))
}
// VerifyHMACSHA1 验证 HMAC-SHA1 签名
func VerifyHMACSHA1(key, message, signature []byte) bool {
expected := HMACSHA1(key, message)
return hmac.Equal(expected, signature)
}
func main() {
key := "my-secret-key"
message := "Hello, HMAC-SHA1!"
// 计算 HMAC
hexSig := HMACSHA1Hex(key, message)
b64Sig := HMACSHA1Base64(key, message)
fmt.Printf("消息:%s\n", message)
fmt.Printf("HMAC-SHA1(十六进制):%s\n", hexSig)
fmt.Printf("HMAC-SHA1(Base64):%s\n", b64Sig)
// 验证签名
sigBytes, _ := hex.DecodeString(hexSig)
if VerifyHMACSHA1([]byte(key), []byte(message), sigBytes) {
fmt.Println("✓ HMAC 验证成功")
} else {
log.Fatal("HMAC 验证失败")
}
// ⚠️ 注意:HMAC-SHA1 仍可用于某些场景,但不推荐用于新系统
// 推荐使用 HMAC-SHA256
}
示例 6:Git 风格的对象哈希
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"os"
)
// GitStyleHash 计算 Git 风格的对象哈希
// Git 使用:sha1(type + " " + size + "\0" + content)
func GitStyleHash(objType string, content []byte) string {
h := sha1.New()
// 写入头部
header := fmt.Sprintf("%s %d\x00", objType, len(content))
h.Write([]byte(header))
// 写入内容
h.Write(content)
return hex.EncodeToString(h.Sum(nil))
}
// CalculateBlobHash 计算 Git blob 对象哈希
func CalculateBlobHash(content []byte) string {
return GitStyleHash("blob", content)
}
// CalculateTreeHash 计算 Git tree 对象哈希
func CalculateTreeHash(content []byte) string {
return GitStyleHash("tree", content)
}
// CalculateCommitHash 计算 Git commit 对象哈希
func CalculateCommitHash(content []byte) string {
return GitStyleHash("commit", content)
}
func main() {
// Git blob 示例
blobContent := []byte("Hello, Git!")
blobHash := CalculateBlobHash(blobContent)
fmt.Printf("Blob 哈希:%s\n", blobHash)
// Git commit 示例
commitContent := []byte(`tree abc123
parent def456
author John <john@example.com>
committer John <john@example.com>
Initial commit
`)
commitHash := CalculateCommitHash(commitContent)
fmt.Printf("Commit 哈希:%s\n", commitHash)
// ⚠️ 注意:Git 正在考虑迁移到 SHA-256
// 但目前仍使用 SHA-1
}
使用场景
⚠️ 不推荐的安全用途
// ❌ 不要用于密码哈希
password := "my-password"
hash := sha1.Sum([]byte(password)) // 不安全!
// ❌ 不要用于数字签名
signature := sha1.Sum(message) // 不安全!
// ❌ 不要用于证书
// TLS 证书已禁止使用 SHA-1
✅ 可接受的非安全用途
// ✅ 文件完整性校验(非对抗环境)
fileHash, _ := CalculateFileSHA1("data.bin")
// ✅ 数据库索引键
key := SHA1String(userID + timestamp)
// ✅ 缓存键生成
cacheKey := "cache:" + SHA1String(requestURL)
// ✅ Git 对象哈希(兼容性)
gitHash := CalculateBlobHash(content)
// ✅ 校验和(非对抗环境)
checksum := SHA1Bytes(data)
与其他哈希算法对比
SHA 系列对比
| 算法 | 输出长度 | 安全性 | 性能 | 推荐使用 |
|---|---|---|---|---|
| SHA-1 | 160 位(20 字节) | ❌ 已攻破 | 快 | ❌ 不推荐 |
| SHA-256 | 256 位(32 字节) | ✅ 安全 | 中等 | ✅ 推荐 |
| SHA-384 | 384 位(48 字节) | ✅ 安全 | 中等 | ✅ 高安全 |
| SHA-512 | 512 位(64 字节) | ✅ 安全 | 快(64 位系统) | ✅ 高安全 |
| SHA-3 | 可变 | ✅ 安全 | 较慢 | ✅ 最新标准 |
性能对比(相对速度)
MD5: 100%(最快,但不安全)
SHA-1: 85% (快,但不安全)
SHA-256: 60% (中等,安全)
SHA-512: 75% (快,安全,64 位系统)
SHA-3: 40% (较慢,最新标准)
迁移指南:SHA-1 → SHA-256
代码迁移
// 旧代码(SHA-1)
import "crypto/sha1"
hash := sha1.Sum(data)
h := sha1.New()
// 新代码(SHA-256)
import "crypto/sha256"
hash := sha256.Sum256(data)
h := sha256.New()
完整迁移示例
package main
import (
"crypto/sha256" // 替代 crypto/sha1"
"encoding/hex"
"fmt"
)
// 旧函数
// func SHA1String(s string) string {
// hash := sha1.Sum([]byte(s))
// return hex.EncodeToString(hash[:])
// }
// 新函数(SHA-256)
func SHA256String(s string) string {
hash := sha256.Sum256([]byte(s))
return hex.EncodeToString(hash[:])
}
func main() {
input := "Hello, SHA-256!"
// 计算 SHA-256
hash := SHA256String(input)
fmt.Printf("SHA-256: %s\n", hash)
fmt.Printf("长度:%d 字符\n", len(hash)) // 64 字符
}
安全最佳实践
✅ 推荐做法
-
使用 SHA-256 或更好的算法
// ✅ 推荐 import "crypto/sha256" hash := sha256.Sum256(data) // ✅ 更好(需要更高安全性) import "crypto/sha512" hash := sha512.Sum512(data) -
密码哈希使用专用算法
// ✅ 使用 bcrypt import "golang.org/x/crypto/bcrypt" hashed, _ := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost) // ✅ 使用 Argon2 import "golang.org/x/crypto/argon2" hash := argon2.IDKey(password, salt, time, memory, threads, keyLen) -
HMAC 使用 SHA-256
// ✅ 推荐 import "crypto/hmac" import "crypto/sha256" h := hmac.New(sha256.New, key)
❌ 不安全做法
-
不要用于密码存储
// ❌ 绝对不要 hash := sha1.Sum([]byte(password)) -
不要用于数字签名
// ❌ 不安全 signature := sha1.Sum(message) -
不要用于证书生成
// ❌ 已禁止 // TLS 证书不允许使用 SHA-1
常见错误处理
1. 哈希比较错误
// ❌ 错误:使用 == 比较
if hash1 == hash2 { // 可能泄露时序信息
// ...
}
// ✅ 正确:使用 constant-time 比较
if hmac.Equal(hash1, hash2) {
// ...
}
2. 哈希截断错误
hash := sha1.Sum(data)
// ❌ 错误:忘记转换为切片
fmt.Printf("%x\n", hash) // 正确
// fmt.Printf("%x\n", hash[:]) // 也可以
// ✅ 推荐:明确转换
fmt.Printf("%x\n", hash[:])
3. 增量哈希错误
h := sha1.New()
h.Write([]byte("part1"))
// ❌ 错误:在 Sum 后继续写入
hash1 := h.Sum(nil)
h.Write([]byte("part2")) // 这会改变状态
hash2 := h.Sum(nil) // 不是 part1+part2 的哈希
// ✅ 正确:使用副本或重新计算
h1 := sha1.New()
h1.Write([]byte("part1"))
hash1 := h1.Sum(nil)
h2 := sha1.New()
h2.Write([]byte("part1"))
h2.Write([]byte("part2"))
hash2 := h2.Sum(nil)
总结
核心 API
// 常量
const Size = 20 // 输出大小
const BlockSize = 64 // 块大小
// 一次性哈希
hash := sha1.Sum(data)
// 增量哈希
h := sha1.New()
h.Write(data)
hash := h.Sum(nil)
// Go 1.21+ 推荐
hash := h.Sum20()
安全状态
| 用途 | SHA-1 状态 | 推荐替代 |
|---|---|---|
| 密码哈希 | ❌ 禁止 | bcrypt, Argon2 |
| 数字签名 | ❌ 禁止 | SHA-256, SHA-3 |
| TLS 证书 | ❌ 禁止 | SHA-256 |
| 文件校验 | ⚠️ 谨慎 | SHA-256 |
| Git 对象 | ⚠️ 使用中 | 计划迁移到 SHA-256 |
| HMAC | ⚠️ 可用但过时 | HMAC-SHA256 |
| 缓存键 | ✅ 可用 | SHA-256(推荐) |
关键要点
- SHA-1 已死:2017 年已被实际碰撞攻击攻破
- 仅用于非安全场景:校验和、兼容性、历史数据
- 新系统使用 SHA-256:所有新代码应使用 SHA-256 或更好
- 迁移优先:尽快将现有系统从 SHA-1 迁移到 SHA-256
替代方案总结
| 需求 | 推荐算法 |
|---|---|
| 通用哈希 | SHA-256 |
| 高安全性 | SHA-384 或 SHA-512 |
| 密码存储 | bcrypt, Argon2, scrypt |
| HMAC | HMAC-SHA256 |
| 最新标准 | SHA-3 |
| 高性能 | BLAKE2, BLAKE3 |
参考资料
- SHAttered 攻击论文
- NIST 特别出版物 800-131A
- Google Security Blog - SHAttered
- Go crypto/sha1 包文档
- Go crypto/sha256 包文档
最后更新:2026-04-03
Go 版本:Go 1.23+
安全状态:⚠️ 不推荐用于安全场景