mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
fix(ext/node): import RSA JWK keys (#25267)
Fixes https://github.com/denoland/deno/issues/24129
This commit is contained in:
parent
00c8a89547
commit
13d7777a6a
4 changed files with 135 additions and 1 deletions
|
@ -233,6 +233,7 @@ deno_core::extension!(deno_node,
|
||||||
ops::crypto::op_node_verify_ed25519,
|
ops::crypto::op_node_verify_ed25519,
|
||||||
ops::crypto::keys::op_node_create_private_key,
|
ops::crypto::keys::op_node_create_private_key,
|
||||||
ops::crypto::keys::op_node_create_ed_raw,
|
ops::crypto::keys::op_node_create_ed_raw,
|
||||||
|
ops::crypto::keys::op_node_create_rsa_jwk,
|
||||||
ops::crypto::keys::op_node_create_ec_jwk,
|
ops::crypto::keys::op_node_create_ec_jwk,
|
||||||
ops::crypto::keys::op_node_create_public_key,
|
ops::crypto::keys::op_node_create_public_key,
|
||||||
ops::crypto::keys::op_node_create_secret_key,
|
ops::crypto::keys::op_node_create_secret_key,
|
||||||
|
|
|
@ -582,6 +582,61 @@ impl KeyObjectHandle {
|
||||||
Ok(KeyObjectHandle::AsymmetricPublic(key))
|
Ok(KeyObjectHandle::AsymmetricPublic(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_rsa_jwk(
|
||||||
|
jwk: RsaJwkKey,
|
||||||
|
is_public: bool,
|
||||||
|
) -> Result<KeyObjectHandle, AnyError> {
|
||||||
|
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
|
||||||
|
|
||||||
|
let n = BASE64_URL_SAFE_NO_PAD.decode(jwk.n.as_bytes())?;
|
||||||
|
let e = BASE64_URL_SAFE_NO_PAD.decode(jwk.e.as_bytes())?;
|
||||||
|
|
||||||
|
if is_public {
|
||||||
|
let public_key = RsaPublicKey::new(
|
||||||
|
rsa::BigUint::from_bytes_be(&n),
|
||||||
|
rsa::BigUint::from_bytes_be(&e),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Rsa(
|
||||||
|
public_key,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
let d = BASE64_URL_SAFE_NO_PAD.decode(
|
||||||
|
jwk
|
||||||
|
.d
|
||||||
|
.ok_or_else(|| type_error("missing RSA private component"))?
|
||||||
|
.as_bytes(),
|
||||||
|
)?;
|
||||||
|
let p = BASE64_URL_SAFE_NO_PAD.decode(
|
||||||
|
jwk
|
||||||
|
.p
|
||||||
|
.ok_or_else(|| type_error("missing RSA private component"))?
|
||||||
|
.as_bytes(),
|
||||||
|
)?;
|
||||||
|
let q = BASE64_URL_SAFE_NO_PAD.decode(
|
||||||
|
jwk
|
||||||
|
.q
|
||||||
|
.ok_or_else(|| type_error("missing RSA private component"))?
|
||||||
|
.as_bytes(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut private_key = RsaPrivateKey::from_components(
|
||||||
|
rsa::BigUint::from_bytes_be(&n),
|
||||||
|
rsa::BigUint::from_bytes_be(&e),
|
||||||
|
rsa::BigUint::from_bytes_be(&d),
|
||||||
|
vec![
|
||||||
|
rsa::BigUint::from_bytes_be(&p),
|
||||||
|
rsa::BigUint::from_bytes_be(&q),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
private_key.precompute()?; // precompute CRT params
|
||||||
|
|
||||||
|
Ok(KeyObjectHandle::AsymmetricPrivate(
|
||||||
|
AsymmetricPrivateKey::Rsa(private_key),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_ec_jwk(
|
pub fn new_ec_jwk(
|
||||||
jwk: &JwkEcKey,
|
jwk: &JwkEcKey,
|
||||||
is_public: bool,
|
is_public: bool,
|
||||||
|
@ -1178,6 +1233,24 @@ pub fn op_node_create_ed_raw(
|
||||||
KeyObjectHandle::new_ed_raw(curve, key, is_public)
|
KeyObjectHandle::new_ed_raw(curve, key, is_public)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct RsaJwkKey {
|
||||||
|
n: String,
|
||||||
|
e: String,
|
||||||
|
d: Option<String>,
|
||||||
|
p: Option<String>,
|
||||||
|
q: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op2]
|
||||||
|
#[cppgc]
|
||||||
|
pub fn op_node_create_rsa_jwk(
|
||||||
|
#[serde] jwk: RsaJwkKey,
|
||||||
|
is_public: bool,
|
||||||
|
) -> Result<KeyObjectHandle, AnyError> {
|
||||||
|
KeyObjectHandle::new_rsa_jwk(jwk, is_public)
|
||||||
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
#[cppgc]
|
#[cppgc]
|
||||||
pub fn op_node_create_ec_jwk(
|
pub fn op_node_create_ec_jwk(
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
op_node_create_ed_raw,
|
op_node_create_ed_raw,
|
||||||
op_node_create_private_key,
|
op_node_create_private_key,
|
||||||
op_node_create_public_key,
|
op_node_create_public_key,
|
||||||
|
op_node_create_rsa_jwk,
|
||||||
op_node_create_secret_key,
|
op_node_create_secret_key,
|
||||||
op_node_derive_public_key_from_private_key,
|
op_node_derive_public_key_from_private_key,
|
||||||
op_node_export_private_key_der,
|
op_node_export_private_key_der,
|
||||||
|
@ -324,7 +325,32 @@ function getKeyObjectHandleFromJwk(key, ctx) {
|
||||||
return op_node_create_ec_jwk(key, isPublic);
|
return op_node_create_ec_jwk(key, isPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeError("rsa jwk imports not implemented");
|
// RSA
|
||||||
|
validateString(key.n, "key.n");
|
||||||
|
validateString(key.e, "key.e");
|
||||||
|
|
||||||
|
const jwk = {
|
||||||
|
kty: key.kty,
|
||||||
|
n: key.n,
|
||||||
|
e: key.e,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isPublic) {
|
||||||
|
validateString(key.d, "key.d");
|
||||||
|
validateString(key.p, "key.p");
|
||||||
|
validateString(key.q, "key.q");
|
||||||
|
validateString(key.dp, "key.dp");
|
||||||
|
validateString(key.dq, "key.dq");
|
||||||
|
validateString(key.qi, "key.qi");
|
||||||
|
jwk.d = key.d;
|
||||||
|
jwk.p = key.p;
|
||||||
|
jwk.q = key.q;
|
||||||
|
jwk.dp = key.dp;
|
||||||
|
jwk.dq = key.dq;
|
||||||
|
jwk.qi = key.qi;
|
||||||
|
}
|
||||||
|
|
||||||
|
return op_node_create_rsa_jwk(jwk, isPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prepareAsymmetricKey(
|
export function prepareAsymmetricKey(
|
||||||
|
|
|
@ -440,6 +440,40 @@ Deno.test("create private key with invalid utf-8 string", function () {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("RSA JWK import public key", function () {
|
||||||
|
const key = {
|
||||||
|
"kty": "RSA",
|
||||||
|
"alg": "RS256",
|
||||||
|
"n":
|
||||||
|
"5Ddosh0Bze5zy-nQ6gAJFpBfL13muCXrTyKYTps61bmnUxpp3bJnt_2N2MXGfuxBENO0Rbc8DhVPd-lNa4H3XjMwIBdxDAwW32z3pfVr8pHyWxeFtK4SCbvX8B0C6n8ZHigJsvdiCNmoj7_LO_QUzIXmXLFvEXtAqzD_hCr0pJxRIr0BrBjYwL23PkxOYzBR-URcd4Ilji6410Eh9NXycyFzKOcqZ7rjG_PnRyUX1EBZH_PN4RExjJuXYgiqhtU-tDjQFzXLhvwAd5s3ThP9lax27A6MUpjLSKkNy-dG5tlaA0QvECfDzA-5eQjcL_OfvbHlKHQH9zPh-U9Q8gsf3iXmbJrypkalUiTCqnzJu5TgZORSg6zmxNyOCz53YxBHEEaF8yROPwxWDylZfC4fxCRTdoAyFgmFLfMbiepV7AZ24KLj4jfMbGfKpkbPq0xirnSAS-3vbOfkgko5X420AttP8Z1ZBbFSD20Ath_TA9PSHiRCak4AXvOoCZg0t-WuMwzkd_B2V_JZZSTb1yBWrKTL1QzUamqlufjdWuz7M-O2Wkb2cyDSESVNuQyJgDkYb0AOWo0BaN3wbOeT_D4cSrjQoo01xQQCZHQ9SVR4QzUQNAiQcSriqEiptHYhbi6R5_GfGAeMHmlJa4atO2hense0Qk4vDc2fc-sbnQ1jPiE",
|
||||||
|
"e": "AQAB",
|
||||||
|
"key_ops": [
|
||||||
|
"verify",
|
||||||
|
],
|
||||||
|
"ext": true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const keyObject = createPublicKey({ key, format: "jwk" });
|
||||||
|
const expectedPem = `-----BEGIN PUBLIC KEY-----
|
||||||
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5Ddosh0Bze5zy+nQ6gAJ
|
||||||
|
FpBfL13muCXrTyKYTps61bmnUxpp3bJnt/2N2MXGfuxBENO0Rbc8DhVPd+lNa4H3
|
||||||
|
XjMwIBdxDAwW32z3pfVr8pHyWxeFtK4SCbvX8B0C6n8ZHigJsvdiCNmoj7/LO/QU
|
||||||
|
zIXmXLFvEXtAqzD/hCr0pJxRIr0BrBjYwL23PkxOYzBR+URcd4Ilji6410Eh9NXy
|
||||||
|
cyFzKOcqZ7rjG/PnRyUX1EBZH/PN4RExjJuXYgiqhtU+tDjQFzXLhvwAd5s3ThP9
|
||||||
|
lax27A6MUpjLSKkNy+dG5tlaA0QvECfDzA+5eQjcL/OfvbHlKHQH9zPh+U9Q8gsf
|
||||||
|
3iXmbJrypkalUiTCqnzJu5TgZORSg6zmxNyOCz53YxBHEEaF8yROPwxWDylZfC4f
|
||||||
|
xCRTdoAyFgmFLfMbiepV7AZ24KLj4jfMbGfKpkbPq0xirnSAS+3vbOfkgko5X420
|
||||||
|
AttP8Z1ZBbFSD20Ath/TA9PSHiRCak4AXvOoCZg0t+WuMwzkd/B2V/JZZSTb1yBW
|
||||||
|
rKTL1QzUamqlufjdWuz7M+O2Wkb2cyDSESVNuQyJgDkYb0AOWo0BaN3wbOeT/D4c
|
||||||
|
SrjQoo01xQQCZHQ9SVR4QzUQNAiQcSriqEiptHYhbi6R5/GfGAeMHmlJa4atO2he
|
||||||
|
nse0Qk4vDc2fc+sbnQ1jPiECAwEAAQ==
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
`;
|
||||||
|
|
||||||
|
const pem = keyObject.export({ format: "pem", type: "spki" });
|
||||||
|
assertEquals(pem, expectedPem);
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("Ed25519 import jwk public key #1", function () {
|
Deno.test("Ed25519 import jwk public key #1", function () {
|
||||||
const key = {
|
const key = {
|
||||||
"kty": "OKP",
|
"kty": "OKP",
|
||||||
|
|
Loading…
Reference in a new issue