2017-10-19 205 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,它的工作原理,你真的節省了我的時間,謝謝:) –