Python与C#中AES加解密结果不一致问题排查
问题描述
在Linux(Debian、Ubuntu)系统中使用Python3的pycryptodome包实现AES加解密,现需在Windows系统用C#实现同款程序,但跨语言加解密结果存在不一致问题:
- Python加密内容在C#解密时因填充错误抛出异常;
- C#加密内容有时无法在Python解密;
- 同语言内加密解密可正常完成。
已验证Windows上的Python程序与Linux表现一致,但必须使用C#实现。
测试示例:
- Python:
t:'TEST+++TEST+++TEST' -> e:'dMTh7CVOf/1PkhTEWRsnv68j' -> d:'TEST+++TEST+++TEST' - C#:
t:'TEST+++TEST+++TEST' -> e:'dMTh7CVOf/1PkhTEWRsnv68jdQ==' -> d:'TEST+++TEST+++TEST'
Python实现代码
#!/usr/bin/env python # -*- coding: utf-8 -*- # import base64 # pip install pycryptodome from Crypto.Cipher import AES KEY = b'\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41' IV = b'\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41' MODE = AES.MODE_CFB BLOCK = 128 def en(plain_text): crypto = AES.new(key=KEY, mode=MODE, iv=IV) crypto.block_size = BLOCK b_plain = plain_text.encode('utf-8') b_encrypted = crypto.encrypt(b_plain) base64_encrypted_text = base64.b64encode(b_encrypted).decode('utf-8') return base64_encrypted_text def de(base64_encrypted_text): crypto = AES.new(key=KEY, mode=MODE, iv=IV) crypto.block_size = BLOCK b_encrypted = base64.b64decode(base64_encrypted_text) b_plain = crypto.decrypt(b_encrypted) plain_text = b_plain.decode('utf-8') return plain_text t = 'TEST+++TEST+++TEST' e = en(t) d = de(e) print(f"t:'{t}' -> e:'{e}' -> d:'{d}'") # t:'TEST+++TEST+++TEST' -> e:'dMTh7CVOf/1PkhTEWRsnv68j' -> d:'TEST+++TEST+++TEST'
C#实现代码
// C# .NET 6.0 Console // using System.Security.Cryptography; byte[] KEY = { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }; byte[] IV = { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }; CipherMode MODE = CipherMode.CFB; int BLOCK = 128; string en(string plain_text) { string base64_encrypted_text; using (var crypto = Aes.Create()) { crypto.Mode = MODE; crypto.BlockSize = BLOCK; crypto.Key = KEY; crypto.IV = IV; using (var s_encrypted = new MemoryStream()) { using (var s_crypto = new CryptoStream(s_encrypted, crypto.CreateEncryptor(KEY, IV), CryptoStreamMode.Write)) { using (var s_plain = new StreamWriter(s_crypto)) { s_plain.Write(plain_text); } } base64_encrypted_text = Convert.ToBase64String(s_encrypted.ToArray()); } } return base64_encrypted_text; } string de(string base64_encrypted_text) { string plain_text; using (var crypto = Aes.Create()) { crypto.Mode = MODE; crypto.BlockSize = BLOCK; crypto.Key = KEY; crypto.IV = IV; var b_encrypted = Convert.FromBase64String(base64_encrypted_text); using (var s_encrypted = new MemoryStream(b_encrypted)) { using (var s_crypto = new CryptoStream(s_encrypted, crypto.CreateDecryptor(KEY, IV), CryptoStreamMode.Read)) { using (var s_plain = new StreamReader(s_crypto)) { plain_text = s_plain.ReadToEnd(); } } } } return plain_text; } var t = "TEST+++TEST+++TEST"; var e = en(t); var d = de(e); Console.WriteLine($"t:'{t}' -> e:'{e}' -> d:'{d}'"); // t:'TEST+++TEST+++TEST' -> e:'dMTh7CVOf/1PkhTEWRsnv68jdQ==' -> d:'TEST+++TEST+++TEST'
问题原因与解决方案
核心差异点
CFB模式反馈位长度不匹配
PyCryptodome的AES-CFB默认使用8位反馈长度(CFB8),而.NET的Aes类默认使用128位反馈长度(CFB128),这是加密结果长度、内容不一致的根本原因。填充机制误触发
CFB属于流加密模式,本身不需要填充,但.NET的CryptoStream因反馈长度不匹配,会在解密时误判需要填充,进而抛出填充错误异常。
修正方案
C#代码修改要点
- 指定
FeedbackSize = 8,与Python端保持一致的CFB反馈长度; - 设置
Padding = PaddingMode.None,明确流模式不需要填充; - 直接操作字节数组替代StreamWriter/StreamReader,避免编码转换潜在问题。
修正后的C#代码
// C# .NET 6.0 Console using System.Security.Cryptography; byte[] KEY = { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }; byte[] IV = { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }; CipherMode MODE = CipherMode.CFB; int BLOCK = 128; int FEEDBACK_SIZE = 8; // 和Python端对齐的CFB反馈长度 string en(string plain_text) { byte[] plainBytes = System.Text.Encoding.UTF8.GetBytes(plain_text); using (var crypto = Aes.Create()) { crypto.Mode = MODE; crypto.BlockSize = BLOCK; crypto.Key = KEY; crypto.IV = IV; crypto.FeedbackSize = FEEDBACK_SIZE; crypto.Padding = PaddingMode.None; byte[] encryptedBytes = crypto.CreateEncryptor().TransformFinalBlock(plainBytes, 0, plainBytes.Length); return Convert.ToBase64String(encryptedBytes); } } string de(string base64_encrypted_text) { byte[] encryptedBytes = Convert.FromBase64String(base64_encrypted_text); using (var crypto = Aes.Create()) { crypto.Mode = MODE; crypto.BlockSize = BLOCK; crypto.Key = KEY; crypto.IV = IV; crypto.FeedbackSize = FEEDBACK_SIZE; crypto.Padding = PaddingMode.None; byte[] plainBytes = crypto.CreateDecryptor().TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length); return System.Text.Encoding.UTF8.GetString(plainBytes); } } var t = "TEST+++TEST+++TEST"; var e = en(t); var d = de(e); Console.WriteLine($"t:'{t}' -> e:'{e}' -> d:'{d}'"); // 输出将与Python完全一致:t:'TEST+++TEST+++TEST' -> e:'dMTh7CVOf/1PkhTEWRsnv68j' -> d:'TEST+++TEST+++TEST'
验证结果
修改后,C#与Python的加密结果完全一致,跨语言加解密可正常互操作:
- Python加密内容能在C#正常解密;
- C#加密内容能在Python正常解密;
- 填充错误异常不再出现。
内容的提问来源于stack exchange,提问作者beta-tester
相关产品推荐
相关产品推荐

