crypto: make PEM parsing RFC7468-compliant
PR-URL: https://github.com/nodejs/node/pull/23164 Fixes: https://github.com/nodejs/node/issues/13612 Fixes: https://github.com/nodejs/node/issues/22815 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
0a1c65079a
commit
ad07e44b37
@ -45,13 +45,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
static const char PUBLIC_KEY_PFX[] = "-----BEGIN PUBLIC KEY-----";
|
|
||||||
static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1;
|
|
||||||
static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----";
|
|
||||||
static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1;
|
|
||||||
static const char CERTIFICATE_PFX[] = "-----BEGIN CERTIFICATE-----";
|
|
||||||
static const int CERTIFICATE_PFX_LEN = sizeof(CERTIFICATE_PFX) - 1;
|
|
||||||
|
|
||||||
static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
|
static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
|
||||||
| ASN1_STRFLGS_UTF8_CONVERT
|
| ASN1_STRFLGS_UTF8_CONVERT
|
||||||
| XN_FLAG_SEP_MULTILINE
|
| XN_FLAG_SEP_MULTILINE
|
||||||
@ -3650,6 +3643,31 @@ enum ParsePublicKeyResult {
|
|||||||
kParsePublicFailed
|
kParsePublicFailed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static ParsePublicKeyResult TryParsePublicKey(
|
||||||
|
EVPKeyPointer* pkey,
|
||||||
|
const BIOPointer& bp,
|
||||||
|
const char* name,
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
std::function<EVP_PKEY*(const unsigned char** p, long l)> parse) {
|
||||||
|
unsigned char* der_data;
|
||||||
|
long der_len; // NOLINT(runtime/int)
|
||||||
|
|
||||||
|
// This skips surrounding data and decodes PEM to DER.
|
||||||
|
{
|
||||||
|
MarkPopErrorOnReturn mark_pop_error_on_return;
|
||||||
|
if (PEM_bytes_read_bio(&der_data, &der_len, nullptr, name,
|
||||||
|
bp.get(), nullptr, nullptr) != 1)
|
||||||
|
return kParsePublicNotRecognized;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenSSL might modify the pointer, so we need to make a copy before parsing.
|
||||||
|
const unsigned char* p = der_data;
|
||||||
|
pkey->reset(parse(&p, der_len));
|
||||||
|
OPENSSL_clear_free(der_data, der_len);
|
||||||
|
|
||||||
|
return *pkey ? kParsePublicOk : kParsePublicFailed;
|
||||||
|
}
|
||||||
|
|
||||||
static ParsePublicKeyResult ParsePublicKey(EVPKeyPointer* pkey,
|
static ParsePublicKeyResult ParsePublicKey(EVPKeyPointer* pkey,
|
||||||
const char* key_pem,
|
const char* key_pem,
|
||||||
int key_pem_len) {
|
int key_pem_len) {
|
||||||
@ -3657,31 +3675,32 @@ static ParsePublicKeyResult ParsePublicKey(EVPKeyPointer* pkey,
|
|||||||
if (!bp)
|
if (!bp)
|
||||||
return kParsePublicFailed;
|
return kParsePublicFailed;
|
||||||
|
|
||||||
// Check if this is a PKCS#8 or RSA public key before trying as X.509.
|
ParsePublicKeyResult ret;
|
||||||
if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
|
|
||||||
pkey->reset(
|
|
||||||
PEM_read_bio_PUBKEY(bp.get(), nullptr, NoPasswordCallback, nullptr));
|
|
||||||
} else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
|
|
||||||
RSAPointer rsa(PEM_read_bio_RSAPublicKey(
|
|
||||||
bp.get(), nullptr, PasswordCallback, nullptr));
|
|
||||||
if (rsa) {
|
|
||||||
pkey->reset(EVP_PKEY_new());
|
|
||||||
if (*pkey)
|
|
||||||
EVP_PKEY_set1_RSA(pkey->get(), rsa.get());
|
|
||||||
}
|
|
||||||
} else if (strncmp(key_pem, CERTIFICATE_PFX, CERTIFICATE_PFX_LEN) == 0) {
|
|
||||||
// X.509 fallback
|
|
||||||
X509Pointer x509(PEM_read_bio_X509(
|
|
||||||
bp.get(), nullptr, NoPasswordCallback, nullptr));
|
|
||||||
if (!x509)
|
|
||||||
return kParsePublicFailed;
|
|
||||||
|
|
||||||
pkey->reset(X509_get_pubkey(x509.get()));
|
// Try PKCS#8 first.
|
||||||
} else {
|
ret = TryParsePublicKey(pkey, bp, "PUBLIC KEY",
|
||||||
return kParsePublicNotRecognized;
|
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
|
||||||
}
|
return d2i_PUBKEY(nullptr, p, l);
|
||||||
|
});
|
||||||
|
if (ret != kParsePublicNotRecognized)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return *pkey ? kParsePublicOk : kParsePublicFailed;
|
// Maybe it is PKCS#1.
|
||||||
|
CHECK(BIO_reset(bp.get()));
|
||||||
|
ret = TryParsePublicKey(pkey, bp, "RSA PUBLIC KEY",
|
||||||
|
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
|
||||||
|
return d2i_PublicKey(EVP_PKEY_RSA, nullptr, p, l);
|
||||||
|
});
|
||||||
|
if (ret != kParsePublicNotRecognized)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// X.509 fallback.
|
||||||
|
CHECK(BIO_reset(bp.get()));
|
||||||
|
return TryParsePublicKey(pkey, bp, "CERTIFICATE",
|
||||||
|
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
|
||||||
|
X509Pointer x509(d2i_X509(nullptr, p, l));
|
||||||
|
return x509 ? X509_get_pubkey(x509.get()) : nullptr;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Verify::Initialize(Environment* env, v8::Local<Object> target) {
|
void Verify::Initialize(Environment* env, v8::Local<Object> target) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user