使用 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 对就不会重复。
根据关于如何在 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 编码器:
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 对就不会重复。