我想用HKDF作为密钥推导函数来实现椭圆曲线diffie。我在前端使用python后端和(vanilla) javascript。我在后端使用python密码学库,在前端使用Web Crypto api作为加密库。我在两边创建了ECDH密钥对,并交换了pbulic密钥。现在,我正在尝试创建AES共享密钥与交换的公钥和私钥以及HKDF算法。我能够在python后端完成这个任务(我遵循了这个例子的python代码):
def encrypt(public_key, secret):
global loaded_public_key
loaded_public_key = public_key
shared_key = server_private_key.exchange(ec.ECDH(), public_key)
IV = bytes("ddfbccae-b4c4-11", encoding="utf-8")
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=None,
).derive(shared_key)
aes = Cipher(algorithms.AES(derived_key), modes.GCM(IV))
encryptor = aes.encryptor()
padder = padding.PKCS7(128).padder()
padded_data = padder.update(secret.encode()) + padder.finalize()
return encryptor.update(secret.encode()) + encryptor.finalize()但是我不知道如何使用网络加密api来实现它。以下是我的尝试:(但不起作用)
async function deriveSecretKey(privateKey, publicKey) {
let sharedKey = await window.crypto.subtle.deriveKey(
{
name: "ECDH",
public: publicKey
},
privateKey,
{
name: "AES-GCM",
length: 256
},
false,
["encrypt", "decrypt"]
);
return window.crypto.subtle.deriveKey(
{
name: "HKDF",
hash: {name: "SHA-256"} ,
salt: new ArrayBuffer(0),
info: new ArrayBuffer(0)
},
sharedKey,
{
name: "AES-GCM",
length: 256
},
false,
["encrypt", "decrypt"]
);
}如何使用在前端创建与HKDF (与python相同的方式)共享的AES密钥?
发布于 2021-06-11 19:40:32
参考Python代码使用P-384 (又名secp384r1)作为椭圆曲线。这与WebCrypto API兼容,它支持三条曲线P-256 (又名secp256r1)、P-384和P-521 (又名secp521r1),参见EcKeyImportParams。
以下WebCrypto代码使用ECDH生成共享密钥,并使用HKDF从共享密钥派生AES密钥。具体情况如下:
deriveBits() (而不是deriveKey())用ECDH创建共享秘密。importKey()导入,然后使用HKDF导出AES密钥,同样使用deriveBits()导出。
(async () => {
await deriveKey();
})();
async function deriveKey() {
//
// Key import
//
var server_x509 = `-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEd7fej9GYVI7Vt6x5B6XhruHvmE/rnzIj
HmpxP8PKfnfWgrJbyG2cgQc3mf9uusqk1FKImA86rx2+avK8+7xIK9wxuF3x2KQq
nxNp7bUBit3phyhp72Nt/QLXmZHcDKXL
-----END PUBLIC KEY-----`;
var client_pkcs8 = `-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBjr4EGktNtx+3xErsC
MzldruzzfAEEO8Oth1/3b8sNfrqRsAgMnB/oVy024I+15wOhZANiAASbTF7LLedW
dik6nH8JX8WeU0R1ZRlqq0EAZ/t+UrFcSOaVJSOx5jMJ3nrqwuk2DnobDqFwXH6t
ZMsZHh4NFZ+bCVeHJRqy4SCZvQFB/xcksF29p1v14XHYI/XKMGyLLx4=
-----END PRIVATE KEY-----`;
var client_private_key = b64_to_ab(client_pkcs8.replace('-----BEGIN PRIVATE KEY-----', '').replace('-----END PRIVATE KEY-----', ''));
var server_public_key = b64_to_ab(server_x509.replace('-----BEGIN PUBLIC KEY-----', '').replace('-----END PUBLIC KEY-----', ''));
var privateKey = await window.crypto.subtle.importKey(
'pkcs8',
client_private_key,
{ name: "ECDH", namedCurve: "P-384" },
true,
["deriveKey", "deriveBits"]
);
var publicKey = await window.crypto.subtle.importKey(
"spki",
server_public_key,
{ name: "ECDH", namedCurve: "P-384" },
true,
[]
);
//
// Determine shared secret
//
var sharedSecret = await window.crypto.subtle.deriveBits(
{ name: "ECDH", namedCurve: "P-384", public: publicKey },
privateKey,
384
);
console.log("Shared secret:\n", ab_to_b64(sharedSecret).replace(/(.{48})/g,'$1\n'));
//
// Derive key from shared secret via HKDF
//
var sharedSecretKey = await window.crypto.subtle.importKey(
"raw",
sharedSecret,
{ name: "HKDF" },
false,
["deriveKey", "deriveBits"]
);
var derived_key = await crypto.subtle.deriveBits(
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array([]), info: new Uint8Array([]) },
sharedSecretKey,
256
);
console.log("Derived key:\n", ab_to_b64(derived_key).replace(/(.{48})/g,'$1\n'))
};
function b64_to_ab(base64_string){
return Uint8Array.from(atob(base64_string), c => c.charCodeAt(0));
}
function ab_to_b64(arrayBuffer){
return btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}
具有以下输出:
Shared secret:
xbU6oDHMTYj3O71liM5KEJof3/0P4HlHJ28k7qtdqU/36llCizIlOWXtj8v+IngF
Derived key:
Yh0FkhqrT9XDQqIiSrGv5YmBjCSj9jhR5fF6HusbN1Q=要将生成的AES键与引用的Python代码进行比较,将使用以下Python代码,该代码基于引用的代码,但应用与WebCrypto代码中使用的键对应的预定义密钥。由于此处的重点是密钥派生,因此不考虑AES部分:
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
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
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=None,
).derive(shared_secret)
print('Derived key: ' + base64.b64encode(derived_key).decode('utf8')) # Derived key: Yh0FkhqrT9XDQqIiSrGv5YmBjCSj9jhR5fF6HusbN1Q=
deriveKey()具有以下输出:
Shared secret: xbU6oDHMTYj3O71liM5KEJof3/0P4HlHJ28k7qtdqU/36llCizIlOWXtj8v+IngF
Derived key: Yh0FkhqrT9XDQqIiSrGv5YmBjCSj9jhR5fF6HusbN1Q=它对应于WebCrypto代码的值。
https://stackoverflow.com/questions/67938461
复制相似问题