如何将 ECDH 密钥对的原始表示转换为 JSON Web 密钥?
How to convert raw representations of ECDH key pair into a JSON Web Key?
目前,我正在尝试将通过 ECDH 生成的一对 public/private 密钥(表示为十六进制字符串)传递到 Web Crypto API 的 importKey
函数中。
我从外部来源接收这些密钥,但我通过 node.js 生成了类似的密钥用于测试。曲线是prime256v1
。作为参考,我用来测试的public密钥是04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc
,私钥是4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377
。
const crypto = require('crypto');
const ecdh = crypto.createECDH('prime256v1');
ecdh.generateKeys();
console.log('ecdh p256 pubkey', ecdh.getPublicKey('hex'));
console.log('ecdh p256 prvkey', ecdh.getPrivateKey('hex'));
通过 importKey
的 raw
选项成功导入 public 密钥。
const hexToUintArray = hex => {
const a = [];
for (let i = 0, len = hex.length; i < len; i += 2) {
a.push(parseInt(hex.substr(i, 2), 16));
}
return new Uint8Array(a);
}
const importedKey = await crypto.subtle.importKey(
'raw',
hexToUintArray('04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc'),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);
但是,无法通过相同的方法导入私钥,因为它失败并显示错误 DataError: Data provided to an operation does not meet requirements
,因为 "raw" 选项仅接受 EC public 密钥。
const importedPrvKey = await crypto.subtle.importKey(
'raw',
hexToUintArray('4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377'),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);
我知道如果密钥是 JSON Web 密钥格式,我可以轻松导入密钥,但我不知道有什么方法可以将其从原始格式转换为 JWK 格式,或任何Web Crypto API 接受的其他可导入格式。
通过查看 pem-to-jwk 库源代码的源代码,我自己设法解决了这个问题。该库本身提供从 PEM 到 JWK 的转换。
"d"参数是私钥的ArrayBuffer,url-encodedBase64。
"x" 参数是 ArrayBuffer 中未压缩的 public 键的前半部分,url-encoded 作为 Base64 字符串。
"y" 参数是 ArrayBuffer 中未压缩的 public 键的后半部分,url-encoded 作为 Base64 字符串。
const publicKeyHex = '04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc';
const privateKeyHex = '4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377';
const hexToUintArray = hex => {
const a = [];
for (let i = 0, len = hex.length; i < len; i += 2) {
a.push(parseInt(hex.substr(i, 2), 16));
}
return new Uint8Array(a);
}
const hexToArrayBuf = hex => {
return hexToUintArray(hex).buffer;
}
const arrayBufToBase64UrlEncode = buf => {
let binary = '';
const bytes = new Uint8Array(buf);
for (var i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary)
.replace(/\//g, '_')
.replace(/=/g, '')
.replace(/\+/g, '-');
}
const jwkConv = (prvHex, pubHex) => ({
kty: "EC",
crv: "P-256",
d: arrayBufToBase64UrlEncode(hexToArrayBuf(prvHex)),
x: arrayBufToBase64UrlEncode(hexToArrayBuf(pubHex).slice(1, 33)),
y: arrayBufToBase64UrlEncode(hexToArrayBuf(pubHex).slice(33, 66))
});
const importedPrivateKey = await crypto.subtle.importKey(
'jwk',
jwkConv(privateKeyHex, publicKeyHex),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);
目前,我正在尝试将通过 ECDH 生成的一对 public/private 密钥(表示为十六进制字符串)传递到 Web Crypto API 的 importKey
函数中。
我从外部来源接收这些密钥,但我通过 node.js 生成了类似的密钥用于测试。曲线是prime256v1
。作为参考,我用来测试的public密钥是04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc
,私钥是4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377
。
const crypto = require('crypto');
const ecdh = crypto.createECDH('prime256v1');
ecdh.generateKeys();
console.log('ecdh p256 pubkey', ecdh.getPublicKey('hex'));
console.log('ecdh p256 prvkey', ecdh.getPrivateKey('hex'));
通过 importKey
的 raw
选项成功导入 public 密钥。
const hexToUintArray = hex => {
const a = [];
for (let i = 0, len = hex.length; i < len; i += 2) {
a.push(parseInt(hex.substr(i, 2), 16));
}
return new Uint8Array(a);
}
const importedKey = await crypto.subtle.importKey(
'raw',
hexToUintArray('04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc'),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);
但是,无法通过相同的方法导入私钥,因为它失败并显示错误 DataError: Data provided to an operation does not meet requirements
,因为 "raw" 选项仅接受 EC public 密钥。
const importedPrvKey = await crypto.subtle.importKey(
'raw',
hexToUintArray('4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377'),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);
我知道如果密钥是 JSON Web 密钥格式,我可以轻松导入密钥,但我不知道有什么方法可以将其从原始格式转换为 JWK 格式,或任何Web Crypto API 接受的其他可导入格式。
通过查看 pem-to-jwk 库源代码的源代码,我自己设法解决了这个问题。该库本身提供从 PEM 到 JWK 的转换。
"d"参数是私钥的ArrayBuffer,url-encodedBase64。 "x" 参数是 ArrayBuffer 中未压缩的 public 键的前半部分,url-encoded 作为 Base64 字符串。 "y" 参数是 ArrayBuffer 中未压缩的 public 键的后半部分,url-encoded 作为 Base64 字符串。
const publicKeyHex = '04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc';
const privateKeyHex = '4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377';
const hexToUintArray = hex => {
const a = [];
for (let i = 0, len = hex.length; i < len; i += 2) {
a.push(parseInt(hex.substr(i, 2), 16));
}
return new Uint8Array(a);
}
const hexToArrayBuf = hex => {
return hexToUintArray(hex).buffer;
}
const arrayBufToBase64UrlEncode = buf => {
let binary = '';
const bytes = new Uint8Array(buf);
for (var i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary)
.replace(/\//g, '_')
.replace(/=/g, '')
.replace(/\+/g, '-');
}
const jwkConv = (prvHex, pubHex) => ({
kty: "EC",
crv: "P-256",
d: arrayBufToBase64UrlEncode(hexToArrayBuf(prvHex)),
x: arrayBufToBase64UrlEncode(hexToArrayBuf(pubHex).slice(1, 33)),
y: arrayBufToBase64UrlEncode(hexToArrayBuf(pubHex).slice(33, 66))
});
const importedPrivateKey = await crypto.subtle.importKey(
'jwk',
jwkConv(privateKeyHex, publicKeyHex),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);