2017-10-19 193 views
1

我手头有一个问题,用于解密规格如下的AES加密密文 密文包含: ·256 RFC2898衍生盐的字节,后面是使用密码,“密码”和派生IV进行AES加密的消息。 示例消息是“这是我的秘密串,Lorem存有”和口令是“口令”,它是用C#代码 此消息解密细跟以下C#代码C#RFC2898DeriveBytes正在运行,但Python PBKDF2生成的密钥和IV不能与Python一起使用AES解密

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存有”

但是当我尝试通过以下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 “牛逼串,Lorem存有”

预期输出: “这是我的秘密字符串,Lorem存有”

我试图找到这个问题,当我使用由C#RFC2898DeriveBytes方法生成的keyBytes和IV,该方法也可以正常工作,但python代码不能使用生成的keyBytes和IV正确解密整个邮件。

两个C#RFC2898DeriveBytes和python PBKDF2使用HMACSHA1散列算法中,但C#RFC2898DeriveBytes方法产生不同keyBytes和IV而Python的PBKDF2在返回前16个字节生成keyBytes的用于IV呼叫产生keyBytes。

请给我一些有用的指导方针。

谢谢, 中号乌默尔

+0

这是**不是**生成初始化向量的正确方法。 ** IV必须是唯一的,并且不必保密**您应该做的是使用CPRNG生成IV(请参阅RNGCryptoServiceProvider),并将其添加到加密的消息中。 –

+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的解决方案将是使一个48字节的呼叫PBKDF2,然后将其拆分成32字节的AES密钥和16字节IV。

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

+0

对,上面的Python代码使用密钥的前16个字节作为IV。 **如果这被用于加密,这将泄漏过程中的一半密钥** ** 现在,这指出了在这个代码中的另一个问题:它基本上使得IV无用,因为IV仅取决于密码,就像钥匙一样。 ** IVs应该是随机的**这意味着它们只能使用一次。而且,它们并不意味着保密,因此通常是信息的一部分。 –

+0

@ErwanLegrand加密不使用密钥的前16个字节作为IV,“如果这用于加密,则会在该过程中泄漏一半密钥!” –

+0

@bartonjs,它的工作原理,你真的节省了我的时间,谢谢:) –

相关问题