1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

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
This commit is contained in:
Divy Srivastava 2024-04-29 19:16:38 +05:30 committed by GitHub
parent 7d93704591
commit b02ffec37c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 73 additions and 10 deletions

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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,
};

View file

@ -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);
});