
本文详解如何在 C# 中实现与 Go 标准库 cipher.NewCFBEncrypter/Decrypter 完全兼容的 AES-CFB(FeedbackSize=128)加解密逻辑,避免自研密码学代码风险,确保跨语言数据互通。
本文详解如何在 c# 中实现与 go 标准库 `cipher.newcfbencrypter/decrypter` 完全兼容的 aes-cfb(feedbacksize=128)加解密逻辑,避免自研密码学代码风险,确保跨语言数据互通。
在 Go 中,cipher.NewCFBEncrypter(block, iv) 创建的流式加密器本质是 AES-CFB 模式(FeedbackSize = block size = 128 bit),其核心行为是:以 IV 为初始反馈寄存器,每轮用 AES 加密当前反馈值,再将结果与明文逐字节异或(XOR),并将该字节输出作为下一轮反馈寄存器的最高字节(其余左移填充)。Go 的 XORKeyStream(dst, src) 即执行该流式异或操作——它不依赖填充、不改变数据长度,纯字节流变换。
C# 的 AesCryptoServiceProvider(或 RijndaelManaged,已过时但兼容性佳)同样支持 CFB 模式,但关键在于正确配置 FeedbackSize 和禁用填充:
- ✅ CipherMode.CFB:启用 CFB 模式
- ✅ FeedbackSize = 128:匹配 Go 的 cipher.NewCFB* 默认行为(即 full-block feedback,等价于 CFB-128)
- ✅ PaddingMode.None:CFB 是流模式,无需填充;若设为 PKCS7 等会导致异常或错误结果
- ✅ BlockSize = 128, KeySize = 128:对应 AES-128
- ✅ IV 必须为 16 字节(128 bit),且需与 Go 中 r1 完全一致(字节级相同)
以下为生产就绪的 C# 实现(使用现代 Aes 类替代已弃用的 RijndaelManaged):
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public static class AesCfbInterop
{
public static byte[] EncryptAesCfb128(byte[] plaintext, byte[] key, byte[] iv)
{
if (key.Length != 16 || iv.Length != 16)
throw new ArgumentException("Key and IV must be exactly 16 bytes for AES-128-CFB128.");
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CFB;
aes.FeedbackSize = 128; // Critical: matches Go's cipher.NewCFB*
aes.Padding = PaddingMode.None;
aes.BlockSize = 128;
aes.KeySize = 128;
using var encryptor = aes.CreateEncryptor();
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
cs.Write(plaintext, 0, plaintext.Length);
cs.FlushFinalBlock(); // Required for CryptoStream to complete
return ms.ToArray();
}
public static byte[] DecryptAesCfb128(byte[] ciphertext, byte[] key, byte[] iv)
{
if (key.Length != 16 || iv.Length != 16)
throw new ArgumentException("Key and IV must be exactly 16 bytes for AES-128-CFB128.");
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CFB;
aes.FeedbackSize = 128;
aes.Padding = PaddingMode.None;
aes.BlockSize = 128;
aes.KeySize = 128;
using var decryptor = aes.CreateDecryptor();
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write);
cs.Write(ciphertext, 0, ciphertext.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
// 使用示例
class Program
{
static void Main()
{
byte[] data = Encoding.UTF8.GetBytes("0123456789");
byte[] key = Encoding.UTF8.GetBytes("0123456789abcdef"); // 16 bytes
byte[] iv = Encoding.UTF8.GetBytes("1234567890abcdef"); // 16 bytes
Console.WriteLine($"original {BitConverter.ToString(data)}");
byte[] encrypted = AesCfbInterop.EncryptAesCfb128(data, key, iv);
Console.WriteLine($"crypted {BitConverter.ToString(encrypted)}");
byte[] decrypted = AesCfbInterop.DecryptAesCfb128(encrypted, key, iv);
Console.WriteLine($"decrypted {BitConverter.ToString(decrypted)} ({Encoding.UTF8.GetString(decrypted)})");
}
}⚠️ 注意事项:
- 绝对不要自行实现 XORKeyStream 或 AES 基础算法——既违反密码学最佳实践(易引入侧信道、逻辑漏洞),也存在合规风险(如 FIPS、GDPR、行业审计要求)。.NET 内置 Aes 类已通过 FIPS 140-2 验证(启用时),安全可靠。
- Go 与 C# 的 CFB-128 行为完全一致的前提是:FeedbackSize=128、IV 相同、key 相同、无填充。任何偏差(如 FeedbackSize=8)将导致结果不兼容。
- FlushFinalBlock() 在 CryptoStream 中不可省略,否则末尾字节可能未写入。
- 推荐使用 Aes.Create()(.NET Core 2.0+ / .NET 5+)替代 RijndaelManaged,后者已标记为过时且在 .NET 6+ 中默认禁用。
通过以上配置,C# 输出将与 Go 完全一致(十六进制字符串相同),实现真正的跨语言加密互操作。


















