Crypto-js encryption and Python decryption using HKDF key

Viewed 382

Based on the example provided here on how to establish a shared secret and derived key between JS (Crypto-JS) and Python, I can end up with the same shared secret and derived key on both ends.

However, when I try to encrypt as below, I cannot find a way to properly decrypt from Python. My understanding is that probably I am messing with the padding or salts and hashes.

    const payload = "hello"
    var iv = CryptoJS.enc.Utf8.parse("1020304050607080");

    var test = CryptoJS.AES.encrypt(
        payload,
        derived_key,
        {iv: iv, mode: CryptoJS.mode.CBC}
    ).toString();

    console.log(test)

Output "y+In4kriw0qy4lji6/x14g=="

Python (one of the attempts):

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad

iv = "1020304050607080"

test_enc = "y+In4kriw0qy4lji6/x14g=="
enc = base64.b64decode(test_enc)

cipher = AES.new(derived_key, AES.MODE_CBC, iv.encode('utf-8'))

print(base64.b64decode(cipher.decrypt(enc)))

print(unpad(cipher.decrypt(enc),16))

Any guidance here would be greatly appreciated as I am stuck for quite some time.

(I have encryption working using a password, but struggling with HKDF).

EDIT:

Here is the full Python code:

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
import base64


from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad


def deriveKey():

  server_pkcs8 = b'''-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBReGpDVmoVTzxNbJx6
aL4L9z1EdB91eonAmAw7mKDocLfCJITXZPUAmM46c6AipTmhZANiAAR3t96P0ZhU
jtW3rHkHpeGu4e+YT+ufMiMeanE/w8p+d9aCslvIbZyBBzeZ/266yqTUUoiYDzqv
Hb5q8rz7vEgr3DG4XfHYpCqfE2nttQGK3emHKGnvY239AteZkdwMpcs=
-----END PRIVATE KEY-----'''

  client_x509 = b'''-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEm0xeyy3nVnYpOpx/CV/FnlNEdWUZaqtB
AGf7flKxXEjmlSUjseYzCd566sLpNg56Gw6hcFx+rWTLGR4eDRWfmwlXhyUasuEg
mb0BQf8XJLBdvadb9eFx2CP1yjBsiy8e
-----END PUBLIC KEY-----'''

  client_public_key = serialization.load_pem_public_key(client_x509)
  server_private_key = serialization.load_pem_private_key(server_pkcs8, password=None)
  shared_secret = server_private_key.exchange(ec.ECDH(), client_public_key)
  print('Shared secret: ' + base64.b64encode(shared_secret).decode('utf8')) # Shared secret: xbU6oDHMTYj3O71liM5KEJof3/0P4HlHJ28k7qtdqU/36llCizIlOWXtj8v+IngF

  salt_bytes = "12345678".encode('utf-8')
  info_bytes = "abc".encode('utf-8')

  derived_key = HKDF(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt_bytes,
    info=info_bytes,
  ).derive(shared_secret)
  print('Derived key:   ' + base64.b64encode(derived_key).decode('utf8'))
  return derived_key

derived_key = deriveKey()
iv = "1020304050607080"

test_enc = "y+In4kriw0qy4lji6/x14g=="
enc = base64.b64decode(test_enc)

cipher = AES.new(derived_key, AES.MODE_CBC, iv.encode('utf-8'))

print(base64.b64decode(cipher.decrypt(enc)))

print(unpad(cipher.decrypt(enc),16))
1 Answers

The issue is that the key is not passed correctly in the CryptoJS code.


The posted Python code generates LefjQ2pEXmiy/nNZvEJ43i8hJuaAnzbA1Cbn1hOuAgA= as Base64-encoded key. This must be imported in the CryptoJS code using the Base64 encoder:

const payload = "hello"
var derived_key = CryptoJS.enc.Base64.parse("LefjQ2pEXmiy/nNZvEJ43i8hJuaAnzbA1Cbn1hOuAgA=")
var iv = CryptoJS.enc.Utf8.parse("1020304050607080");
var test = CryptoJS.AES.encrypt(payload, derived_key, {iv: iv, mode: CryptoJS.mode.CBC}).toString();
document.getElementById("ct").innerHTML = test; // bLdmGA+HLLyFEVtBEuCzVg==
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<p style="font-family:'Courier New', monospace;" id="ct"></p>

The hereby generated ciphertext bLdmGA+HLLyFEVtBEuCzVg== can be decrypted with the Python code:

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

test_enc = "bLdmGA+HLLyFEVtBEuCzVg=="
enc = base64.b64decode(test_enc)
derived_key = base64.b64decode("LefjQ2pEXmiy/nNZvEJ43i8hJuaAnzbA1Cbn1hOuAgA=")
iv = "1020304050607080"
cipher = AES.new(derived_key, AES.MODE_CBC, iv.encode('utf-8'))
print(unpad(cipher.decrypt(enc),16)) # b'hello'

Note that for security reasons, a static IV should not be used so that key/IV pairs are not repeated.

Related