Skip to content

Commit cc844ee

Browse files
divybotlittledivy
andauthored
fix(ext/node): decrypt encrypted private keys in publicEncrypt/privateDecrypt and ignore passphrase (#33770)
Enables `test-crypto-keygen-async-encrypted-private-key` in node_compat suite. Co-authored-by: divybot <divybot@users.noreply.github.com> Co-authored-by: Divy Srivastava <me@littledivy.com>
1 parent fd0f204 commit cc844ee

3 files changed

Lines changed: 38 additions & 4 deletions

File tree

ext/node/polyfills/internal/crypto/cipher.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import {
1818
op_node_cipheriv_take,
1919
op_node_create_cipheriv,
2020
op_node_create_decipheriv,
21+
op_node_create_private_key,
2122
op_node_decipheriv_auth_tag,
2223
op_node_decipheriv_decrypt,
2324
op_node_decipheriv_final,
2425
op_node_decipheriv_set_aad,
26+
op_node_export_private_key_pem,
2527
op_node_export_secret_key,
2628
op_node_private_decrypt,
2729
op_node_private_encrypt,
@@ -755,14 +757,36 @@ export function prepareKey(key) {
755757
const data = key.export({ type: "pkcs8", format: "pem" });
756758
return { data: getArrayBufferOrView(data, "key") };
757759
} else if (typeof key == "object") {
758-
const { key: data, encoding } = key;
760+
const { key: data, encoding, passphrase, format, type } = key;
759761
if (isKeyObject(data)) {
760762
return prepareKey(data);
761763
}
762764
if (!isStringOrBuffer(data)) {
763765
throw new TypeError("Invalid key type");
764766
}
765767

768+
// If a passphrase is supplied with raw key material, decrypt the key via
769+
// the native key handle and re-export as unencrypted PKCS#8 PEM so the
770+
// downstream RSA ops can parse it.
771+
if (passphrase != null) {
772+
const keyFormat = format ?? (typeof data === "string" ? "pem" : "der");
773+
const keyData = getArrayBufferOrView(data, "key", encoding);
774+
const passphraseData = getArrayBufferOrView(passphrase, "passphrase");
775+
const handle = op_node_create_private_key(
776+
keyData,
777+
keyFormat,
778+
type ?? "",
779+
passphraseData,
780+
);
781+
const pem = op_node_export_private_key_pem(
782+
handle,
783+
"pkcs8",
784+
null,
785+
null,
786+
);
787+
return { data: getArrayBufferOrView(pem, "key") };
788+
}
789+
766790
return { data: getArrayBufferOrView(data, "key", encoding) };
767791
}
768792

ext/node_crypto/keys.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,18 @@ impl KeyObjectHandle {
902902
"der" => match typ {
903903
"pkcs8" => {
904904
if let Some(passphrase) = passphrase {
905-
SecretDocument::from_pkcs8_encrypted_der(key, passphrase).map_err(
906-
|_| AsymmetricPrivateKeyError::InvalidEncryptedPkcs8PrivateKey,
907-
)?
905+
if EncryptedPrivateKeyInfo::try_from(key).is_ok() {
906+
SecretDocument::from_pkcs8_encrypted_der(key, passphrase)
907+
.map_err(|_| {
908+
AsymmetricPrivateKeyError::InvalidEncryptedPkcs8PrivateKey
909+
})?
910+
} else {
911+
// Node ignores the passphrase when the key isn't actually
912+
// encrypted.
913+
SecretDocument::from_pkcs8_der(key).map_err(|_| {
914+
AsymmetricPrivateKeyError::InvalidPkcs8PrivateKey
915+
})?
916+
}
908917
} else if EncryptedPrivateKeyInfo::try_from(key).is_ok() {
909918
return Err(
910919
AsymmetricPrivateKeyError::EncryptedPkcs8DerRequiresPassphrase,

tests/node_compat/config.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@
513513
"parallel/test-crypto-keygen-async-elliptic-curve-jwk-ec.js": {},
514514
"parallel/test-crypto-keygen-async-elliptic-curve-jwk.js": {},
515515
"parallel/test-crypto-keygen-async-elliptic-curve-jwk-rsa.js": {},
516+
"parallel/test-crypto-keygen-async-encrypted-private-key.js": {},
516517
"parallel/test-crypto-keygen-async-named-elliptic-curve.js": {},
517518
"parallel/test-crypto-keygen-async-rsa.js": {
518519
"ignore": true,

0 commit comments

Comments
 (0)