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:源数据切片
特点:
- 加密和解密使用相同的操作
dst和src可以是同一个切片(原地操作)- 如果
dst和src长度不同,会 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 的问题
-
密钥调度攻击(KSA)
- 初始密钥字节存在统计偏差
- 前 256-768 字节的输出存在偏差
-
相关密钥攻击
- 相关密钥可导致密钥恢复
-
单密钥攻击
- 只需观察少量密文即可恢复明文
-
现实攻击
- 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 现代密码对比
| 特性 | RC4 | AES-CTR | ChaCha20 |
|---|---|---|---|
| 安全性 | ❌ 已攻破 | ✅ 安全 | ✅ 安全 |
| 密钥长度 | 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()
关键要点
- RC4 已死:不要在新代码中使用
- 仅用于学习:理解历史系统
- 迁移优先:尽快迁移到 AES 或 ChaCha20
- 密钥管理:即使使用 RC4,也要使用足够长的密钥
推荐实践
✅ 应该:
- 使用 AES-CTR 或 ChaCha20 替代 RC4
- 在遗留系统中尽快迁移
- 理解 RC4 的历史作用
❌ 不应该:
- 在新项目中使用 RC4
- 用于任何安全敏感应用
- 认为 RC4 提供真正的安全性
替代方案总结
| 需求 | 推荐方案 |
|---|---|
| 流密码 | ChaCha20 |
| 块密码 | AES-GCM |
| 认证加密 | AES-GCM 或 ChaCha20-Poly1305 |
| 高性能 | ChaCha20(软件)或 AES-CTR(硬件) |
参考资料
- RFC 7465 - 禁止在 TLS 中使用 RC4
- NIST 特别出版物 800-131A - 密码算法迁移
- RC4 密码分析攻击论文
- Go crypto/cipher 包文档
- Go crypto/aes 包文档
最后更新:2026-04-03
Go 版本:Go 1.23+
安全状态:⚠️ 已弃用,仅用于学习