PBKDF2 Python Keys vs. .NET Rfc2898

I am trying to write a Python module that will encrypt text that our existing .NET classes can decrypt. As far as I can tell, my lines of code, but this is not decryption (I get the error "Invalid fill length" on the C # side). My pkcs7 code looks good, but studies show that invalid keys can cause the same problem.

What is the difference between these two settings? Python:

derived_key = PBKDF2(crm_key, salt, 256 / 8, iterations) iv = PBKDF2(crm_key, salt, 128 / 8, iterations) encoder = pkcs7.PKCS7Encoder() cipher = AES.new(derived_key, AES.MODE_CBC, iv) decoded = cipher.decrypt(encoded_secret) #encode - just stepped so i could debug. padded_secret = encoder.encode(secret) # 1 encodedtext = cipher.encrypt(padded_secret) # 2 based_secret = base64.b64encode(encodedtext) # 3 

I thought based_secret could be passed to C # and decoded there. But that fails. The same C # encryption code:

 var rfc = new Rfc2898DeriveBytes(key, saltBytes); // create provider & encryptor using (var cryptoProvider = new AesManaged()) { // Set cryptoProvider parameters cryptoProvider.BlockSize = cryptoProvider.LegalBlockSizes[0].MaxSize; cryptoProvider.KeySize = cryptoProvider.LegalKeySizes[0].MaxSize; cryptoProvider.Key = rfc.GetBytes(cryptoProvider.KeySize / 8); cryptoProvider.IV = rfc.GetBytes(cryptoProvider.BlockSize / 8); using (var encryptor = cryptoProvider.CreateEncryptor()) { // Create a MemoryStream. using (var memoryStream = new MemoryStream()) { // Create a CryptoStream using the MemoryStream and the encryptor. using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) { // Convert the passed string to a byte array. var valueBytes = Encoding.UTF8.GetBytes(plainValue); // Write the byte array to the crypto stream and flush it. cryptoStream.Write(valueBytes, 0, valueBytes.Length); cryptoStream.FlushFinalBlock(); // Get an array of bytes from the // MemoryStream that holds the // encrypted data. var encryptBytes = memoryStream.ToArray(); // Close the streams. cryptoStream.Close(); memoryStream.Close(); // Return the encrypted buffer. return Convert.ToBase64String(encryptBytes); } } } 

The Python implementation of pkcs7 that I'm using is: https://gist.github.com/chrix2/4171336

+7
python c # encryption aes pbkdf2
source share
1 answer

First, I confirmed that Rfc2898 and PBKDF2 are the same thing. Then, as stated above, the problem appears as .net-ism. I found on msdn

that the implementation of GetBytes inside Rfc2898DeriveBytes changes with every call, i.e. he has a fortune. (see comments about half the page)

An example in Python (pseudo output):

 derived_key = PBKDF2(key, salt, 32, 1000) iv = PBKDF2(key, salt, 16, 1000) print(base64.b64encode(derived_key)) print(base64.b64encode(iv)) $123456789101112134== $12345678== 

The same (ish) code in .NET (again, pseudo output):

 var rfc = new Rfc2898DeriveBytes(key, saltBytes); using (var cryptoProvider = new AesManaged()) { // Set cryptoProvider parameters cryptoProvider.BlockSize = cryptoProvider.LegalBlockSizes[0].MaxSize; cryptoProvider.KeySize = cryptoProvider.LegalKeySizes[0].MaxSize; cryptoProvider.Key = rfc.GetBytes(cryptoProvider.KeySize / 8); cryptoProvider.IV = rfc.GetBytes(cryptoProvider.BlockSize / 8); } Console.Writeline(Convert.ToBase64(cryptoProvider.Key)); Console.Writeline(Convert.ToBase64(cryptoProvider.IV)); $123456789101112134== $600200300== 

Subsequent calls to rfc.GetBytes always give different results. MSDN says it combines key call sizes. Therefore, if you call GetBytes (20) twice, it is the same as calling GetBytes (20 + 20) or GetBytes (40). Theoretically, this should simply increase the size of the key, and not completely change it.

There are several solutions to this problem that can generate a longer key on the first call, and then cut it both into the derived key And IV, and randomly generate IV, adding it to the encoded message and decoupling it before decrypting it.

Python output slicing gives the same results as .NET. It looks like this:

 derived_key = PBKDF2(key, salt, 32, 1000) iv = PBKDF2(key, salt, 32 + 16, 1000) # We need 16, but we're compensating for .NETs 'already called' awesomeness on the GetBytes method split_key = iv[32:] print(base64.b64encode(derived_key)) print(base64.b64encode(iv)) print(base64.b64encode(split_key)) $ 123456789101112134== # matches our derived key $ 12345678== # doesn't match $ 600200300== # matches. this is the base 64 encoded version of the tailing 16 bytes. 

Enjoy,

+7
source share

All Articles