2

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

Mohammad_Umer
  • 58
  • 1
  • 6
  • 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 Oct 20 '17 at 10:38
  • (Obviously, the above does not really matter **if** the encryption key is used only once.) – Erwan Legrand Oct 20 '17 at 10:46

2 Answers2

2

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.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • 1
    Right, the Python code above use the first 16 bytes of the key as the IV. **If this was used to encrypt, this would leak half of the key in the process!** Now, this points to another issue in this code: it basically makes the IV useless, because the IV depends solely on the password, just like the key. **IVs are meant to be nonces.** This means they should be used only once. Also, they are not meant to be kept secret and so are usually part of the message. – Erwan Legrand Oct 20 '17 at 10:32
  • 1
    @ErwanLegrand Encryption does not use the first 16 bytes of key as IV, "If this was used to encrypt, this would leak half of the key in the process!" – Mohammad_Umer Oct 20 '17 at 10:47
  • @bartonjs, It works, you really saved my time, thank you :) – Mohammad_Umer Oct 20 '17 at 10:53
0
# coding:utf8
# python3
# pip3 install pycryptodome

import base64
from Crypto.Cipher import AES
from Crypto.Protocol import KDF
from Crypto.Util.Padding import unpad

cipherText = 'i+EKwmlAF0VYh4GwDd+bGf3+yreYsPJW2Oq/w9FXjsp7RI3VqRiqtnqiAD4n6U0JJSTe2ct4B7lgrG+dHxeGcXYEYIERXvU0xnUdH+z3mRwmgYOqCU9HRUKy/z3GKISTm8qH030KTYm3YMBjnKpU8gaRcoDPP/nCiB3o5fPdyspgJgT/qt5BuvwYq7n0qg6ez/Wi4447gq/qHwG3wuuYLSBUCfmIkgGaO1KXqv3SsR8EAhrmMBmPDJfjc3sydNqs5B8J9/JvZFEZULTb8rLQZKQvgHhH9/53Bzs3zmoq0RFbgSueUbyeWb9rLAzYieTz8Yj0srG4GtwPrTPoItc6/hvx5stZ6pX8tgyk9Y3baT0JFMtGgxve7yduy8idTCQdAwRc5NOo4+CBk7P/sIw6+Q=='
the_pass = 'password'

text_dec = base64.b64decode(cipherText)
salt = text_dec[:256]
enc_txt = text_dec[256:]
key_iv_bytes = KDF.PBKDF2(the_pass, salt, dkLen=48)

key = key_iv_bytes[:32]
iv = key_iv_bytes[32:]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(enc_txt)
plaintext = unpad(plaintext, 16)

print(plaintext)
# b'This is my secret string, lorem ipsum'
qux
  • 505
  • 4
  • 9