Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
forked fromnodejs/node

Commita790901

Browse files
authored
tls: implement tls.getCACertificates()
To accompany --use-system-ca, this adds a new API that allowsquerying 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 includethem, and also it is contradicting the previous description about`tls.rootCertificates` always returning the Mozilla CA store andstaying the same across platforms.PR-URL:nodejs#57107Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent3483937 commita790901

16 files changed

+468
-26
lines changed

‎doc/api/tls.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,9 +1985,13 @@ changes:
19851985
*`allowPartialTrustChain` {boolean} Treat intermediate (non-self-signed)
19861986
certificates in the trust CA certificate list as trusted.
19871987
*`ca` {string|string\[]|Buffer|Buffer\[]} Optionally override the trusted CA
1988-
certificates. Default is to trust the well-known CAs curated by Mozilla.
1989-
Mozilla's CAs are completely replaced when CAs are explicitly specified
1990-
using this option. The value can be a string or`Buffer`, or an`Array` of
1988+
certificates. If not specified, the CA certificates trusted by default are
1989+
the same as the ones returned by[`tls.getCACertificates()`][] using the
1990+
`default` type. If specified, the default list would be completely replaced
1991+
(instead of being concatenated) by the certificates in the`ca` option.
1992+
Users need to concatenate manually if they wish to add additional certificates
1993+
instead of completely overriding the default.
1994+
The value can be a string or`Buffer`, or an`Array` of
19911995
strings and/or`Buffer`s. Any string or`Buffer` can contain multiple PEM
19921996
CAs concatenated together. The peer's certificate must be chainable to a CA
19931997
trusted by the server for the connection to be authenticated. When using
@@ -2001,7 +2005,6 @@ changes:
20012005
provided.
20022006
For PEM encoded certificates, supported types are "TRUSTED CERTIFICATE",
20032007
"X509 CERTIFICATE", and "CERTIFICATE".
2004-
See also[`tls.rootCertificates`][].
20052008
*`cert` {string|string\[]|Buffer|Buffer\[]} Cert chains in PEM format. One
20062009
cert chain should be provided per private key. Each cert chain should
20072010
consist of the PEM formatted certificate for a provided private`key`,
@@ -2364,6 +2367,39 @@ openssl pkcs12 -certpbe AES-256-CBC -export -out client-cert.pem \
23642367
The server can be tested by connecting to it using the example client from
23652368
[`tls.connect()`][].
23662369

2370+
##`tls.getCACertificates([type])`
2371+
2372+
<!-- YAML
2373+
added: REPLACEME
2374+
-->
2375+
2376+
*`type` {string|undefined} The type of CA certificates that will be returned. Valid values
2377+
are`"default"`,`"system"`,`"bundled"` and`"extra"`.
2378+
**Default:**`"default"`.
2379+
* Returns: {string\[]} An array of PEM-encoded certificates. The array may contain duplicates
2380+
if the same certificate is repeatedly stored in multiple sources.
2381+
2382+
Returns an array containing the CA certificates from various sources, depending on`type`:
2383+
2384+
*`"default"`: return the CA certificates that will be used by the Node.js TLS clients by default.
2385+
* When[`--use-bundled-ca`][] is enabled (default), or[`--use-openssl-ca`][] is not enabled,
2386+
this would include CA certificates from the bundled Mozilla CA store.
2387+
* When[`--use-system-ca`][] is enabled, this would also include certificates from the system's
2388+
trusted store.
2389+
* When[`NODE_EXTRA_CA_CERTS`][] is used, this would also include certificates loaded from the specified
2390+
file.
2391+
*`"system"`: return the CA certificates that are loaded from the system's trusted store, according
2392+
to rules set by[`--use-system-ca`][]. This can be used to get the certificates from the system
2393+
when[`--use-system-ca`][] is not enabled.
2394+
*`"bundled"`: return the CA certificates from the bundled Mozilla CA store. This would be the same
2395+
as[`tls.rootCertificates`][].
2396+
*`"extra"`: return the CA certificates loaded from[`NODE_EXTRA_CA_CERTS`][]. It's an empty array if
2397+
[`NODE_EXTRA_CA_CERTS`][] is not set.
2398+
2399+
<!-- YAML
2400+
added: v0.10.2
2401+
-->
2402+
23672403
##`tls.getCiphers()`
23682404

23692405
<!-- YAML
@@ -2400,8 +2436,10 @@ from the bundled Mozilla CA store as supplied by the current Node.js version.
24002436
The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store
24012437
that is fixed at release time. It is identical on all supported platforms.
24022438

2403-
On macOS if`--use-system-ca` is passed then trusted certificates
2404-
from the user and system keychains are also included.
2439+
To get the actual CA certificates used by the current Node.js instance, which
2440+
may include certificates loaded from the system store (if`--use-system-ca` is used)
2441+
or loaded from a file indicated by`NODE_EXTRA_CA_CERTS`, use
2442+
[`tls.getCACertificates()`][].
24052443

24062444
##`tls.DEFAULT_ECDH_CURVE`
24072445

@@ -2487,7 +2525,11 @@ added:
24872525
[`'secureConnection'`]:#event-secureconnection
24882526
[`'session'`]:#event-session
24892527
[`--tls-cipher-list`]:cli.md#--tls-cipher-listlist
2528+
[`--use-bundled-ca`]:cli.md#--use-bundled-ca---use-openssl-ca
2529+
[`--use-openssl-ca`]:cli.md#--use-bundled-ca---use-openssl-ca
2530+
[`--use-system-ca`]:cli.md#--use-system-ca
24902531
[`Duplex`]:stream.md#class-streamduplex
2532+
[`NODE_EXTRA_CA_CERTS`]:cli.md#node_extra_ca_certsfile
24912533
[`NODE_OPTIONS`]:cli.md#node_optionsoptions
24922534
[`SSL_export_keying_material`]:https://www.openssl.org/docs/man1.1.1/man3/SSL_export_keying_material.html
24932535
[`SSL_get_version`]:https://www.openssl.org/docs/man1.1.1/man3/SSL_get_version.html
@@ -2516,6 +2558,7 @@ added:
25162558
[`tls.createSecureContext()`]:#tlscreatesecurecontextoptions
25172559
[`tls.createSecurePair()`]:#tlscreatesecurepaircontext-isserver-requestcert-rejectunauthorized-options
25182560
[`tls.createServer()`]:#tlscreateserveroptions-secureconnectionlistener
2561+
[`tls.getCACertificates()`]:#tlsgetcacertificatestype
25192562
[`tls.getCiphers()`]:#tlsgetciphers
25202563
[`tls.rootCertificates`]:#tlsrootcertificates
25212564
[`x509.checkHost()`]:crypto.md#x509checkhostname-options

‎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
constinternalUtil=require('internal/util');
3942
internalUtil.assertCrypto();
@@ -44,12 +47,18 @@ const {
4447

4548
constnet=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-
letrootCertificates;
97+
letbundledRootCertificates;
98+
functioncacheBundledRootCertificates(){
99+
bundledRootCertificates||=ObjectFreeze(getBundledRootCertificates());
89100

90-
functioncacheRootCertificates(){
91-
rootCertificates=ObjectFreeze(getRootCertificates());
101+
returnbundledRootCertificates;
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-
returnrootCertificates;
102-
},
108+
get:cacheBundledRootCertificates,
103109
});
104110

111+
letextraCACertificates;
112+
functioncacheExtraCACertificates(){
113+
extraCACertificates||=ObjectFreeze(getExtraCACertificates());
114+
115+
returnextraCACertificates;
116+
}
117+
118+
letsystemCACertificates;
119+
functioncacheSystemCACertificates(){
120+
systemCACertificates||=ObjectFreeze(getSystemCACertificates());
121+
122+
returnsystemCACertificates;
123+
}
124+
125+
letdefaultCACertificates;
126+
functioncacheDefaultCACertificates(){
127+
if(defaultCACertificates){returndefaultCACertificates;}
128+
defaultCACertificates=[];
129+
130+
if(!getOptionValue('--use-openssl-ca')){
131+
constbundled=cacheBundledRootCertificates();
132+
for(leti=0;i<bundled.length;++i){
133+
ArrayPrototypePush(defaultCACertificates,bundled[i]);
134+
}
135+
if(getOptionValue('--use-system-ca')){
136+
constsystem=cacheSystemCACertificates();
137+
for(leti=0;i<system.length;++i){
138+
139+
ArrayPrototypePush(defaultCACertificates,system[i]);
140+
}
141+
}
142+
}
143+
144+
if(process.env.NODE_EXTRA_CA_CERTS){
145+
constextra=cacheExtraCACertificates();
146+
for(leti=0;i<extra.length;++i){
147+
148+
ArrayPrototypePush(defaultCACertificates,extra[i]);
149+
}
150+
}
151+
152+
ObjectFreeze(defaultCACertificates);
153+
returndefaultCACertificates;
154+
}
155+
156+
// TODO(joyeecheung): support X509Certificate output?
157+
functiongetCACertificates(type='default'){
158+
validateString(type,'type');
159+
160+
switch(type){
161+
case'default':
162+
returncacheDefaultCACertificates();
163+
case'bundled':
164+
returncacheBundledRootCertificates();
165+
case'system':
166+
returncacheSystemCACertificates();
167+
case'extra':
168+
returncacheExtraCACertificates();
169+
default:
170+
thrownewERR_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
functionconvertProtocols(protocols){

‎src/crypto/crypto_context.cc

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ using ncrypto::MarkPopErrorOnReturn;
4242
using ncrypto::SSLPointer;
4343
using ncrypto::StackOfX509;
4444
using ncrypto::X509Pointer;
45+
using ncrypto::X509View;
4546
using v8::Array;
4647
using v8::ArrayBufferView;
4748
using v8::Boolean;
4849
using v8::Context;
4950
using v8::DontDelete;
51+
using v8::EscapableHandleScope;
5052
using v8::Exception;
5153
using v8::External;
5254
using v8::FunctionCallbackInfo;
@@ -57,7 +59,9 @@ using v8::Integer;
5759
using v8::Isolate;
5860
using v8::JustVoid;
5961
using v8::Local;
62+
using v8::LocalVector;
6063
using v8::Maybe;
64+
using v8::MaybeLocal;
6165
using v8::Nothing;
6266
using v8::Object;
6367
using v8::PropertyAttribute;
@@ -672,9 +676,6 @@ static void LoadCertsFromDir(std::vector<X509*>* certs,
672676
return;
673677
}
674678

675-
uv_fs_t stats_req;
676-
auto cleanup_stats =
677-
OnScopeLeave([&stats_req]() {uv_fs_req_cleanup(&stats_req); });
678679
for (;;) {
679680
uv_dirent_t ent;
680681

@@ -691,12 +692,14 @@ static void LoadCertsFromDir(std::vector<X509*>* certs,
691692
return;
692693
}
693694

695+
uv_fs_t stats_req;
694696
std::string file_path =std::string(cert_dir) +"/" + ent.name;
695697
int stats_r =uv_fs_stat(nullptr, &stats_req, file_path.c_str(),nullptr);
696698
if (stats_r ==0 &&
697699
(static_cast<uv_stat_t*>(stats_req.ptr)->st_mode & S_IFREG)) {
698700
LoadCertsFromFile(certs, file_path.c_str());
699701
}
702+
uv_fs_req_cleanup(&stats_req);
700703
}
701704
}
702705

@@ -775,7 +778,7 @@ static std::vector<X509*> InitializeSystemStoreCertificates() {
775778
return system_store_certs;
776779
}
777780

778-
static std::vector<X509*>&GetSystemStoreRootCertificates() {
781+
static std::vector<X509*>&GetSystemStoreCACertificates() {
779782
// Use function-local static to guarantee thread safety.
780783
static std::vector<X509*> system_store_certs =
781784
InitializeSystemStoreCertificates();
@@ -847,7 +850,7 @@ X509_STORE* NewRootCertStore() {
847850
CHECK_EQ(1,X509_STORE_add_cert(store, cert));
848851
}
849852
if (per_process::cli_options->use_system_ca) {
850-
for (X509* cert :GetSystemStoreRootCertificates()) {
853+
for (X509* cert :GetSystemStoreCACertificates()) {
851854
CHECK_EQ(1,X509_STORE_add_cert(store, cert));
852855
}
853856
}
@@ -869,7 +872,7 @@ void CleanupCachedRootCertificates() {
869872
}
870873
}
871874
if (has_cached_system_root_certs.load()) {
872-
for (X509* cert :GetSystemStoreRootCertificates()) {
875+
for (X509* cert :GetSystemStoreCACertificates()) {
873876
X509_free(cert);
874877
}
875878
}
@@ -881,7 +884,7 @@ void CleanupCachedRootCertificates() {
881884
}
882885
}
883886

884-
voidGetRootCertificates(const FunctionCallbackInfo<Value>& args) {
887+
voidGetBundledRootCertificates(const FunctionCallbackInfo<Value>& args) {
885888
Environment* env =Environment::GetCurrent(args);
886889
Local<Value> result[arraysize(root_certs)];
887890

@@ -898,6 +901,58 @@ void GetRootCertificates(const FunctionCallbackInfo<Value>& args) {
898901
Array::New(env->isolate(), result,arraysize(root_certs)));
899902
}
900903

904+
MaybeLocal<Array>X509sToArrayOfStrings(Environment* env,
905+
const std::vector<X509*>& certs) {
906+
ClearErrorOnReturn clear_error_on_return;
907+
EscapableHandleScopescope(env->isolate());
908+
909+
LocalVector<Value>result(env->isolate(), certs.size());
910+
for (size_t i =0; i < certs.size(); ++i) {
911+
X509Viewview(certs[i]);
912+
auto pem_bio = view.toPEM();
913+
if (!pem_bio) {
914+
ThrowCryptoError(env,ERR_get_error(),"X509 to PEM conversion");
915+
return MaybeLocal<Array>();
916+
}
917+
918+
char* pem_data =nullptr;
919+
auto pem_size =BIO_get_mem_data(pem_bio.get(), &pem_data);
920+
if (pem_size <=0 || !pem_data) {
921+
ThrowCryptoError(env,ERR_get_error(),"Reading PEM data");
922+
return MaybeLocal<Array>();
923+
}
924+
// PEM is base64-encoded, so it must be one-byte.
925+
if (!String::NewFromOneByte(env->isolate(),
926+
reinterpret_cast<uint8_t*>(pem_data),
927+
v8::NewStringType::kNormal,
928+
pem_size)
929+
.ToLocal(&result[i])) {
930+
return MaybeLocal<Array>();
931+
}
932+
}
933+
return scope.Escape(Array::New(env->isolate(), result.data(), result.size()));
934+
}
935+
936+
voidGetSystemCACertificates(const FunctionCallbackInfo<Value>& args) {
937+
Environment* env =Environment::GetCurrent(args);
938+
Local<Array> results;
939+
if (X509sToArrayOfStrings(env,GetSystemStoreCACertificates())
940+
.ToLocal(&results)) {
941+
args.GetReturnValue().Set(results);
942+
}
943+
}
944+
945+
voidGetExtraCACertificates(const FunctionCallbackInfo<Value>& args) {
946+
Environment* env =Environment::GetCurrent(args);
947+
if (extra_root_certs_file.empty()) {
948+
return args.GetReturnValue().Set(Array::New(env->isolate()));
949+
}
950+
Local<Array> results;
951+
if (X509sToArrayOfStrings(env,GetExtraCACertificates()).ToLocal(&results)) {
952+
args.GetReturnValue().Set(results);
953+
}
954+
}
955+
901956
boolSecureContext::HasInstance(Environment* env,const Local<Value>& value) {
902957
returnGetConstructorTemplate(env)->HasInstance(value);
903958
}
@@ -981,8 +1036,14 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
9811036
GetConstructorTemplate(env),
9821037
SetConstructorFunctionFlag::NONE);
9831038

1039+
SetMethodNoSideEffect(context,
1040+
target,
1041+
"getBundledRootCertificates",
1042+
GetBundledRootCertificates);
1043+
SetMethodNoSideEffect(
1044+
context, target,"getSystemCACertificates", GetSystemCACertificates);
9841045
SetMethodNoSideEffect(
985-
context, target,"getRootCertificates",GetRootCertificates);
1046+
context, target,"getExtraCACertificates",GetExtraCACertificates);
9861047
}
9871048

9881049
voidSecureContext::RegisterExternalReferences(
@@ -1022,7 +1083,9 @@ void SecureContext::RegisterExternalReferences(
10221083

10231084
registry->Register(CtxGetter);
10241085

1025-
registry->Register(GetRootCertificates);
1086+
registry->Register(GetBundledRootCertificates);
1087+
registry->Register(GetSystemCACertificates);
1088+
registry->Register(GetExtraCACertificates);
10261089
}
10271090

10281091
SecureContext*SecureContext::Create(Environment* env) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp