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

发布于 2025-01-17 06:21:39 字数 2947 浏览 0 评论 0原文

基于此处提供的示例了解如何建立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))

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

二智少女 2025-01-24 06:21:40

问题是 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,以免重复密钥/IV对。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文