2

I'm trying to encrypt in Javascript and decrypt the same data in Python/Django.

Disclaimer: Not Production level code, but to learn concept

I generate a key through Diffie Hellman (Ajax using jQuery), which I pass to the encrypt function below. Input is generally an ID and Password in JSON format.

This is how the encryption takes place. {Found the code somewhere.}

function toWordArray(str){
    return CryptoJS.enc.Utf8.parse(str);
}

function toString(words){
    return CryptoJS.enc.Utf8.stringify(words);
}

function toBase64String(words){
    return CryptoJS.enc.Base64.stringify(words);
}

function encrypt(input, key){
    console.log("Input: " + input);  
    var PROTOCOL_AES256 = 2;
    var secret_key = CryptoJS.SHA256(key);
    var header = toWordArray("AMAZON" + String.fromCharCode(PROTOCOL_AES256));
    var iv = CryptoJS.lib.WordArray.random(16);
    var body = CryptoJS.AES.encrypt(input, secret_key, {iv: iv});

    // construct the packet
    // HEADER + IV + BODY
    header.concat(iv);
    header.concat(body.ciphertext);

    console.log("Bytes before Base64 encoding: " + header); //Line 1
    // encode in base64
    return toBase64String(header);
}

The output I get is as follows:

final key: 47 signin:119:33
Input: {"name":"Buzz","password":"lightyear"} signin:55:25
Bytes before Base64 encoding: 414d415a4f4e02e8ec9b8a949eb754e305acfbe5207f1ebe75272c18146bca57ce399928c0ffd7e506d90e11b011da42b1bd8d2393ec59cc926cef33c2121da3f48dfd59925138 signin:67:25
Payload: QU1BWk9OAujsm4qUnrdU4wWs++Ugfx6+dScsGBRrylfOOZkowP/X5QbZDhGwEdpCsb2NI5PsWcySbO8zwhIdo/SN/VmSUTg= signin:137:37
XHRGEThttp://127.0.0.1:8000/Shenzen/actsignin/?encrypted_string=QU1BWk9OAujsm4qUnrdU4wWs%2B%2BUgfx6%2BdScsGBRrylfOOZkowP%2FX5QbZDhGwEdpCsb2NI5PsWcySbO8zwhIdo%2FSN%2FVmSUTg%3D
[HTTP/1.1 500 Internal Server Error 24ms]

AES failed.

Now I decode it in python as follows:

encrypted_string = request.GET['encrypted_string']

print("Encrypted string decoded: ",base64.b64decode(encrypted_string)) #Line 2
print("----")
sha256_key = SHA256.new(data=bytes(key))
cipher = AES.new(sha256_key.digest(),AES.MODE_CBC)
print(cipher.decrypt(base64.b64decode(encrypted_string)))
[12/Oct/2019 18:39:41] "GET /Shenzen/dh/?step=calcval&level1=37 HTTP/1.1" 200 16
Encrypted string decoded:  b"AMAZON\x02\xcb0\xb5~ \xbf<\x96\x16\x0eJY@\x88\xfe\x94\xc28\xf2j\x19n\x8f\x8d\xdb\xb6yc\x89-L\x93\xa3\x9f\xc3i\xd5\xf4e4'|\xa1\x1f\x9d\xb9k\x95O\xb9<\xc3\xa0\xd7\xa6B^\x85+SSToe"
----
Internal Server Error: /Shenzen/actsignin/
Traceback (most recent call last):
  File "/home/tarunmaganti/Documents/AbhiramSlavery/ProjectLogin/hell/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/tarunmaganti/Documents/AbhiramSlavery/ProjectLogin/hell/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/tarunmaganti/Documents/AbhiramSlavery/ProjectLogin/hell/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/tarunmaganti/Documents/AbhiramSlavery/ProjectLogin/UrbanHell/Shenzen/views.py", line 67, in actsignin
    print(cipher.decrypt(base64.b64decode(encrypted_string)))
  File "/home/tarunmaganti/Documents/AbhiramSlavery/ProjectLogin/hell/lib/python3.6/site-packages/Crypto/Cipher/_mode_cbc.py", line 246, in decrypt
    raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode
[12/Oct/2019 18:39:44] "GET /Shenzen/actsignin/?encrypted_string=QU1BWk9OAsswtX4gvzyWFg5KWUCI%2FpTCOPJqGW6Pjdu2eWOJLUyTo5%2FDadX0ZTQnfKEfnblrlU%2B5PMOg16ZCXoUrU1NUb2U%3D HTTP/1.1" 500 17651put

I expect that Output of Line 1 and Line 2 to be equal but this is what I get:

Javascript Bytes before Base64 encoding: 414d415a4f4e02e8ec9b8a949eb754e305acfbe5207f1ebe75272c18146bca57ce399928c0ffd7e506d90e11b011da42b1bd8d2393ec59cc926cef33c2121da3f48dfd59925138 signin:67:25

Python Encrypted string decoded: b"AMAZON\x02\xcb0\xb5~ \xbf<\x96\x16\x0eJY@\x88\xfe\x94\xc28\xf2j\x19n\x8f\x8d\xdb\xb6yc\x89-L\x93\xa3\x9f\xc3i\xd5\xf4e4'|\xa1\x1f\x9d\xb9k\x95O\xb9<\xc3\xa0\xd7\xa6B^\x85+SSToe"

Can you explain what is happening? How to get the same string as javascript? or How to convert the string in Python into a decryptable(?) data.

Tarun Maganti
  • 3,076
  • 2
  • 35
  • 64

1 Answers1

1
  • In the JavaScript-code, a header (as hexstring: 414d415a4f4e02), the random generated IV and the ciphertext are concatenated and Base64-encoded. In the Python-code, the concatenated data are Base64-decoded. However, the splitting into header, IV and ciphertext doesn't seem to be performed. Therefore the ciphertext and the IV are missing for the decryption. Instead of the ciphertext, the concatenated data are used for decryption, which is wrong. And the AES-instance is created without IV, which is also wrong.

  • On the JavaScript-side, the concatenated data before the Base64-encoding are given in hexadecimal representation by:

    414d415a4f4e02e8ec9b8a949eb754e305acfbe5207f1ebe75272c18146bca57ce399928c0ffd7e506d90e11b011da42b1bd8d2393ec59cc926cef33c2121da3f48dfd59925138 
    

    On the Python-side, the concatenated data after the Base64-decoding are given in hexadecimal representation by:

    414d415a4f4e02cb30b57e20bf3c96160e4a594088fe94c238f26a196e8f8ddbb67963892d4c93a39fc369d5f46534277ca11f9db96b954fb93cc3a0d7a6425e852b5353546f65
    

    The data obviously differ starting with the IV (i.e. starting with inclusively the 8th byte). The JavaScript-code generates a random IV with each run, so that the ciphertext is also different for each run. It is most likely that both data come from different runs, because the beginning is the same and the deviation starts with the part that is randomly generated in each run. Otherwise, the data would have to be changed elsewhere (and probably not by the posted code) in this characteristic way.

  • There may also be a padding-issue. CryptoJS uses PKCS7-padding by default. In contrast, PyCrypto/PyCryptodome doesn't use padding by default (i.e. the user must pad manually), so the padding on the Python-side may not be removed automatically during decryption.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • How is python string base64 decoded like what you got? I got something else. Can you explain, how you got that value? – Tarun Maganti Oct 13 '19 at 18:25
  • It is only the hexadecimal representation of the data Base64-decoded by you: `print(b"AMAZON\x02\xcb0\xb5~ \xbf<\x96\x16\x0eJY@\x88\xfe\x94\xc28\xf2j\x19n\x8f\x8d\xdb\xb6yc\x89-L\x93\xa3\x9f\xc3i\xd5\xf4e4'|\xa1\x1f\x9d\xb9k\x95O\xb9<\xc3\xa0\xd7\xa6B^\x85+SSToe".hex())`, see here for [Python 3](https://stackoverflow.com/a/36149089/9014097) and here for [Python 2](https://stackoverflow.com/a/6624521/9014097). – Topaco Oct 13 '19 at 18:53
  • So the string content after AMAZON is also in Hexadecimal. – Tarun Maganti Oct 13 '19 at 19:28
  • which means, I can extract the IV from the string version I see and other stuff, the rest, I have to Decrypt. – Tarun Maganti Oct 13 '19 at 19:30
  • let me try and let you know. – Tarun Maganti Oct 13 '19 at 19:30