summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2024-04-29 19:16:38 +0530
committerGitHub <noreply@github.com>2024-04-29 19:16:38 +0530
commitb02ffec37c73be8a73b95b33b32efa693e84e01b (patch)
tree6bdcda1ee6e6e7d1b63d05320fe2236dfa86999b
parent7d937045910968fbb2c050e803d79bc1c1e5984b (diff)
fix(ext/node): exporting rsa public keys (#23596)
Initial support for exporting rsa public KeyObject. Current assumption is that RSA keys are stored in pkcs1 der format in key storage. Ref https://github.com/denoland/deno/issues/23471 Ref https://github.com/denoland/deno/issues/18928 Ref https://github.com/denoland/deno/issues/21124
-rw-r--r--ext/node/lib.rs2
-rw-r--r--ext/node/ops/crypto/mod.rs22
-rw-r--r--ext/node/polyfills/internal/crypto/keygen.ts10
-rw-r--r--ext/node/polyfills/internal/crypto/keys.ts34
-rw-r--r--tests/unit_node/crypto/crypto_key_test.ts15
5 files changed, 73 insertions, 10 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index c99467d23..43a5b158e 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -241,6 +241,8 @@ deno_core::extension!(deno_node,
ops::crypto::op_node_ecdh_compute_secret,
ops::crypto::op_node_ecdh_compute_public_key,
ops::crypto::op_node_ecdh_encode_pubkey,
+ ops::crypto::op_node_export_rsa_public_pem,
+ ops::crypto::op_node_export_rsa_spki_der,
ops::crypto::x509::op_node_x509_parse,
ops::crypto::x509::op_node_x509_ca,
ops::crypto::x509::op_node_x509_check_email,
diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs
index ed1b7fc75..f39fb6d10 100644
--- a/ext/node/ops/crypto/mod.rs
+++ b/ext/node/ops/crypto/mod.rs
@@ -42,6 +42,7 @@ use rsa::Oaep;
use rsa::Pkcs1v15Encrypt;
use rsa::RsaPrivateKey;
use rsa::RsaPublicKey;
+use spki::EncodePublicKey;
mod cipher;
mod dh;
@@ -681,13 +682,32 @@ pub async fn op_node_generate_rsa_async(
spawn_blocking(move || generate_rsa(modulus_length, public_exponent)).await?
}
+#[op2]
+#[string]
+pub fn op_node_export_rsa_public_pem(
+ #[buffer] pkcs1_der: &[u8],
+) -> Result<String, AnyError> {
+ let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?;
+ let export = public_key.to_public_key_pem(Default::default())?;
+ Ok(export)
+}
+
+#[op2]
+#[serde]
+pub fn op_node_export_rsa_spki_der(
+ #[buffer] pkcs1_der: &[u8],
+) -> Result<ToJsBuffer, AnyError> {
+ let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?;
+ let export = public_key.to_public_key_der()?.to_vec();
+ Ok(export.into())
+}
+
fn dsa_generate(
modulus_length: usize,
divisor_length: usize,
) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
let mut rng = rand::thread_rng();
use dsa::pkcs8::EncodePrivateKey;
- use dsa::pkcs8::EncodePublicKey;
use dsa::Components;
use dsa::KeySize;
use dsa::SigningKey;
diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts
index f3263aecf..dd5d5ad7e 100644
--- a/ext/node/polyfills/internal/crypto/keygen.ts
+++ b/ext/node/polyfills/internal/crypto/keygen.ts
@@ -7,6 +7,8 @@
import { KeyObject } from "ext:deno_node/internal/crypto/keys.ts";
import { kAesKeyLengths } from "ext:deno_node/internal/crypto/util.ts";
import {
+ PrivateKeyObject,
+ PublicKeyObject,
SecretKeyObject,
setOwnedKey,
} from "ext:deno_node/internal/crypto/keys.ts";
@@ -564,8 +566,8 @@ export function generateKeyPair(
) => void,
) {
createJob(kAsync, type, options).then(([privateKey, publicKey]) => {
- privateKey = new KeyObject("private", setOwnedKey(privateKey));
- publicKey = new KeyObject("public", setOwnedKey(publicKey));
+ privateKey = new PrivateKeyObject(setOwnedKey(privateKey), { type });
+ publicKey = new PublicKeyObject(setOwnedKey(publicKey), { type });
if (typeof options === "object" && options !== null) {
const { publicKeyEncoding, privateKeyEncoding } = options as any;
@@ -766,8 +768,8 @@ export function generateKeyPairSync(
| KeyPairSyncResult<string | Buffer, string | Buffer> {
let [privateKey, publicKey] = createJob(kSync, type, options);
- privateKey = new KeyObject("private", setOwnedKey(privateKey));
- publicKey = new KeyObject("public", setOwnedKey(publicKey));
+ privateKey = new PrivateKeyObject(setOwnedKey(privateKey), { type });
+ publicKey = new PublicKeyObject(setOwnedKey(publicKey), { type });
if (typeof options === "object" && options !== null) {
const { publicKeyEncoding, privateKeyEncoding } = options as any;
diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts
index 4ab8cac4f..8cb9ab690 100644
--- a/ext/node/polyfills/internal/crypto/keys.ts
+++ b/ext/node/polyfills/internal/crypto/keys.ts
@@ -7,6 +7,8 @@
import {
op_node_create_private_key,
op_node_create_public_key,
+ op_node_export_rsa_public_pem,
+ op_node_export_rsa_spki_der,
} from "ext:core/ops";
import {
@@ -360,7 +362,7 @@ class AsymmetricKeyObject extends KeyObject {
}
}
-class PrivateKeyObject extends AsymmetricKeyObject {
+export class PrivateKeyObject extends AsymmetricKeyObject {
constructor(handle: unknown, details: unknown) {
super("private", handle, details);
}
@@ -370,13 +372,35 @@ class PrivateKeyObject extends AsymmetricKeyObject {
}
}
-class PublicKeyObject extends AsymmetricKeyObject {
+export class PublicKeyObject extends AsymmetricKeyObject {
constructor(handle: unknown, details: unknown) {
super("public", handle, details);
}
- export(_options: unknown) {
- notImplemented("crypto.PublicKeyObject.prototype.export");
+ export(options: unknown) {
+ const key = KEY_STORE.get(this[kHandle]);
+ switch (this.asymmetricKeyType) {
+ case "rsa":
+ case "rsa-pss": {
+ switch (options.format) {
+ case "pem":
+ return op_node_export_rsa_public_pem(key);
+ case "der": {
+ if (options.type == "pkcs1") {
+ return key;
+ } else {
+ return op_node_export_rsa_spki_der(key);
+ }
+ }
+ default:
+ throw new TypeError(`exporting ${options.type} is not implemented`);
+ }
+ }
+ default:
+ throw new TypeError(
+ `exporting ${this.asymmetricKeyType} is not implemented`,
+ );
+ }
}
}
@@ -414,4 +438,6 @@ export default {
prepareSecretKey,
setOwnedKey,
SecretKeyObject,
+ PrivateKeyObject,
+ PublicKeyObject,
};
diff --git a/tests/unit_node/crypto/crypto_key_test.ts b/tests/unit_node/crypto/crypto_key_test.ts
index c7c741f3e..013601572 100644
--- a/tests/unit_node/crypto/crypto_key_test.ts
+++ b/tests/unit_node/crypto/crypto_key_test.ts
@@ -15,7 +15,7 @@ import {
} from "node:crypto";
import { promisify } from "node:util";
import { Buffer } from "node:buffer";
-import { assertEquals, assertThrows } from "@std/assert/mod.ts";
+import { assert, assertEquals, assertThrows } from "@std/assert/mod.ts";
const RUN_SLOW_TESTS = Deno.env.get("SLOW_TESTS") === "1";
@@ -402,3 +402,16 @@ SogaIHQjE81ZkmNtU5gM5Q==
`jEwckJ/d5GkF/8TTm+wllq2JNghG/m2JYJIW7vS8Vms53zCTTNSSegTSoIVoxWymwTPw2dTtZi41Lg0O271/WvEmQhiWD2dnjz6D/0F4eyn+QUhcmGCadDFyfp7+8x1XOppSw2YB8vL5WCL0QDdp3TAa/rWI0Hn4OftHMa6HPvatkGs+8XlQOGCCfd3TLg+t1UROgpgmetjoAM67mlwxXMGGu/Tr/EbXnnINKeB0iuSmD1FCxlrgFuYWDKxd79n2jZ74FrS/zto+bqWSI5uUa4Ar7yvXtek1Cu1OFM6vgdN9Y6Po2UD9+IT04EhU03LUDY5paYOO8yohz7p7kqHvpA==`,
);
});
+
+Deno.test("generate rsa export public key", async function () {
+ const { publicKey } = await generateKeyPairAsync("rsa", {
+ modulusLength: 2048,
+ });
+
+ const spkiPem = publicKey.export({ format: "pem", type: "spki" });
+ assert(typeof spkiPem === "string");
+ assert(spkiPem.startsWith("-----BEGIN PUBLIC KEY-----"));
+
+ const der = publicKey.export({ format: "der", type: "spki" });
+ assert(der instanceof Uint8Array);
+});