9

I'm trying to convert this C# code to Python (2.5, GAE). The problem is that the encrypted string from the python script is different each time the encryption (on the same string) is run.

string Encrypt(string textToEncrypt, string passphrase) { RijndaelManaged rijndaelCipher = new RijndaelManaged(); rijndaelCipher.Mode = CipherMode.CBC; rijndaelCipher.Padding = PaddingMode.PKCS7; rijndaelCipher.KeySize = 128; rijndaelCipher.BlockSize = 128; byte[] pwdBytes = Encoding.UTF8.GetBytes(passphrase); byte[] keyBytes = new byte[16]; int len = pwdBytes.Length; if (len > keyBytes.Length) { len = keyBytes.Length; } Array.Copy(pwdBytes, keyBytes, len); rijndaelCipher.Key = keyBytes; rijndaelCipher.IV = new byte[16]; ICryptoTransform transform = rijndaelCipher.CreateEncryptor(); byte[] plainText = Encoding.UTF8.GetBytes(textToEncrypt); return Convert.ToBase64String(transform.TransformFinalBlock(plainText, 0, plainText.Length)); } 

Python code: (PKCS7Encoder: http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html)

from Crypto.Cipher import AES from pkcs7 import PKCS7Encoder #declared outside of all functions key = '####' mode = AES.MODE_CBC iv = '\x00' * 16 encryptor = AES.new(key, mode, iv) encoder = PKCS7Encoder() def function(self): text = self.request.get('passwordTextBox') pad_text = encoder.encode(text) cipher = encryptor.encrypt(pad_text) enc_cipher = base64.b64encode(cipher) 

The C# code is inherited. Python code must be encrypted and decrypted the same way so that the C# code can decode the value correctly.

Note: I am a noob at python :)

Edit: sorry. should have made the distinction that there was a function being called.

Thanks!

3
  • 2
    Are you aware how an IV / CBC mode works? If you run that stub each time, it should produce the same output for the same input. If however, you call encryptor.encrypt multiple times with the same input without reinitializing encryptor (to reset it to the same initial state), it will produce different output each time. Commented Jul 19, 2011 at 13:11
  • does my edit change your comment? if not, what's the best way to fix it? Commented Jul 19, 2011 at 13:33
  • fixed by moving: encryptor = AES.new(key, mode, iv) into the function. Thanks @Foon Commented Jul 19, 2011 at 15:12

4 Answers 4

3

Your C# code is invalid.

The Encrypt function takes in the passphrase as string passphrase but then tries to reference it in this line byte[] pwdBytes = Encoding.UTF8.GetBytes(key);

Change key to passphrase.

The two functions now produce identical results for me:

Python

secret_text = 'The rooster crows at midnight!' key = 'A16ByteKey......' mode = AES.MODE_CBC iv = '\x00' * 16 encoder = PKCS7Encoder() padded_text = encoder.encode(secret_text) e = AES.new(key, mode, iv) cipher_text = e.encrypt(padded_text) print(base64.b64encode(cipher_text)) # e = AES.new(key, mode, iv) # cipher_text = e.encrypt(padded_text) # print(base64.b64encode(cipher_text)) 

C# (with the typo fix mentioned above)

Console.WriteLine(Encrypt("The rooster crows at midnight!", "A16ByteKey......")); 

Python Result

XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=

C# Result

XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=

I suspect you're re-using 'e' in your python code multiple times. If you uncomment the last two lines of my python script, you'll see the output is now different. But if you uncomment the last three lines, you'll see the output is the same. As Foon said, this is due to how CBC works.

CBC (Cipher-block chaining) works when encrypting a sequence of bytes in blocks. The first block is encrypted by incorporating the IV with the first bytes of your plaintext ("The rooster..."). The second block uses the result of that first operation instead of the IV.

When you call e.encrypt() a second time (e.g. by uncommmenting the last two lines of the python script) you pick up where you left off. Instead of using the IV when encrypting the first block, it will use the output of the last encrypted block. This is why the results look different. By uncommening the last three lines of the python script you initialize a new encryptor which will use the IV for its first block, causing you to get identical results.

Sign up to request clarification or add additional context in comments.

3 Comments

you are correct. the line in my C# code should have read: byte[] pwdBytes = Encoding.UTF8.GetBytes(passphrase);. fixed in original
Great well explained post, but I coded it up and I still get different encrypted text using the same code you had above. Any ideas? I'm running Visual C# 2010 Express and Python 2.7.2 with pycrypto 2.3.1. Have ran the python side on both Linux and Windows (ActiveState Python) with same results - just can't get it to match with the C#.
In my case, if secret_key's character count < 16, return same results (24 character) both of them. But if it is >= 16, c# returns result that has 44 character (for example: JC3ysTmIE7p8i9i5Ue4ySRWq45aJrOhp9VTfnrIx87Y=) and python returns result that has 24 character (for example: JC3ysTmIE7p8i9i5Ue4ySQ==). This are same characters but python is missing. Why? ( I use Python3 )
3

changed python code to:

from Crypto.Cipher import AES from pkcs7 import PKCS7Encoder #declared outside of all functions key = '####' mode = AES.MODE_CBC iv = '\x00' * 16 encoder = PKCS7Encoder() def function(self): encryptor = AES.new(key, mode, iv)** text = self.request.get('passwordTextBox') pad_text = encoder.encode(text) cipher = encryptor.encrypt(pad_text) enc_cipher = base64.b64encode(cipher) 

in case anyone reaches this page via google

Comments

1

This esotic PKCS7 encoder is anything else then a function that pads with a static lenght. So I implemented it with a very chip of code

#!/usr/bin/env python from Crypto.Cipher import AES import base64 # the block size for the cipher object; must be 16, 24, or 32 for AES BLOCK_SIZE = 16 # the character used for padding--with a block cipher such as AES, the value # you encrypt must be a multiple of BLOCK_SIZE in length. This character is # used to ensure that your value is always a multiple of BLOCK_SIZE # PKCS7 method PADDING = '\x06' mode = AES.MODE_CBC iv = '\x08' * 16 # static vector: dangerous for security. This could be changed periodically # # one-liner to sufficiently pad the text to be encrypted pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING # one-liners to encrypt/encode and decrypt/decode a string # encrypt with AES, encode with base64 EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) def CryptIt(password, secret): cipher = AES.new(secret, mode, iv) encoded = EncodeAES(cipher, password) return encoded def DeCryptIt(encoded, secret): cipher = AES.new(secret, mode, iv) decoded = DecodeAES(cipher, encoded) return decoded 

I hope that this could help. Cheers

1 Comment

I asked this question (and accepted an answer) on July of 2011 almost 2 years ago! Your answer may help others in the future but I have long moved on from this project
0

Microsoft's implementation of PKCS7 is a bit different than Python's.

This article helped me with this problem: http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html

His code for pkcs7 encoding and decoding is on github here: https://github.com/janglin/crypto-pkcs7-example

With that PKCS7 library, this code worked for me:

from Crypto.Cipher import AES aes = AES.new(shared_key, AES.MODE_CBC, IV) aes.encrypt(PKCS7Encoder().encode(data)) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.