diff --git a/src/node_crypto.cc b/src/node_crypto.cc index fdaf91acdc0a1c..716dafdf946a98 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -988,19 +988,33 @@ static X509_STORE* NewRootCertStore() { void GetRootCertificates(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local result[arraysize(root_certs)]; - for (size_t i = 0; i < arraysize(root_certs); i++) { - if (!String::NewFromOneByte( - env->isolate(), - reinterpret_cast(root_certs[i]), - NewStringType::kNormal).ToLocal(&result[i])) { - return; + if (root_cert_store == nullptr) { + root_cert_store = NewRootCertStore(); + } + + stack_st_X509_OBJECT* objs = X509_STORE_get0_objects(root_cert_store); + int num_objs = sk_X509_OBJECT_num(objs); + + std::vector> result; + result.reserve(num_objs); + + for (size_t i = 0; i < num_objs; i++) { + X509_OBJECT* obj = sk_X509_OBJECT_value(objs, i); + if (X509_OBJECT_get_type(obj) == X509_LU_X509) { + X509* cert = X509_OBJECT_get0_X509(obj); + + Local value; + if (!X509ToPEM(env, cert).ToLocal(&value)) { + return; + } + + result.push_back(value); } } args.GetReturnValue().Set( - Array::New(env->isolate(), result, arraysize(root_certs))); + Array::New(env->isolate(), result.data(), result.size())); } diff --git a/src/node_crypto_common.cc b/src/node_crypto_common.cc index 197bc5cd5913a4..7a725f6b500dbc 100644 --- a/src/node_crypto_common.cc +++ b/src/node_crypto_common.cc @@ -968,6 +968,16 @@ MaybeLocal GetPeerCert( return result; } +MaybeLocal X509ToPEM(Environment* env, X509* cert) { + BIOPointer bio(BIO_new(BIO_s_mem())); + + if (PEM_write_bio_X509(bio.get(), cert) == 0) { + return Undefined(env->isolate()); + } + + return ToV8Value(env, bio); +} + MaybeLocal X509ToObject(Environment* env, X509* cert) { EscapableHandleScope scope(env->isolate()); Local context = env->context(); diff --git a/src/node_crypto_common.h b/src/node_crypto_common.h index e42e249ef2ba2e..992959c1c44439 100644 --- a/src/node_crypto_common.h +++ b/src/node_crypto_common.h @@ -131,6 +131,10 @@ v8::MaybeLocal X509ToObject( Environment* env, X509* cert); +v8::MaybeLocal X509ToPEM( + Environment* env, + X509* cert); + } // namespace crypto } // namespace node diff --git a/test/parallel/test-tls-root-certificates.js b/test/parallel/test-tls-root-certificates.js index 5f7aa418ac05a3..f200231fa301a5 100644 --- a/test/parallel/test-tls-root-certificates.js +++ b/test/parallel/test-tls-root-certificates.js @@ -2,30 +2,49 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); const assert = require('assert'); const tls = require('tls'); - -assert(Array.isArray(tls.rootCertificates)); -assert(tls.rootCertificates.length > 0); - -// Getter should return the same object. -assert.strictEqual(tls.rootCertificates, tls.rootCertificates); - -// Array is immutable... -assert.throws(() => tls.rootCertificates[0] = 0, /TypeError/); -assert.throws(() => tls.rootCertificates.sort(), /TypeError/); - -// ...and so is the property. -assert.throws(() => tls.rootCertificates = 0, /TypeError/); - -// Does not contain duplicates. -assert.strictEqual(tls.rootCertificates.length, - new Set(tls.rootCertificates).size); - -assert(tls.rootCertificates.every((s) => { - return s.startsWith('-----BEGIN CERTIFICATE-----\n'); -})); - -assert(tls.rootCertificates.every((s) => { - return s.endsWith('\n-----END CERTIFICATE-----'); -})); +const { fork } = require('child_process'); + +if (process.argv[2] !== 'child') { + // Parent + const NODE_EXTRA_CA_CERTS = fixtures.path('keys', 'ca1-cert.pem'); + + fork( + __filename, + ['child'], + { env: { ...process.env, NODE_EXTRA_CA_CERTS } } + ).on('exit', common.mustCall(function(status) { + assert.strictEqual(status, 0); + })); +} else { + // Child + assert(Array.isArray(tls.rootCertificates)); + assert(tls.rootCertificates.length > 0); + + // Getter should return the same object. + assert.strictEqual(tls.rootCertificates, tls.rootCertificates); + + // Array is immutable... + assert.throws(() => tls.rootCertificates[0] = 0, /TypeError/); + assert.throws(() => tls.rootCertificates.sort(), /TypeError/); + + // ...and so is the property. + assert.throws(() => tls.rootCertificates = 0, /TypeError/); + + // Does not contain duplicates. + assert.strictEqual(tls.rootCertificates.length, + new Set(tls.rootCertificates).size); + + assert(tls.rootCertificates.every((s) => { + return s.startsWith('-----BEGIN CERTIFICATE-----\n'); + })); + + assert(tls.rootCertificates.every((s) => { + return s.endsWith('\n-----END CERTIFICATE-----\n'); + })); + + const extraCert = fixtures.readKey('ca1-cert.pem', 'utf8'); + assert(tls.rootCertificates.includes(extraCert)); +}