C#RFC2898DeriveBytes正在运行,但Python PBKDF2生成的密钥和IV不能与Python AES解密一起使用

时间:2017-10-19 00:04:23

标签: c# python pycrypto pbkdf2 python-cryptography

我手头有问题需要解密AES规范所遵循的AES加密密文 密文包括: ·256字节的RFC2898派生盐,然后是使用密码,密码和派生IV进行AES加密的消息。 示例消息是“这是我的秘密字符串,lorem ipsum”,密码是“密码”,使用C#代码加密 使用以下c#代码

解密此消息
private static readonly int SALT_SIZE = 256;
public static void Decrytor(){
// Encrypted Message
           var cipherText = "i+EKwmlAF0VYh4GwDd+bGf3+yreYsPJW2Oq/w9FXjsp7RI3VqRiqtnqiAD4n6U0JJSTe2ct4B7lgrG+dHxeGcXYEYIERXvU0xnUdH+z3mRwmgYOqCU9HRUKy/z3GKISTm8qH030KTYm3YMBjnKpU8gaRcoDPP/nCiB3o5fPdyspgJgT/qt5BuvwYq7n0qg6ez/Wi4447gq/qHwG3wuuYLSBUCfmIkgGaO1KXqv3SsR8EAhrmMBmPDJfjc3sydNqs5B8J9/JvZFEZULTb8rLQZKQvgHhH9/53Bzs3zmoq0RFbgSueUbyeWb9rLAzYieTz8Yj0srG4GtwPrTPoItc6/hvx5stZ6pX8tgyk9Y3baT0JFMtGgxve7yduy8idTCQdAwRc5NOo4+CBk7P/sIw6+Q==";
            var key = "password";
            // Extract the salt from our cipherText
            var allTheBytes = Convert.FromBase64String(cipherText);
            var saltBytes = allTheBytes.Take(SALT_SIZE).ToArray();
            var cipherTextBytes = allTheBytes.Skip(SALT_SIZE).Take(allTheBytes.Length - SALT_SIZE).ToArray();

            var keyDerivationFunction = new Rfc2898DeriveBytes(key, saltBytes);
            // Derive the previous IV from the Key and Salt
            var keyBytes = keyDerivationFunction.GetBytes(32);
            var ivBytes = keyDerivationFunction.GetBytes(16);

            // Create a decrytor to perform the stream transform.
            // Create the streams used for decryption.
            // The default Cipher Mode is CBC and the Padding is PKCS7 which are both good
            var aesManaged = new AesManaged();
            var decryptor = aesManaged.CreateDecryptor(keyBytes, ivBytes);
            var memoryStream = new MemoryStream(cipherTextBytes);
            var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
            var streamReader = new StreamReader(cryptoStream);

            // Return the decrypted bytes from the decrypting stream.
            Console.WriteLine("\n{0}\n", streamReader.ReadToEnd());
        }

输出是:“这是我的秘密字符串,lorem ipsum”

但是当我尝试通过遵循Python2.7等效实现来解密消息时,它并没有正确地解密前几个字符

import base64
from Crypto.Cipher import AES
from Crypto.Protocol import KDF

def p_decrypt( self, text ):
    text_dec = base64.b64decode(text)
    salt = text_dec[:256]
    enc_txt = text_dec[256:]
    key_bytes = KDF.PBKDF2(self.key, salt, dkLen=32)
    iv = KDF.PBKDF2(self.key, salt)
    cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
    return cipher.decrypt(enc_txt)

输出为:“ 增 ”j “t string,lorem ipsum”

预期输出:“这是我的秘密字符串,lorem ipsum”

我试图找到问题,当我使用C#RFC2898DeriveBytes方法生成的keyBytes和IV时,它也能正常工作python代码,但python代码没有使用PBKDF2正确解密整个消息 生成keyBytes和IV。

C#RFC2898DeriveBytes和python PBKDF2都使用HMACSHA1散列算法生成keyBytes,但C#RFC2898DeriveBytes方法生成不同的keyBytes和IV,而Python PBKDF2返回生成的keyBytes的前16个字节用于IV调用

请给我一些有用的指导。

谢谢, M Umer

1 个答案:

答案 0 :(得分:1)

Rfc2898DeriveBytes是一个流式响应对象,因此连接两个连续的调用与将两个长度相加的一次调用相同。

var pbkdf2WithTwoCalls = new Rfc2898DeriveBytes(...)
var pbkdf2WithOneCall = new Rfc2898DeriveBytes(sameParametersAsAbove);

byte[] twoCallA = pbkdf2WithTwoCalls.GetBytes(32);
byte[] twoCallB = pbkdf2WithTwoCalls.GetBytes(16);

byte[] oneCall = pbkdf2WithOneCall.GetBytes(32 + 16);

if (!oneCall.SequenceEquals(twoCallA.Concat(twoCallB))
    throw new TheUniverseMakesNoSenseException();

因此,您在Python中的解决方案是对PBKDF2进行一次48字节的调用,然后将其拆分为32字节的AES密钥和16字节的IV。

您的解密响应表明密钥是正确的,但IV不是。