Repository navigation
crypto.publicDecrypt and crypto.publicEncrypt doesn't detect PEM encoded public key in parameters #13612
Description
Activity
- addedcryptoIssues and PRs related to the crypto subsystem.Issues and PRs related to the crypto subsystem.
on Jun 11, 2017 cc/ @nodejs/crypto
And this seems to be the case in all versions I tried (>= node.js 4.6).
@rinne both of those keys are technically invalid because they start with a newline. Node.js looks at the beginning of the key to figure out what kind of a key it is and assumes PrivateKey if all else fails. (see here) That works when the key actually is a private key (I guess OpenSSL is lenient about whitespace) and doesn't if it's a public key.
- Do we want to explicitly support leading whitespace in keys?
- If not, do we want to explicitly not support leading whitespace and throw an error instead of assuming PrivateKey?
cc @nodejs/crypto
Do we want to explicitly support leading whitespace in keys?
This seems reasonable at first glance.
Then the next question is whether OpenSSL has any function that automatically detects key type, or if a long chain of if/else with comparisons is really the only way.
Oh my. The problem indeed was the newline in the beginning of the key. Weird that it wasn't a problem with the private key though. And isn't the beginning and the end markers with base64 in between just something to get around the possible trash around the actual payload?
Anyways, I leave it to you, whether you want just to close the ticket or do something about it.
One thing that at least needs fixing, is the documentation of the crypto session of node.js. Even in public key encrypt/decrypt it talks about:
crypto.publicEncrypt(public_key, buffer) Added in: v0.11.14 public_key <Object> | <string> key <string> A PEM encoded private key. ...It does say later that the private key can be used instead of the public key, but still it's clearly wrong. And a comment about the whitespace would be welcome :).
And isn't the beginning and the end markers with base64 in between just something to get around the possible trash around the actual payload?
Indeed, if I insert garbage in the beginning of the private key, it still works just fine. Maybe instead of inspecting the beginning of the key, Node.js should just try calling each of OpenSSL's
readfunctions until one works?One thing that at least needs fixing, is the documentation of the crypto session of node.js.
You're welcome to submit a PR for that. 😉
OK, I did. #13633
(I almost lost my interest, when I noticed I can get around this feature by trimming the input. :)Reacted by Gibson FahnestockI think this should probably be fixed, if you read RFC 7468, on page 4:
Data before the encapsulation boundaries are
permitted, and parsers MUST NOT malfunction when processing such
data. Furthermore, parsers SHOULD ignore whitespace and other non-
base64 characters and MUST handle different newline conventions.If there's no objections, I'd like to take on this change
Pull request welcome. Node.js probably needs to start using
PEM_bytes_read_bio()to find the-----BEGINand-----ENDmarkers, then dispatch based on the key type. A convenience function for private keys exists:node/deps/openssl/openssl/crypto/pem/pem_pkey.c
Lines 78 to 148 in b3e5367
EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u) { char *nm = NULL; const unsigned char *p = NULL; unsigned char *data = NULL; long len; int slen; EVP_PKEY *ret = NULL; if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_EVP_PKEY, bp, cb, u)) return NULL; p = data; if (strcmp(nm, PEM_STRING_PKCS8INF) == 0) { PKCS8_PRIV_KEY_INFO *p8inf; p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, len); if (!p8inf) goto p8err; ret = EVP_PKCS82PKEY(p8inf); if (x) { if (*x) EVP_PKEY_free((EVP_PKEY *)*x); *x = ret; } PKCS8_PRIV_KEY_INFO_free(p8inf); } else if (strcmp(nm, PEM_STRING_PKCS8) == 0) { PKCS8_PRIV_KEY_INFO *p8inf; X509_SIG *p8; int klen; char psbuf[PEM_BUFSIZE]; p8 = d2i_X509_SIG(NULL, &p, len); if (!p8) goto p8err; if (cb) klen = cb(psbuf, PEM_BUFSIZE, 0, u); else klen = PEM_def_callback(psbuf, PEM_BUFSIZE, 0, u); if (klen <= 0) { PEMerr(PEM_F_PEM_READ_BIO_PRIVATEKEY, PEM_R_BAD_PASSWORD_READ); X509_SIG_free(p8); goto err; } p8inf = PKCS8_decrypt(p8, psbuf, klen); X509_SIG_free(p8); OPENSSL_cleanse(psbuf, klen); if (!p8inf) goto p8err; ret = EVP_PKCS82PKEY(p8inf); if (x) { if (*x) EVP_PKEY_free((EVP_PKEY *)*x); *x = ret; } PKCS8_PRIV_KEY_INFO_free(p8inf); } else if ((slen = pem_check_suffix(nm, "PRIVATE KEY")) > 0) { const EVP_PKEY_ASN1_METHOD *ameth; ameth = EVP_PKEY_asn1_find_str(NULL, nm, slen); if (!ameth || !ameth->old_priv_decode) goto p8err; ret = d2i_PrivateKey(ameth->pkey_id, x, &p, len); } p8err: if (ret == NULL) PEMerr(PEM_F_PEM_READ_BIO_PRIVATEKEY, ERR_R_ASN1_LIB); err: OPENSSL_free(nm); OPENSSL_cleanse(data, len); OPENSSL_free(data); return (ret); } Consolidate the public/private key handling in src/node_crypto.cc while you're at it.
Up, this issue is still present in node v8.11
- added a commit that references this issue
on Sep 29, 2018 - added a commit that references this issue
on Jul 27, 2026
crypto.publicDecrypt and crypto.publicEncrypt doesn't detect PEM encoded public key in parameters.