Skip to content

Commit 51c1ec6

Browse files
committed
tls: add 'as' option to getCACertificates() for X509Certificate output
1 parent f7c2a7e commit 51c1ec6

3 files changed

Lines changed: 59 additions & 19 deletions

File tree

doc/api/tls.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,21 +2308,25 @@ const additionalCerts = ['-----BEGIN CERTIFICATE-----\n...'];
23082308
tls.setDefaultCACertificates([...currentCerts, ...additionalCerts]);
23092309
```
23102310

2311-
## `tls.getCACertificates([type])`
2311+
## `tls.getCACertificates([options])`
23122312

23132313
<!-- YAML
23142314
added:
23152315
- v23.10.0
23162316
- v22.15.0
23172317
-->
23182318

2319-
* `type` {string|undefined} The type of CA certificates that will be returned. Valid values
2320-
are `"default"`, `"system"`, `"bundled"` and `"extra"`.
2321-
**Default:** `"default"`.
2322-
* Returns: {string\[]} An array of PEM-encoded certificates. The array may contain duplicates
2323-
if the same certificate is repeatedly stored in multiple sources.
2319+
* `options` {string|Object|undefined}\
2320+
Optional. If a string, it is treated as the `type` of certificates to return.\
2321+
If an object, it may contain:
2322+
* `type` {string} The type of CA certificates to return. One of `"default"`, `"system"`, `"bundled"`, or `"extra"`.\
2323+
**Default:** `"default"`.
2324+
* `as` {string} The format of returned certificates. One of:
2325+
* `"buffer"` (default): Returns an array of certificate data as `Buffer` objects.
2326+
* `"x509"`: Returns an array of \[`X509Certificate`]\[] instances.
23242327

2325-
Returns an array containing the CA certificates from various sources, depending on `type`:
2328+
* Returns: {Array.\<Buffer|X509Certificate>}\
2329+
An array of certificates in the specified format.
23262330

23272331
* `"default"`: return the CA certificates that will be used by the Node.js TLS clients by default.
23282332
* When [`--use-bundled-ca`][] is enabled (default), or [`--use-openssl-ca`][] is not enabled,
@@ -2331,11 +2335,14 @@ Returns an array containing the CA certificates from various sources, depending
23312335
trusted store.
23322336
* When [`NODE_EXTRA_CA_CERTS`][] is used, this would also include certificates loaded from the specified
23332337
file.
2338+
23342339
* `"system"`: return the CA certificates that are loaded from the system's trusted store, according
23352340
to rules set by [`--use-system-ca`][]. This can be used to get the certificates from the system
23362341
when [`--use-system-ca`][] is not enabled.
2342+
23372343
* `"bundled"`: return the CA certificates from the bundled Mozilla CA store. This would be the same
23382344
as [`tls.rootCertificates`][].
2345+
23392346
* `"extra"`: return the CA certificates loaded from [`NODE_EXTRA_CA_CERTS`][]. It's an empty array if
23402347
[`NODE_EXTRA_CA_CERTS`][] is not set.
23412348

lib/tls.js

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const { canonicalizeIP } = internalBinding('cares_wrap');
6161
const tlsCommon = require('internal/tls/common');
6262
const tlsWrap = require('internal/tls/wrap');
6363
const { validateString } = require('internal/validators');
64+
const { X509Certificate } = require('crypto');
6465

6566
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
6667
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
@@ -164,23 +165,32 @@ function cacheDefaultCACertificates() {
164165
return defaultCACertificates;
165166
}
166167

167-
// TODO(joyeecheung): support X509Certificate output?
168-
function getCACertificates(type = 'default') {
168+
function getCACertificates(options = {}) {
169+
if (typeof options === 'string') {
170+
options = { type: options };
171+
} else if (typeof options !== 'object' || options === null) {
172+
throw new ERR_INVALID_ARG_TYPE('options', ['string', 'object'], options);
173+
}
174+
175+
const { type = 'default', as = 'buffer' } = options;
169176
validateString(type, 'type');
170177

178+
let certs;
171179
switch (type) {
172-
case 'default':
173-
return cacheDefaultCACertificates();
174-
case 'bundled':
175-
return cacheBundledRootCertificates();
176-
case 'system':
177-
return cacheSystemCACertificates();
178-
case 'extra':
179-
return cacheExtraCACertificates();
180-
default:
181-
throw new ERR_INVALID_ARG_VALUE('type', type);
180+
case 'default': certs = cacheDefaultCACertificates(); break;
181+
case 'bundled': certs = cacheBundledRootCertificates(); break;
182+
case 'system': certs = cacheSystemCACertificates(); break;
183+
case 'extra': certs = cacheExtraCACertificates(); break;
184+
default: throw new ERR_INVALID_ARG_VALUE('type', type);
185+
}
186+
187+
if (as === 'x509') {
188+
return certs.map((cert) => new X509Certificate(cert));
182189
}
190+
191+
return certs;
183192
}
193+
184194
exports.getCACertificates = getCACertificates;
185195

186196
function setDefaultCACertificates(certs) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const tls = require('tls');
8+
const { X509Certificate } = require('crypto');
9+
10+
function testX509Option() {
11+
const certs = tls.getCACertificates({ type: 'default', as: 'x509' });
12+
13+
assert.ok(Array.isArray(certs), 'should return an array');
14+
assert.ok(certs.length > 0, 'should return non-empty array');
15+
assert.ok(certs[0] instanceof X509Certificate,
16+
'each cert should be instance of X509Certificate');
17+
18+
assert.match(certs[0].serialNumber, /^[0-9A-F]+$/i, 'serialNumber should be hex string');
19+
}
20+
21+
testX509Option();
22+
23+
console.log('Test passed: getCACertificates with as: "x509"');

0 commit comments

Comments
 (0)