mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
fix(ext/node): export JWK public key (#25239)
Fixes https://github.com/denoland/deno/issues/18928 Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
This commit is contained in:
parent
567b4967a9
commit
00c8a89547
4 changed files with 137 additions and 1 deletions
|
@ -242,6 +242,7 @@ deno_core::extension!(deno_node,
|
||||||
ops::crypto::keys::op_node_export_private_key_pem,
|
ops::crypto::keys::op_node_export_private_key_pem,
|
||||||
ops::crypto::keys::op_node_export_public_key_der,
|
ops::crypto::keys::op_node_export_public_key_der,
|
||||||
ops::crypto::keys::op_node_export_public_key_pem,
|
ops::crypto::keys::op_node_export_public_key_pem,
|
||||||
|
ops::crypto::keys::op_node_export_public_key_jwk,
|
||||||
ops::crypto::keys::op_node_export_secret_key_b64url,
|
ops::crypto::keys::op_node_export_secret_key_b64url,
|
||||||
ops::crypto::keys::op_node_export_secret_key,
|
ops::crypto::keys::op_node_export_secret_key,
|
||||||
ops::crypto::keys::op_node_generate_dh_group_key_async,
|
ops::crypto::keys::op_node_generate_dh_group_key_async,
|
||||||
|
|
|
@ -235,6 +235,16 @@ impl RsaPssPrivateKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EcPublicKey {
|
||||||
|
pub fn to_jwk(&self) -> Result<elliptic_curve::JwkEcKey, AnyError> {
|
||||||
|
match self {
|
||||||
|
EcPublicKey::P224(_) => Err(type_error("Unsupported JWK EC curve: P224")),
|
||||||
|
EcPublicKey::P256(key) => Ok(key.to_jwk()),
|
||||||
|
EcPublicKey::P384(key) => Ok(key.to_jwk()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EcPrivateKey {
|
impl EcPrivateKey {
|
||||||
/// Derives the public key from the private key.
|
/// Derives the public key from the private key.
|
||||||
pub fn to_public_key(&self) -> EcPublicKey {
|
pub fn to_public_key(&self) -> EcPublicKey {
|
||||||
|
@ -848,7 +858,63 @@ fn parse_rsa_pss_params(
|
||||||
Ok(details)
|
Ok(details)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
|
||||||
|
|
||||||
|
fn bytes_to_b64(bytes: &[u8]) -> String {
|
||||||
|
BASE64_URL_SAFE_NO_PAD.encode(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
impl AsymmetricPublicKey {
|
impl AsymmetricPublicKey {
|
||||||
|
fn export_jwk(&self) -> Result<deno_core::serde_json::Value, AnyError> {
|
||||||
|
match self {
|
||||||
|
AsymmetricPublicKey::Ec(key) => {
|
||||||
|
let jwk = key.to_jwk()?;
|
||||||
|
Ok(deno_core::serde_json::json!(jwk))
|
||||||
|
}
|
||||||
|
AsymmetricPublicKey::X25519(key) => {
|
||||||
|
let bytes = key.as_bytes();
|
||||||
|
let jwk = deno_core::serde_json::json!({
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "X25519",
|
||||||
|
"x": bytes_to_b64(bytes),
|
||||||
|
});
|
||||||
|
Ok(jwk)
|
||||||
|
}
|
||||||
|
AsymmetricPublicKey::Ed25519(key) => {
|
||||||
|
let bytes = key.to_bytes();
|
||||||
|
let jwk = deno_core::serde_json::json!({
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "Ed25519",
|
||||||
|
"x": bytes_to_b64(&bytes),
|
||||||
|
});
|
||||||
|
Ok(jwk)
|
||||||
|
}
|
||||||
|
AsymmetricPublicKey::Rsa(key) => {
|
||||||
|
let n = key.n();
|
||||||
|
let e = key.e();
|
||||||
|
|
||||||
|
let jwk = deno_core::serde_json::json!({
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": bytes_to_b64(&n.to_bytes_be()),
|
||||||
|
"e": bytes_to_b64(&e.to_bytes_be()),
|
||||||
|
});
|
||||||
|
Ok(jwk)
|
||||||
|
}
|
||||||
|
AsymmetricPublicKey::RsaPss(key) => {
|
||||||
|
let n = key.key.n();
|
||||||
|
let e = key.key.e();
|
||||||
|
|
||||||
|
let jwk = deno_core::serde_json::json!({
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": bytes_to_b64(&n.to_bytes_be()),
|
||||||
|
"e": bytes_to_b64(&e.to_bytes_be()),
|
||||||
|
});
|
||||||
|
Ok(jwk)
|
||||||
|
}
|
||||||
|
_ => Err(type_error("jwk export not implemented for this key type")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn export_der(&self, typ: &str) -> Result<Box<[u8]>, AnyError> {
|
fn export_der(&self, typ: &str) -> Result<Box<[u8]>, AnyError> {
|
||||||
match typ {
|
match typ {
|
||||||
"pkcs1" => match self {
|
"pkcs1" => match self {
|
||||||
|
@ -1848,6 +1914,18 @@ pub fn op_node_export_secret_key_b64url(
|
||||||
Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(key))
|
Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op2]
|
||||||
|
#[serde]
|
||||||
|
pub fn op_node_export_public_key_jwk(
|
||||||
|
#[cppgc] handle: &KeyObjectHandle,
|
||||||
|
) -> Result<deno_core::serde_json::Value, AnyError> {
|
||||||
|
let public_key = handle
|
||||||
|
.as_public_key()
|
||||||
|
.ok_or_else(|| type_error("key is not an asymmetric public key"))?;
|
||||||
|
|
||||||
|
public_key.export_jwk()
|
||||||
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
#[string]
|
#[string]
|
||||||
pub fn op_node_export_public_key_pem(
|
pub fn op_node_export_public_key_pem(
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
op_node_export_private_key_der,
|
op_node_export_private_key_der,
|
||||||
op_node_export_private_key_pem,
|
op_node_export_private_key_pem,
|
||||||
op_node_export_public_key_der,
|
op_node_export_public_key_der,
|
||||||
|
op_node_export_public_key_jwk,
|
||||||
op_node_export_public_key_pem,
|
op_node_export_public_key_pem,
|
||||||
op_node_export_secret_key,
|
op_node_export_secret_key,
|
||||||
op_node_export_secret_key_b64url,
|
op_node_export_secret_key_b64url,
|
||||||
|
@ -786,8 +787,9 @@ export class PublicKeyObject extends AsymmetricKeyObject {
|
||||||
|
|
||||||
export(options: JwkKeyExportOptions | KeyExportOptions<KeyFormat>) {
|
export(options: JwkKeyExportOptions | KeyExportOptions<KeyFormat>) {
|
||||||
if (options && options.format === "jwk") {
|
if (options && options.format === "jwk") {
|
||||||
notImplemented("jwk public key export not implemented");
|
return op_node_export_public_key_jwk(this[kHandle]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
format,
|
format,
|
||||||
type,
|
type,
|
||||||
|
|
|
@ -498,6 +498,61 @@ MC4CAQAwBQYDK2VwBCIEIJ1hsZ3v/VpguoRK9JLsLMREScVpezJpGXA7rAMcrn9g
|
||||||
assertEquals(pkcs8Actual, pkcs8Expected);
|
assertEquals(pkcs8Actual, pkcs8Expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("RSA export public JWK", async function () {
|
||||||
|
const importKey = "-----BEGIN PUBLIC KEY-----\n" +
|
||||||
|
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqF66soiDvuqUB7ufWtuV\n" +
|
||||||
|
"5a1nZIw90m9qHEl2MeNt66HeEjG2GeHDfF5a4uplutnAh3dwpFweHqGIyB16POTI\n" +
|
||||||
|
"YysJ/rMPKoWZFQ1LEcr23rSgmL49YpifDetl5V/UR+zEygL3UzzZmbdjuyZz+Sjt\n" +
|
||||||
|
"FY+SAoZ9XPCqIaNha9uVFcurW44MvAkhzQR/yy5NWPaJ/yv4oI/exvuZnUwwBHvH\n" +
|
||||||
|
"gwVchfr7Jh5LRmYTPeyuI1lUOovVzE+0Ty/2tFfrm2hpedqYXvEuVu+yJzfuNoLf\n" +
|
||||||
|
"TGfz15J76eoRdFTCTdaG/MQnrzxZnIlmIpdpTPl0xVOwjKRpeYK06GS7EAa7cS9D\n" +
|
||||||
|
"dnsHkF/Mr9Yys5jw/49fXqh9BH3Iy0p5YmeQIMep04CUDFj7MZ+3SK8b0mA4SscH\n" +
|
||||||
|
"dIraZZynLZ1crM0ECAJBldM4TKqIDACYGU7XyRV+419cPJvYybHys5m7thS3QI7E\n" +
|
||||||
|
"LTpMV+WoYtZ5xeBCm7z5i3iPY6eSh2JtTu6oa3ALwwnXPAaZqDIFer8SoQNyVb0v\n" +
|
||||||
|
"EU8bVDeGXm1ha5gcC5KxqqnadO/WDD6Jke79Ji04sBEKTTodSOARyTGpGFEcC3Nn\n" +
|
||||||
|
"xSSScGCxMrGJuTDtnz+Eh6l6ysT+Nei9ZRMxNu8sZKAR43XkVXxF/OdSCbftFOAs\n" +
|
||||||
|
"wyPJtyhQALGPcK5cWPQS2sUCAwEAAQ==\n" +
|
||||||
|
"-----END PUBLIC KEY-----\n";
|
||||||
|
const publicKey = createPublicKey(importKey);
|
||||||
|
|
||||||
|
const jwk = publicKey.export({ format: "jwk" });
|
||||||
|
assertEquals(jwk, {
|
||||||
|
kty: "RSA",
|
||||||
|
n: "qF66soiDvuqUB7ufWtuV5a1nZIw90m9qHEl2MeNt66HeEjG2GeHDfF5a4uplutnAh3dwpFweHqGIyB16POTIYysJ_rMPKoWZFQ1LEcr23rSgmL49YpifDetl5V_UR-zEygL3UzzZmbdjuyZz-SjtFY-SAoZ9XPCqIaNha9uVFcurW44MvAkhzQR_yy5NWPaJ_yv4oI_exvuZnUwwBHvHgwVchfr7Jh5LRmYTPeyuI1lUOovVzE-0Ty_2tFfrm2hpedqYXvEuVu-yJzfuNoLfTGfz15J76eoRdFTCTdaG_MQnrzxZnIlmIpdpTPl0xVOwjKRpeYK06GS7EAa7cS9DdnsHkF_Mr9Yys5jw_49fXqh9BH3Iy0p5YmeQIMep04CUDFj7MZ-3SK8b0mA4SscHdIraZZynLZ1crM0ECAJBldM4TKqIDACYGU7XyRV-419cPJvYybHys5m7thS3QI7ELTpMV-WoYtZ5xeBCm7z5i3iPY6eSh2JtTu6oa3ALwwnXPAaZqDIFer8SoQNyVb0vEU8bVDeGXm1ha5gcC5KxqqnadO_WDD6Jke79Ji04sBEKTTodSOARyTGpGFEcC3NnxSSScGCxMrGJuTDtnz-Eh6l6ysT-Nei9ZRMxNu8sZKAR43XkVXxF_OdSCbftFOAswyPJtyhQALGPcK5cWPQS2sU",
|
||||||
|
e: "AQAB",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("EC export public jwk", async function () {
|
||||||
|
const key = "-----BEGIN PUBLIC KEY-----\n" +
|
||||||
|
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVEEIrFEZ+40Pk90LtKBQ3r7FGAPl\n" +
|
||||||
|
"v4bvX9grC8bNiNiVAcyEKs+QZKQj/0/CUPJV10AmavrUoPk/7Wy0sejopQ==\n" +
|
||||||
|
"-----END PUBLIC KEY-----\n";
|
||||||
|
const publicKey = createPublicKey(key);
|
||||||
|
|
||||||
|
const jwk = publicKey.export({ format: "jwk" });
|
||||||
|
assertEquals(jwk, {
|
||||||
|
kty: "EC",
|
||||||
|
x: "VEEIrFEZ-40Pk90LtKBQ3r7FGAPlv4bvX9grC8bNiNg",
|
||||||
|
y: "lQHMhCrPkGSkI_9PwlDyVddAJmr61KD5P-1stLHo6KU",
|
||||||
|
crv: "P-256",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("Ed25519 export public jwk", async function () {
|
||||||
|
const key = "-----BEGIN PUBLIC KEY-----\n" +
|
||||||
|
"MCowBQYDK2VwAyEAKCVFOD6Le61XM7HbN/MB/N06mX5bti2p50qjLvT1mzE=\n" +
|
||||||
|
"-----END PUBLIC KEY-----\n";
|
||||||
|
const publicKey = createPublicKey(key);
|
||||||
|
|
||||||
|
const jwk = publicKey.export({ format: "jwk" });
|
||||||
|
assertEquals(jwk, {
|
||||||
|
crv: "Ed25519",
|
||||||
|
x: "KCVFOD6Le61XM7HbN_MB_N06mX5bti2p50qjLvT1mzE",
|
||||||
|
kty: "OKP",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("EC import jwk public key", function () {
|
Deno.test("EC import jwk public key", function () {
|
||||||
const publicKey = createPublicKey({
|
const publicKey = createPublicKey({
|
||||||
key: {
|
key: {
|
||||||
|
|
Loading…
Reference in a new issue