3

I am trying to decrypt on CryptoJS and encrypt in PyCrypto.

I saw this excellent answer which works like charm, the only problem is that it adjusts PyCrypto to work with CryptoJS, and I prefer to define rules how I expect the input and do as little as possible adjustments on PyCrypto with its defaults.

I thought to ask to send the iv, and with Zero Padding.

I wrote the following in JS (ES6):

 const iv = CryptoJS.enc.Hex.parse("1234567889012345"); const key = 'aR1h7EefwlPNVkvTHwfs6w==' const encrypted = AES.encrypt( password, key, { iv, padding: CryptoJS.pad.NoPadding } ); const payload = {password: encrypted, iv: iv}; // make HTTPS POST call 

Python:

def decrypt_from_cryptoJS(encrypted, iv): key = "aR1h7EefwlPNVkvTHwfs6w==".encode() aes = AES.new(key.encode(), AES.MODE_CBC, iv) encrypted = aes.decrypt(base64.b64decode(encrypted))) 

However, I get ValueError: raise TypeError("Object type %s cannot be passed to C code" % type(data))

If I try to create the VI via: CryptoJS.lib.WordArray.random(16) and send it via toString() method of JS, I get:

Incorrect IV length (it must be 16 bytes long) 

for initiating the AES

How can I decrypt in CryptoJS with minimum code adjustments in PyCrypto? I am not sure I am even going the right way..

1 Answer 1

4
  • On the CryptoJS-side, key and IV must be passed as WordArray-objects [1]. CryptoJS provides encoders for the conversion of strings into WordArray-objects and vice versa [2]. If the key is passed as a string, it is treated as passphrase and the actual key and IV are derived from it (in the referenced answer, the algorithm used for this is implemented on the Python-side [3]).

  • The key is Base64-encoded and has a length of 16 bytes after Base64-decoding, so AES-128 is used. For the conversion into a WordArray the Base64-encoder on the CryptoJS-side has to be used. On the Python-side, the key must be Base64-decoded (which still needs to be added to the posted code). Note: In the posted code the key is treated as a Utf8-string, so the key has a length of 24 bytes and AES-192 is used. If this is intended, the Utf8-encoder has to be used on the CryptoJS-side instead of the Base64-encoder. On the Python-side, a Base64-decoding of the key is then not necessary.

  • The IV used for encryption must also be used for decryption. In general, the IV is generated on the encryption side as a random byte sequence. Since the IV isn't secret, it's usually placed in front of the ciphertext and the concatenated data are sent to the recipient who separates both parts. On the CryptoJS-side, concatenation is easily done using WordArray#concat. On the Python-side, the separation is done by slicing (which still needs to be added to the posted code).

  • AES/CBC expects a plaintext whose length is an integer multiple of the blocksize (16 bytes). If the plaintext has a different length, a padding must be used. CryptoJS uses CBC-mode and PKCS7-padding by default, so that neither of these need to be explicitly specified [4]. PKCS7-padding is more reliable than Zero-padding [5]. However, if Zero-padding must be used instead (which is not clear to me from the question), it has to be specified explicitly with padding: CryptoJS.pad.ZeroPadding. PyCrypto doesn't remove the padding automatically, i.e. this must be done manually (which still needs to be added to the posted code). Note, that in contrast to PyCrypto, PyCryptodome supports padding [6].

A possible JavaScript-code is:

var password = "The quick brown fox jumps over the lazy dog"; var iv = CryptoJS.lib.WordArray.random(16); // Generate a random 16 bytes IV var key = CryptoJS.enc.Base64.parse('aR1h7EefwlPNVkvTHwfs6w=='); // Interpret key as Base64 encoded var encrypted = CryptoJS.AES.encrypt(password, key, {iv: iv}); // Use CBC-mode and PKCS7-padding var joinedData = iv.clone().concat(encrypted.ciphertext); // Concat IV and Ciphertext var joinedDataB64 = CryptoJS.enc.Base64.stringify(joinedData); console.log(joinedDataB64.replace(/(.{64})/g, "$1\n"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>

The appropriate Python-code could be:

from Crypto.Cipher import AES import base64 def decrypt_from_cryptoJS(encrypted, iv): key = base64.b64decode("aR1h7EefwlPNVkvTHwfs6w==") # Interpret key as Base64 encoded aes = AES.new(key, AES.MODE_CBC, iv) # Use CBC-mode encrypted = aes.decrypt(encrypted) # Remove Base64 decoding return encrypted def unpadPkcs7(data): return data[:-ord(data[-1])] #return data[:-data[-1]] #Python 3 joinedDataB64 = "sbYEr73hZVKviuQ2rt5RcJ5ugpn7XBLTtZIKKk5JjTXmGojFkAS+dK0D8NNAET6bC/Ai4sx+El5Bzu4igT1S9g==" joinedData = base64.b64decode(joinedDataB64) iv = joinedData[:16] # Determine IV from concatenated data encrypted = joinedData[16:] # Determine ciphertext from concatenated data decrypted = unpadPkcs7(decrypt_from_cryptoJS(encrypted, iv)) # Decrypt and remove PKCS7-padding manually print decrypted #print(decrypted) #Python 3 
Sign up to request clarification or add additional context in comments.

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.