使用 HKDF 密钥的 Crypto-js 加密和 Python 解密

Crypto-js encryption and Python decryption using HKDF key

根据关于如何在 JS (Crypto-JS) 和 Python 之间建立共享密钥和派生密钥的 ,我最终可以得到相同的共享密钥和派生密钥两端。

然而,当我尝试如下加密时,我找不到从 Python 正确解密的方法。我的理解是,我可能弄乱了填充或盐和哈希值。

    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)

输出“y+In4kriw0qy4lji6/x14g==”

Python(其中一次尝试):

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))

这里的任何指导将不胜感激,因为我被困了很长一段时间。

(我使用密码进行加密,但在 HKDF 上苦苦挣扎)。

编辑:

这是完整的 Python 代码:

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))

问题是密钥没有在 CryptoJS 代码中正确传递。


发布的 Python 代码生成 LefjQ2pEXmiy/nNZvEJ43i8hJuaAnzbA1Cbn1hOuAgA= 作为 Base64 编码的密钥。这必须使用 Base64 编码器:

在 CryptoJS 代码中导入

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>

特此生成的密文bLdmGA+HLLyFEVtBEuCzVg==可以用Python密码解密:

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'

请注意,出于安全原因,不应使用静态 IV,这样 key/IV 对就不会重复。