2
votes

I have a problem in hand to decrypt the AES encrypted cipher-text which specifications are following The cipher-text consists of: · 256 bytes of RFC2898-derived salt, followed by a message that was AES-encrypted using password, 'password' and derived IV. Sample Message is "This is my secret string, lorem ipsum" and password is "password" which is encrypted using C# code This message is decrypting fine with following c# code

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());
        }

Output is: "This is my secret string, lorem ipsum"

but when I try to decrypt the message by following Python2.7 equivalent implementation, it is not decrypting the first few characters correctly

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)

Output is: "�增��"j�����"t string, lorem ipsum"

Expected output: "This is my secret string, lorem ipsum"

I tried to find the problem, when I used the keyBytes and IV generated by the C# RFC2898DeriveBytes method that also works fine python code but python code is not decrypting the whole message correctly using the PBKDF2 generated keyBytes and IV.

Both C# RFC2898DeriveBytes and python PBKDF2 are generating keyBytes using HMACSHA1 hashing algo but C# RFC2898DeriveBytes method is generated different keyBytes and IV whereas Python PBKDF2 is returning first 16 bytes of generated keyBytes for IV call.

Kindly give me some helpful guidelines on this.

Thanks, M Umer

1
This is not the proper way to generate an Initialization Vector. IVs must be unique and need not be secret. What you should do is generate the IV with a CPRNG (see RNGCryptoServiceProvider) and prepend it to the encrypted message.Erwan Legrand
(Obviously, the above does not really matter if the encryption key is used only once.)Erwan Legrand

1 Answers

2
votes

Rfc2898DeriveBytes is a streaming-response object, so concatenating two successive calls is the same as doing one call with both lengths added together.

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();

So your solution in Python would be to make one 48-byte call to PBKDF2, then split it into the 32-byte AES key and the 16-byte IV.

Your decryption response indicates that the key is correct, but the IV is not.