Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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-1160 位(20 字节)❌ 已攻破❌ 不推荐
SHA-256256 位(32 字节)✅ 安全中等✅ 推荐
SHA-384384 位(48 字节)✅ 安全中等✅ 高安全
SHA-512512 位(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 字符
}

安全最佳实践

✅ 推荐做法

  1. 使用 SHA-256 或更好的算法

    // ✅ 推荐
    import "crypto/sha256"
    hash := sha256.Sum256(data)
    
    // ✅ 更好(需要更高安全性)
    import "crypto/sha512"
    hash := sha512.Sum512(data)
    
  2. 密码哈希使用专用算法

    // ✅ 使用 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)
    
  3. HMAC 使用 SHA-256

    // ✅ 推荐
    import "crypto/hmac"
    import "crypto/sha256"
    
    h := hmac.New(sha256.New, key)
    

❌ 不安全做法

  1. 不要用于密码存储

    // ❌ 绝对不要
    hash := sha1.Sum([]byte(password))
    
  2. 不要用于数字签名

    // ❌ 不安全
    signature := sha1.Sum(message)
    
  3. 不要用于证书生成

    // ❌ 已禁止
    // 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(推荐)

关键要点

  1. SHA-1 已死:2017 年已被实际碰撞攻击攻破
  2. 仅用于非安全场景:校验和、兼容性、历史数据
  3. 新系统使用 SHA-256:所有新代码应使用 SHA-256 或更好
  4. 迁移优先:尽快将现有系统从 SHA-1 迁移到 SHA-256

替代方案总结

需求推荐算法
通用哈希SHA-256
高安全性SHA-384 或 SHA-512
密码存储bcrypt, Argon2, scrypt
HMACHMAC-SHA256
最新标准SHA-3
高性能BLAKE2, BLAKE3

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+
安全状态:⚠️ 不推荐用于安全场景