Currently, I am trying to pass a public/private pair of keys generated via ECDH, represented as hex strings, into the importKey
function of the Web Crypto API.
I am receiving these keys from an external source, but I have generated similar keys via node.js for testing. The curve is prime256v1
. For reference, the public key I am using to test that I obtained is 04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc
, and the private key is 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'));
Importing the public key is successfully done via the raw
option of importKey
.
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,
[]
);
However, the private key cannot be imported via the same method, as it fails with the error DataError: Data provided to an operation does not meet requirements
, since the "raw" option only accepts EC public keys.
const importedPrvKey = await crypto.subtle.importKey(
'raw',
hexToUintArray('4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377'),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);
I am aware that I am able to import keys easily if it is in JSON Web Key format, but I am not aware of a method to convert it from the raw format to JWK format, or any other importable format that the Web Crypto API accepts.
Currently, I am trying to pass a public/private pair of keys generated via ECDH, represented as hex strings, into the importKey
function of the Web Crypto API.
I am receiving these keys from an external source, but I have generated similar keys via node.js for testing. The curve is prime256v1
. For reference, the public key I am using to test that I obtained is 04b71388fced2daee34793f74a7dfa982e37ce539a728233bcadaec298fc4ee422165b8db13e657f9c7b27b35364f523ad11fab29d717606140cc6312ec2c685cc
, and the private key is 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'));
Importing the public key is successfully done via the raw
option of importKey
.
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,
[]
);
However, the private key cannot be imported via the same method, as it fails with the error DataError: Data provided to an operation does not meet requirements
, since the "raw" option only accepts EC public keys.
const importedPrvKey = await crypto.subtle.importKey(
'raw',
hexToUintArray('4bd22700ec3450b5f27e47ba70c233a680c981ab02c1432a859ae23111bef377'),
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
);
I am aware that I am able to import keys easily if it is in JSON Web Key format, but I am not aware of a method to convert it from the raw format to JWK format, or any other importable format that the Web Crypto API accepts.
Share Improve this question asked Jul 2, 2019 at 6:56 FreliaFrelia 1311 silver badge10 bronze badges1 Answer
Reset to default 10Managed to solve this myself, by looking at the source code to the the pem-to-jwk library source code. The library itself provides conversion from PEM to JWK.
The "d" parameter is the private key's ArrayBuffer, url-encoded in Base64. The "x" parameter is the first half of the unpressed public key in an ArrayBuffer, url-encoded as a Base64 string. The "y" parameter is the second half of the unpressed public key in an ArrayBuffer, url-encoded as a Base64 string.
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,
[]
);