【问题标题】:Python to C# AES CBC PKCS7Python 到 C# AES CBC PKCS7
【发布时间】:2013-12-05 03:46:46
【问题描述】:

我正在尝试将此 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 的菜鸟 :)

编辑:对不起。应该区分有一个函数被调用。

谢谢!

【问题讨论】:

  • 您知道 IV / CBC 模式的工作原理吗?如果您每次都运行该存根,它应该为相同的输入产生相同的输出。但是,如果您使用相同的输入多次调用 encryptor.encrypt 而不重新初始化加密器(将其重置为相同的初始状态),则每次都会产生不同的输出。
  • 我的编辑会改变你的评论吗?如果没有,最好的解决方法是什么?
  • 通过将 encryptor = AES.new(key, mode, iv) 移动到函数中来修复。谢谢@Foon

标签: c# python encryption cryptography aes


【解决方案1】:

您的 C# 代码无效。

Encrypt 函数将密码作为string passphrase,但随后尝试在byte[] pwdBytes = Encoding.UTF8.GetBytes(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 所说,这是由于how CBC works

CBC(密码块链接)在加密块中的字节序列时起作用。第一个块是通过将 IV 与您的明文的第一个字节(“公鸡......”)合并来加密的。第二个块使用第一个操作的结果而不是 IV。

当您第二次调用 e.encrypt() 时(例如,通过取消注释 Python 脚本的最后两行),您会从上次中断的地方继续。加密第一个块时不使用 IV,而是使用最后一个加密块的输出。这就是结果看起来不同的原因。通过取消注释 python 脚本的最后三行,您初始化了一个新的加密器,它将使用 IV 作为其第一个块,从而获得相同的结果。

【讨论】:

  • 你是对的。我的 C# 代码中的行应该是:byte[] pwdBytes = Encoding.UTF8.GetBytes(passphrase);。原版固定
  • 很好解释的帖子,但我把它编码了,我仍然使用你上面的相同代码得到不同的加密文本。有任何想法吗?我正在使用 pycrypto 2.3.1 运行 Visual C# 2010 Express 和 Python 2.7.2。在 Linux 和 Windows(ActiveState Python)上运行 python 端,结果相同 - 只是无法使其与 C# 匹配。
  • 就我而言,如果 secret_key 的字符数 = 16,c# 返回的结果为 44 个字符(例如:JC3ysTmIE7p8i9i5Ue4ySRWq45aJrOhp9VTfnrIx87Y=),python 返回的结果为 24 个字符(例如:JC3ysTmIE7p8i9i5Ue4ySQ==)。这是相同的字符,但缺少 python。为什么? (我用 Python3)
【解决方案2】:

将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)

如果有人通过谷歌访问此页面

【讨论】:

    【解决方案3】:

    这个神秘的 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
    

    我希望这会有所帮助。 干杯

    【讨论】:

    • 我在大约 2 年前的 2011 年 7 月问了这个问题(并接受了答案)!您的回答可能会在将来对其他人有所帮助,但我早已离开这个项目
    【解决方案4】:

    Microsoft 的 PKCS7 实现与 Python 的有点不同。

    这篇文章帮助我解决了这个问题: http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html

    他的 pkcs7 编码和解码代码在 github 上: https://github.com/janglin/crypto-pkcs7-example

    使用那个 PKCS7 库,这段代码对我有用:

    from Crypto.Cipher import AES
    
    aes = AES.new(shared_key, AES.MODE_CBC, IV)
    aes.encrypt(PKCS7Encoder().encode(data))
    

    【讨论】:

      猜你喜欢
      • 2015-08-04
      • 2017-10-11
      • 2021-10-15
      • 2015-06-24
      • 1970-01-01
      • 2013-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多