tls: expose built-in root certificates

Fixes: https://github.com/nodejs/node/issues/25824
PR-URL: https://github.com/nodejs/node/pull/26415
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ron Korving <ron@ronkorving.nl>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
This commit is contained in:
Ben Noordhuis 2019-05-20 11:09:02 +02:00
parent cc7e15f850
commit f1a3968a01
6 changed files with 225 additions and 143 deletions

View File

@ -1384,6 +1384,7 @@ changes:
provided.
For PEM encoded certificates, supported types are "TRUSTED CERTIFICATE",
"X509 CERTIFICATE", and "CERTIFICATE".
See also [`tls.rootCertificates`].
* `cert` {string|string[]|Buffer|Buffer[]} Cert chains in PEM format. One cert
chain should be provided per private key. Each cert chain should consist of
the PEM formatted certificate for a provided private `key`, followed by the
@ -1599,6 +1600,17 @@ TLSv1.2 and below.
console.log(tls.getCiphers()); // ['aes128-gcm-sha256', 'aes128-sha', ...]
```
## tls.rootCertificates
<!-- YAML
added: REPLACEME
-->
* {string[]}
An immutable array of strings representing the root certificates (in PEM format)
used for verifying peer certificates. This is the default value of the `ca`
option to [`tls.createSecureContext()`].
## tls.DEFAULT_ECDH_CURVE
<!-- YAML
added: v0.11.13
@ -1784,6 +1796,7 @@ where `secureSocket` has the same API as `pair.cleartext`.
[`tls.createSecurePair()`]: #tls_tls_createsecurepair_context_isserver_requestcert_rejectunauthorized_options
[`tls.createServer()`]: #tls_tls_createserver_options_secureconnectionlistener
[`tls.getCiphers()`]: #tls_tls_getciphers
[`tls.rootCertificates`]: #tls_tls_rootcertificates
[Chrome's 'modern cryptography' setting]: https://www.chromium.org/Home/chromium-security/education/tls#TOC-Cipher-Suites
[DHE]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
[ECDHE]: https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman

View File

@ -21,6 +21,8 @@
'use strict';
const { Object } = primordials;
const {
ERR_TLS_CERT_ALTNAME_INVALID,
ERR_OUT_OF_RANGE
@ -33,7 +35,7 @@ const { isArrayBufferView } = require('internal/util/types');
const net = require('net');
const { getOptionValue } = require('internal/options');
const url = require('url');
const binding = internalBinding('crypto');
const { getRootCertificates, getSSLCiphers } = internalBinding('crypto');
const { Buffer } = require('buffer');
const EventEmitter = require('events');
const { URL } = require('internal/url');
@ -76,9 +78,25 @@ else
exports.getCiphers = internalUtil.cachedResult(
() => internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true)
() => internalUtil.filterDuplicateStrings(getSSLCiphers(), true)
);
let rootCertificates;
function cacheRootCertificates() {
rootCertificates = Object.freeze(getRootCertificates());
}
Object.defineProperty(exports, 'rootCertificates', {
configurable: false,
enumerable: true,
get: () => {
// Out-of-line caching to promote inlining the getter.
if (!rootCertificates) cacheRootCertificates();
return rootCertificates;
},
});
// Convert protocols array into valid OpenSSL protocols list
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
function convertProtocols(protocols) {

View File

@ -944,6 +944,24 @@ static X509_STORE* NewRootCertStore() {
}
void GetRootCertificates(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Array> result = Array::New(env->isolate(), arraysize(root_certs));
for (size_t i = 0; i < arraysize(root_certs); i++) {
Local<Value> value;
if (!String::NewFromOneByte(env->isolate(),
reinterpret_cast<const uint8_t*>(root_certs[i]),
NewStringType::kNormal).ToLocal(&value) ||
!result->Set(env->context(), i, value).FromMaybe(false)) {
return;
}
}
args.GetReturnValue().Set(result);
}
void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
@ -6870,6 +6888,8 @@ void Initialize(Local<Object> target,
env->SetMethodNoSideEffect(target, "certVerifySpkac", VerifySpkac);
env->SetMethodNoSideEffect(target, "certExportPublicKey", ExportPublicKey);
env->SetMethodNoSideEffect(target, "certExportChallenge", ExportChallenge);
env->SetMethodNoSideEffect(target, "getRootCertificates",
GetRootCertificates);
// Exposed for testing purposes only.
env->SetMethodNoSideEffect(target, "isExtraRootCertsFileLoaded",
IsExtraRootCertsFileLoaded);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
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-----');
}));

View File

@ -265,7 +265,7 @@ while (<TXT>) {
$encoded =~ s/(.{1,${opt_w}})/"$1\\n"\n/g;
my $pem = "\"-----BEGIN CERTIFICATE-----\\n\"\n"
. $encoded
. "\"-----END CERTIFICATE-----\\n\",\n";
. "\"-----END CERTIFICATE-----\",\n";
print CRT "\n/* $caname */\n";
my $maxStringLength = length($caname);