
Go 的 cipher.NewOFB 不会自动将 IV 写入加密文件,开发者需手动将其写入文件开头(或通过其他方式安全共享),并在解密时读取并使用该 IV。
go 的 `cipher.newofb` 不会自动将 iv 写入加密文件,开发者需手动将其写入文件开头(或通过其他方式安全共享),并在解密时读取并使用该 iv。
在 Go 的 crypto/cipher 包中,cipher.Stream(如 OFB、CTR、CFB)仅提供加密/解密流能力,不负责 IV 的序列化或传输——这是应用层的责任。你当前的代码中虽生成了随机 IV 并传入 cipher.NewOFB,但 cipher.StreamWriter 仅对原始数据流执行流式加/解密,完全不会操作或写入 IV。因此加密结果中自然不含 IV,导致解密端无法复现相同流状态,造成解密失败。
✅ 正确做法:在加密时主动将 IV 写入输出文件头部(例如前 16 字节),并在解密时先读取该 IV,再初始化相同的 OFB 流:
func encryptFile(filename, keystring string) error {
readFile, err := os.Open(filename)
if err != nil {
return err
}
defer readFile.Close()
iv := generateRandomIV(aes.BlockSize)
outFile, err := os.OpenFile(filename+".enc", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer outFile.Close()
// ✅ 第一步:写入 IV(固定长度,AES 块大小 = 16 字节)
if _, err := outFile.Write(iv); err != nil {
return err
}
key := []byte(keystring)
block, err := aes.NewCipher(key)
if err != nil {
return err
}
// ✅ 第二步:基于同一 IV 创建 OFB 流,并跳过已写入的 IV 位置
stream := cipher.NewOFB(block, iv)
writer := &cipher.StreamWriter{S: stream, W: outFile}
// ✅ 第三步:加密剩余数据(不包含 IV)
_, err = io.Copy(writer, readFile)
return err
}
func decryptFile(filename, keystring string) error {
encryptedFile, err := os.Open(filename)
if err != nil {
return err
}
defer encryptedFile.Close()
// ✅ 解密时先读取前 16 字节作为 IV
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(encryptedFile, iv); err != nil {
return fmt.Errorf("failed to read IV: %w", err)
}
outFile, err := os.OpenFile(filename+".dec", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer outFile.Close()
key := []byte(keystring)
block, err := aes.NewCipher(key)
if err != nil {
return err
}
stream := cipher.NewOFB(block, iv)
reader := &cipher.StreamReader{S: stream, R: encryptedFile}
_, err = io.Copy(outFile, reader)
return err
}⚠️ 注意事项:
- IV 必须是密码学安全的随机值(你已用 rand.Reader 正确实现);
- IV 长度必须严格匹配分组密码块大小(AES 为 16 字节),不可截断或填充;
- 绝不重复使用同一 IV + 密钥组合,否则严重破坏安全性;
- 若采用预共享 IV(如固定 IV),则丧失随机性,仅适用于极特殊场景(如设备固件加密),一般不推荐;
- OFB 模式本身不提供完整性保护,建议结合 HMAC 或使用 AEAD(如 gcm)以防范篡改。
总结:IV 是加密上下文的一部分,而非算法内部元数据。Go 的 crypto/cipher 接口设计强调“最小抽象”,将协议级细节(如 IV 编码、密钥派生、认证)交由开发者决策——这既是灵活性的来源,也要求开发者明确承担安全责任。

















