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/rc4 - RC4 流密码(已弃用)

⚠️ 重要安全警告

RC4 是已被攻破的密码算法,不应在现代安全应用中使用!

  • 密码分析攻击:RC4 存在多个已知的密码分析攻击
  • 密钥调度弱点:初始密钥字节存在偏差
  • 已弃用:Go 官方明确标记为弃用
  • 不符合标准:不允许在 FIPS 140 模式下使用
  • ⚠️ 仅用于学习:本文档仅供学习和理解遗留系统

推荐替代方案

  • AES-CTR:使用 crypto/cipher 包的 CTR 模式
  • ChaCha20:现代流密码,性能更好

概述

crypto/rc4 包实现了 RC4 流密码算法。

RC4(Rivest Cipher 4)是一种流密码,曾经广泛用于:

  • SSL/TLS 协议(现已禁用)
  • WEP 和 WPA(Wi-Fi 加密,已被攻破)
  • PDF 加密
  • 其他历史应用

重要:由于存在严重的安全漏洞,RC4 已被所有主要标准弃用。


核心类型和函数

1. Cipher 类型

type Cipher struct {
    // 包含过滤或未导出的字段
}

Cipher 表示一个 RC4 密码实例。

特点

  • 实现 crypto/cipher.Stream 接口
  • 可用于加密和解密(对称算法)
  • 状态包含 256 字节的置换表和两个索引

2. NewCipher 函数

func NewCipher(key []byte) (*Cipher, error)

功能:创建一个新的 RC4 密码实例。

参数

  • key:密钥,长度必须在 1-256 字节之间

返回值

  • *Cipher:RC4 密码实例
  • error:如果密钥长度无效,返回 KeySizeError

密钥要求

  • 最小长度:1 字节
  • 最大长度:256 字节
  • 推荐长度:16-32 字节(128-256 位)

示例

// 有效密钥
cipher, err := rc4.NewCipher([]byte("my-secret-key"))
if err != nil {
    log.Fatal(err)
}

// 无效密钥(太长)
_, err := rc4.NewCipher(make([]byte, 257))
// err: crypto/rc4: invalid key size

3. XORKeyStream 方法

func (c *Cipher) XORKeyStream(dst, src []byte)

功能:对源数据进行加密或解密,结果写入目标切片。

参数

  • dst:目标切片,用于存储结果
  • src:源数据切片

特点

  • 加密和解密使用相同的操作
  • dstsrc 可以是同一个切片(原地操作)
  • 如果 dstsrc 长度不同,会 panic

示例

// 加密
plaintext := []byte("Hello, World!")
ciphertext := make([]byte, len(plaintext))
cipher.XORKeyStream(ciphertext, plaintext)

// 解密(相同操作)
decrypted := make([]byte, len(ciphertext))
cipher.XORKeyStream(decrypted, ciphertext)

4. Reset 方法(已弃用)

func (c *Cipher) Reset()

功能:清除密码实例的状态。

状态:⚠️ 已弃用(Go 1.23)

用途

  • 清除敏感数据
  • 重用密码实例

注意:由于 RC4 已被弃用,此方法也已弃用。


5. KeySizeError 类型

type KeySizeError int

功能:表示密钥大小错误。

方法

func (k KeySizeError) Error() string

示例

_, err := rc4.NewCipher(make([]byte, 300))
if err != nil {
    if keyErr, ok := err.(rc4.KeySizeError); ok {
        fmt.Printf("无效的密钥大小:%d\n", keyErr)
    }
}

完整示例代码

示例 1:基本加密和解密

package main

import (
    "crypto/rc4"
    "encoding/hex"
    "fmt"
    "log"
)

func main() {
    // 1. 创建密码实例
    key := []byte("my-secret-key-123456")
    cipher, err := rc4.NewCipher(key)
    if err != nil {
        log.Fatal(err)
    }

    // 2. 准备明文
    plaintext := []byte("Hello, RC4!")
    fmt.Printf("明文:%s\n", plaintext)

    // 3. 加密
    ciphertext := make([]byte, len(plaintext))
    cipher.XORKeyStream(ciphertext, plaintext)
    fmt.Printf("密文(十六进制):%s\n", hex.EncodeToString(ciphertext))

    // 4. 解密(需要新的密码实例,因为状态已改变)
    decryptCipher, err := rc4.NewCipher(key)
    if err != nil {
        log.Fatal(err)
    }
    
    decrypted := make([]byte, len(ciphertext))
    decryptCipher.XORKeyStream(decrypted, ciphertext)
    fmt.Printf("解密:%s\n", decrypted)

    // 5. 验证
    if string(decrypted) == string(plaintext) {
        fmt.Println("✓ 加密/解密成功")
    }
}

输出

明文:Hello, RC4!
密文(十六进制):f0c8a3d4e5b6...
解密:Hello, RC4!
✓ 加密/解密成功

示例 2:加密文件内容

package main

import (
    "crypto/rc4"
    "encoding/hex"
    "fmt"
    "io"
    "log"
    "os"
)

func encryptFile(inputPath, outputPath string, key []byte) error {
    // 1. 创建密码
    cipher, err := rc4.NewCipher(key)
    if err != nil {
        return err
    }

    // 2. 读取源文件
    inputData, err := os.ReadFile(inputPath)
    if err != nil {
        return err
    }

    // 3. 加密
    encrypted := make([]byte, len(inputData))
    cipher.XORKeyStream(encrypted, inputData)

    // 4. 写入密文(十六进制编码)
    hexData := []byte(hex.EncodeToString(encrypted))
    return os.WriteFile(outputPath, hexData, 0600)
}

func decryptFile(inputPath, outputPath string, key []byte) error {
    // 1. 创建密码
    cipher, err := rc4.NewCipher(key)
    if err != nil {
        return err
    }

    // 2. 读取密文(十六进制)
    hexData, err := os.ReadFile(inputPath)
    if err != nil {
        return err
    }

    // 3. 解码十六进制
    encrypted, err := hex.DecodeString(string(hexData))
    if err != nil {
        return err
    }

    // 4. 解密
    decrypted := make([]byte, len(encrypted))
    cipher.XORKeyStream(decrypted, encrypted)

    // 5. 写入明文
    return os.WriteFile(outputPath, decrypted, 0600)
}

func main() {
    key := []byte("my-secret-key")
    
    // 创建测试文件
    os.WriteFile("test.txt", []byte("这是秘密内容"), 0600)
    
    // 加密
    err := encryptFile("test.txt", "test.enc", key)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("✓ 文件加密完成")
    
    // 解密
    err = decryptFile("test.enc", "test.dec", key)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("✓ 文件解密完成")
    
    // 验证
    content, _ := os.ReadFile("test.dec")
    fmt.Printf("解密内容:%s\n", content)
}

示例 3:流式加密(大数据)

package main

import (
    "crypto/rc4"
    "fmt"
    "io"
    "log"
    "os"
)

// RC4Stream 包装 RC4 密码实现 io.Reader
type RC4Stream struct {
    reader io.Reader
    cipher *rc4.Cipher
    buffer []byte
}

func NewRC4Stream(reader io.Reader, key []byte) (*RC4Stream, error) {
    cipher, err := rc4.NewCipher(key)
    if err != nil {
        return nil, err
    }
    
    return &RC4Stream{
        reader: reader,
        cipher: cipher,
        buffer: make([]byte, 4096),
    }, nil
}

func (r *RC4Stream) Read(p []byte) (int, error) {
    // 从底层读取器读取
    n, err := r.reader.Read(p)
    if n > 0 {
        // 就地加密
        r.cipher.XORKeyStream(p[:n], p[:n])
    }
    return n, err
}

func main() {
    // 1. 创建大文件
    data := make([]byte, 1024*1024) // 1MB
    for i := range data {
        data[i] = byte(i % 256)
    }
    os.WriteFile("large.bin", data, 0600)
    
    // 2. 流式加密
    file, err := os.Open("large.bin")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    
    stream, err := NewRC4Stream(file, []byte("stream-key"))
    if err != nil {
        log.Fatal(err)
    }
    
    // 3. 读取加密数据
    encrypted, err := io.ReadAll(stream)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("✓ 流式加密完成:%d 字节\n", len(encrypted))
}

使用场景(历史参考)

⚠️ 场景 1:遗留系统兼容

// 仅用于兼容旧系统
func decryptLegacyData(encrypted []byte, key []byte) ([]byte, error) {
    cipher, err := rc4.NewCipher(key)
    if err != nil {
        return nil, err
    }
    
    decrypted := make([]byte, len(encrypted))
    cipher.XORKeyStream(decrypted, encrypted)
    return decrypted, nil
}

⚠️ 场景 2:非安全用途

// 仅用于混淆,不用于真正安全
func obfuscateData(data []byte, key []byte) []byte {
    cipher, _ := rc4.NewCipher(key)
    result := make([]byte, len(data))
    cipher.XORKeyStream(result, data)
    return result
}

错误处理

1. 密钥大小错误

func handleKeyError() {
    // 密钥太短(有效,但不安全)
    _, err := rc4.NewCipher([]byte("a"))
    // err == nil,但不推荐
    
    // 密钥太长
    _, err = rc4.NewCipher(make([]byte, 257))
    // err: crypto/rc4: invalid key size
    
    if err != nil {
        log.Printf("密钥错误:%v", err)
    }
}

2. 切片长度不匹配

func handleSliceError() {
    cipher, _ := rc4.NewCipher([]byte("key"))
    
    src := []byte("hello")
    dst := make([]byte, 3) // 长度不匹配!
    
    // 这会 panic!
    // cipher.XORKeyStream(dst, src)
    
    // 正确做法
    dst = make([]byte, len(src))
    cipher.XORKeyStream(dst, src)
}

安全最佳实践

❌ RC4 的问题

  1. 密钥调度攻击(KSA)

    • 初始密钥字节存在统计偏差
    • 前 256-768 字节的输出存在偏差
  2. 相关密钥攻击

    • 相关密钥可导致密钥恢复
  3. 单密钥攻击

    • 只需观察少量密文即可恢复明文
  4. 现实攻击

    • RC4NOMORE 攻击可破解 TLS
    • WEP 可在几分钟内被攻破

✅ 推荐替代方案

方案 1:AES-CTR(推荐)

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/hex"
    "fmt"
    "io"
    "log"
)

func encryptAESCTR(plaintext []byte, key []byte) ([]byte, error) {
    // 1. 创建块密码
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    
    // 2. 创建 CTR 模式
    stream := cipher.NewCTR(block, make([]byte, 16)) // 使用 nonce
    
    // 3. 加密
    ciphertext := make([]byte, len(plaintext))
    stream.XORKeyStream(ciphertext, plaintext)
    
    return ciphertext, nil
}

func main() {
    key := make([]byte, 32) // 256 位
    rand.Read(key)
    
    plaintext := []byte("Hello, AES-CTR!")
    ciphertext, err := encryptAESCTR(plaintext, key)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))
}

方案 2:ChaCha20(现代推荐)

package main

import (
    "crypto/cipher"
    "crypto/rand"
    "encoding/hex"
    "fmt"
    "log"
    
    "golang.org/x/crypto/chacha20"
)

func encryptChaCha20(plaintext []byte, key []byte) ([]byte, error) {
    // 1. 创建 nonce(12 字节)
    nonce := make([]byte, 12)
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return nil, err
    }
    
    // 2. 创建 ChaCha20 流
    stream, err := chacha20.NewUnauthenticatedCipher(key, nonce)
    if err != nil {
        return nil, err
    }
    
    // 3. 加密
    ciphertext := make([]byte, len(plaintext))
    stream.XORKeyStream(ciphertext, plaintext)
    
    // 4. 返回 nonce + 密文
    result := append(nonce, ciphertext...)
    return result, nil
}

func main() {
    key := make([]byte, 32) // 256 位
    rand.Read(key)
    
    plaintext := []byte("Hello, ChaCha20!")
    ciphertext, err := encryptChaCha20(plaintext, key)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("密文:%s\n", hex.EncodeToString(ciphertext))
}

RC4 vs 现代密码对比

特性RC4AES-CTRChaCha20
安全性❌ 已攻破✅ 安全✅ 安全
密钥长度1-256 字节16/24/32 字节32 字节
速度(软件)中等非常快
速度(硬件)非常快(AES-NI)
FIPS 合规❌ 否✅ 是⚠️ 部分
推荐使用❌ 绝不✅ 是✅ 是

迁移指南

从 RC4 迁移到 AES-CTR

// 旧代码(RC4)
cipher, _ := rc4.NewCipher(key)
cipher.XORKeyStream(dst, src)

// 新代码(AES-CTR)
block, _ := aes.NewCipher(key[:16]) // 或 key[:24], key[:32]
stream := cipher.NewCTR(block, nonce)
stream.XORKeyStream(dst, src)

迁移检查清单

  • 识别所有 RC4 使用位置
  • 选择合适的替代方案(AES-CTR 或 ChaCha20)
  • 实现密钥管理(AES 需要固定长度密钥)
  • 实现 nonce/IV 管理
  • 测试加密/解密兼容性
  • 更新协议版本(如果需要)
  • 提供向后兼容选项(如果需要)

总结

RC4 状态

项目状态
安全性❌ 已攻破,不应使用
Go 支持⚠️ 已弃用,仅保留兼容性
FIPS 合规❌ 不允许
推荐使用❌ 绝不用于安全应用

核心 API

// 创建密码
cipher, err := rc4.NewCipher(key []byte)

// 加密/解密
cipher.XORKeyStream(dst, src []byte)

// 重置(已弃用)
cipher.Reset()

关键要点

  1. RC4 已死:不要在新代码中使用
  2. 仅用于学习:理解历史系统
  3. 迁移优先:尽快迁移到 AES 或 ChaCha20
  4. 密钥管理:即使使用 RC4,也要使用足够长的密钥

推荐实践

应该

  • 使用 AES-CTR 或 ChaCha20 替代 RC4
  • 在遗留系统中尽快迁移
  • 理解 RC4 的历史作用

不应该

  • 在新项目中使用 RC4
  • 用于任何安全敏感应用
  • 认为 RC4 提供真正的安全性

替代方案总结

需求推荐方案
流密码ChaCha20
块密码AES-GCM
认证加密AES-GCM 或 ChaCha20-Poly1305
高性能ChaCha20(软件)或 AES-CTR(硬件)

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+
安全状态:⚠️ 已弃用,仅用于学习