Skip to content

Commit

Permalink
tls: expose built-in root certificates
Browse files Browse the repository at this point in the history
Fixes: nodejs#25824
PR-URL: nodejs#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>
  • Loading branch information
bnoordhuis committed May 20, 2019
1 parent cc7e15f commit f1a3968
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 143 deletions.
13 changes: 13 additions & 0 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 20 additions & 2 deletions lib/tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

'use strict';

const { Object } = primordials;

const {
ERR_TLS_CERT_ALTNAME_INVALID,
ERR_OUT_OF_RANGE
Expand All @@ -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');
Expand Down Expand Up @@ -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) {
Expand Down
20 changes: 20 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit f1a3968

Please sign in to comment.