Python转C# AES CBC PKCS7
我正在尝试把这段C#代码转换成Python(2.5,GAE)。问题是,每次运行加密(对同一个字符串)时,Python脚本生成的加密字符串都不一样。
string Encrypt(string textToEncrypt, string passphrase)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.PKCS7;
rijndaelCipher.KeySize = 128;
rijndaelCipher.BlockSize = 128;
byte[] pwdBytes = Encoding.UTF8.GetBytes(passphrase);
byte[] keyBytes = new byte[16];
int len = pwdBytes.Length;
if (len > keyBytes.Length)
{
len = keyBytes.Length;
}
Array.Copy(pwdBytes, keyBytes, len);
rijndaelCipher.Key = keyBytes;
rijndaelCipher.IV = new byte[16];
ICryptoTransform transform = rijndaelCipher.CreateEncryptor();
byte[] plainText = Encoding.UTF8.GetBytes(textToEncrypt);
return Convert.ToBase64String(transform.TransformFinalBlock(plainText, 0, plainText.Length));
}
Python代码:(PKCS7Encoder: http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html)
from Crypto.Cipher import AES
from pkcs7 import PKCS7Encoder
#declared outside of all functions
key = '####'
mode = AES.MODE_CBC
iv = '\x00' * 16
encryptor = AES.new(key, mode, iv)
encoder = PKCS7Encoder()
def function(self):
text = self.request.get('passwordTextBox')
pad_text = encoder.encode(text)
cipher = encryptor.encrypt(pad_text)
enc_cipher = base64.b64encode(cipher)
C#代码是继承而来的。Python代码必须以相同的方式进行加密和解密,这样C#代码才能正确解码这个值。
注意:我对Python还是个新手 :)
编辑:抱歉,我应该说明一下,有一个函数被调用。
谢谢!
4 个回答
这个奇特的PKCS7编码器并不是简单地用固定长度来填充数据的一个函数。为了实现这个功能,我写了很少的代码。
#!/usr/bin/env python
from Crypto.Cipher import AES
import base64
# the block size for the cipher object; must be 16, 24, or 32 for AES
BLOCK_SIZE = 16
# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
# PKCS7 method
PADDING = '\x06'
mode = AES.MODE_CBC
iv = '\x08' * 16 # static vector: dangerous for security. This could be changed periodically
#
# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
def CryptIt(password, secret):
cipher = AES.new(secret, mode, iv)
encoded = EncodeAES(cipher, password)
return encoded
def DeCryptIt(encoded, secret):
cipher = AES.new(secret, mode, iv)
decoded = DecodeAES(cipher, encoded)
return decoded
希望这能对你有所帮助。祝好!
把Python代码改成了:
from Crypto.Cipher import AES
from pkcs7 import PKCS7Encoder
#declared outside of all functions
key = '####'
mode = AES.MODE_CBC
iv = '\x00' * 16
encoder = PKCS7Encoder()
def function(self):
encryptor = AES.new(key, mode, iv)**
text = self.request.get('passwordTextBox')
pad_text = encoder.encode(text)
cipher = encryptor.encrypt(pad_text)
enc_cipher = base64.b64encode(cipher)
如果有人通过谷歌找到这个页面的话
你的C#代码有问题。
Encrypt
函数接收一个叫做string passphrase
的参数,但在这一行byte[] pwdBytes = Encoding.UTF8.GetBytes(key);
中却试图用key
来引用它。
你需要把key
改成passphrase
。
现在这两个函数的结果对我来说是一样的:
Python
secret_text = 'The rooster crows at midnight!'
key = 'A16ByteKey......'
mode = AES.MODE_CBC
iv = '\x00' * 16
encoder = PKCS7Encoder()
padded_text = encoder.encode(secret_text)
e = AES.new(key, mode, iv)
cipher_text = e.encrypt(padded_text)
print(base64.b64encode(cipher_text))
# e = AES.new(key, mode, iv)
# cipher_text = e.encrypt(padded_text)
# print(base64.b64encode(cipher_text))
C#(修正了上面提到的错误)
Console.WriteLine(Encrypt("The rooster crows at midnight!", "A16ByteKey......"));
Python的结果
XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=
C#的结果
XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=
我怀疑你在Python代码中多次使用了'e'。如果你取消注释我Python脚本最后两行,你会发现输出变得不同。但如果你取消注释最后三行,输出又会变得一样。正如Foon所说,这与CBC的工作原理有关。
CBC(密码块链接)在加密一系列字节时是按块进行的。第一个块的加密是通过将初始向量(IV)与你的明文的前几个字节结合来完成的(比如“公鸡...”)。第二个块则使用第一个操作的结果,而不是IV。
当你第二次调用e.encrypt()
(例如,取消注释Python脚本的最后两行)时,你是从上次停止的地方继续的。加密第一个块时,不再使用IV,而是使用上一个加密块的输出。这就是为什么结果看起来不同的原因。通过取消注释Python脚本的最后三行,你会初始化一个新的加密器,它会在第一个块中使用IV,这样你就能得到相同的结果。