tls: implement tls.getCACertificates() · nodejs/node@75f11ae · GitHub
Skip to content

Commit 75f11ae

Browse files
joyeecheungaduh95
authored andcommitted
tls: implement tls.getCACertificates()
To accompany --use-system-ca, this adds a new API that allows querying various kinds of CA certificates. - If the first argument `type` is `"default"` or undefined, it returns the CA certificates that will be used by Node.js TLS clients by default, which includes the Mozilla CA if --use-bundled-ca is enabled or --use-openssl-ca is not enabled, and the system certificates if --use-system-ca is enabled, and the extra certificates if NODE_EXTRA_CA_CERTS is used. - If `type` is `"system"` this returns the system certificates, regardless of whether --use-system-ca is enabeld or not. - If `type` is `"bundled"` this is the same as `tls.rootCertificates` and returns the Mozilla CA certificates. - If `type` is `"extra"` this returns the certificates parsed from the path specified by NODE_EXTRA_CA_CERTS. Drive-by: remove the inaccurate description in `tls.rootCertificates` about including system certificates, since it in fact does not include them, and also it is contradicting the previous description about `tls.rootCertificates` always returning the Mozilla CA store and staying the same across platforms. PR-URL: #57107 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 4d86a42 commit 75f11ae

16 files changed

Lines changed: 468 additions & 26 deletions

doc/api/tls.md

Lines changed: 49 additions & 6 deletions

lib/tls.js

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
const {
2525
Array,
2626
ArrayIsArray,
27+
// eslint-disable-next-line no-restricted-syntax
28+
ArrayPrototypePush,
2729
JSONParse,
2830
ObjectDefineProperty,
2931
ObjectFreeze,
@@ -34,6 +36,7 @@ const {
3436
ERR_TLS_CERT_ALTNAME_FORMAT,
3537
ERR_TLS_CERT_ALTNAME_INVALID,
3638
ERR_OUT_OF_RANGE,
39+
ERR_INVALID_ARG_VALUE,
3740
} = require('internal/errors').codes;
3841
const internalUtil = require('internal/util');
3942
internalUtil.assertCrypto();
@@ -44,12 +47,18 @@ const {
4447

4548
const net = require('net');
4649
const { getOptionValue } = require('internal/options');
47-
const { getRootCertificates, getSSLCiphers } = internalBinding('crypto');
50+
const {
51+
getBundledRootCertificates,
52+
getExtraCACertificates,
53+
getSystemCACertificates,
54+
getSSLCiphers,
55+
} = internalBinding('crypto');
4856
const { Buffer } = require('buffer');
4957
const { canonicalizeIP } = internalBinding('cares_wrap');
5058
const _tls_common = require('_tls_common');
5159
const _tls_wrap = require('_tls_wrap');
5260
const { createSecurePair } = require('internal/tls/secure-pair');
61+
const { validateString } = require('internal/validators');
5362

5463
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
5564
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
@@ -85,23 +94,84 @@ exports.getCiphers = internalUtil.cachedResult(
8594
() => internalUtil.filterDuplicateStrings(getSSLCiphers(), true),
8695
);
8796

88-
let rootCertificates;
97+
let bundledRootCertificates;
98+
function cacheBundledRootCertificates() {
99+
bundledRootCertificates ||= ObjectFreeze(getBundledRootCertificates());
89100

90-
function cacheRootCertificates() {
91-
rootCertificates = ObjectFreeze(getRootCertificates());
101+
return bundledRootCertificates;
92102
}
93103

94104
ObjectDefineProperty(exports, 'rootCertificates', {
95105
__proto__: null,
96106
configurable: false,
97107
enumerable: true,
98-
get: () => {
99-
// Out-of-line caching to promote inlining the getter.
100-
if (!rootCertificates) cacheRootCertificates();
101-
return rootCertificates;
102-
},
108+
get: cacheBundledRootCertificates,
103109
});
104110

111+
let extraCACertificates;
112+
function cacheExtraCACertificates() {
113+
extraCACertificates ||= ObjectFreeze(getExtraCACertificates());
114+
115+
return extraCACertificates;
116+
}
117+
118+
let systemCACertificates;
119+
function cacheSystemCACertificates() {
120+
systemCACertificates ||= ObjectFreeze(getSystemCACertificates());
121+
122+
return systemCACertificates;
123+
}
124+
125+
let defaultCACertificates;
126+
function cacheDefaultCACertificates() {
127+
if (defaultCACertificates) { return defaultCACertificates; }
128+
defaultCACertificates = [];
129+
130+
if (!getOptionValue('--use-openssl-ca')) {
131+
const bundled = cacheBundledRootCertificates();
132+
for (let i = 0; i < bundled.length; ++i) {
133+
ArrayPrototypePush(defaultCACertificates, bundled[i]);
134+
}
135+
if (getOptionValue('--use-system-ca')) {
136+
const system = cacheSystemCACertificates();
137+
for (let i = 0; i < system.length; ++i) {
138+
139+
ArrayPrototypePush(defaultCACertificates, system[i]);
140+
}
141+
}
142+
}
143+
144+
if (process.env.NODE_EXTRA_CA_CERTS) {
145+
const extra = cacheExtraCACertificates();
146+
for (let i = 0; i < extra.length; ++i) {
147+
148+
ArrayPrototypePush(defaultCACertificates, extra[i]);
149+
}
150+
}
151+
152+
ObjectFreeze(defaultCACertificates);
153+
return defaultCACertificates;
154+
}
155+
156+
// TODO(joyeecheung): support X509Certificate output?
157+
function getCACertificates(type = 'default') {
158+
validateString(type, 'type');
159+
160+
switch (type) {
161+
case 'default':
162+
return cacheDefaultCACertificates();
163+
case 'bundled':
164+
return cacheBundledRootCertificates();
165+
case 'system':
166+
return cacheSystemCACertificates();
167+
case 'extra':
168+
return cacheExtraCACertificates();
169+
default:
170+
throw new ERR_INVALID_ARG_VALUE('type', type);
171+
}
172+
}
173+
exports.getCACertificates = getCACertificates;
174+
105175
// Convert protocols array into valid OpenSSL protocols list
106176
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
107177
function convertProtocols(protocols) {

src/crypto/crypto_context.cc

Lines changed: 72 additions & 9 deletions

0 commit comments

Comments
 (0)