Skip to content

Commit

Permalink
crypto: added support for reading system store certificates in windows
Browse files Browse the repository at this point in the history
  • Loading branch information
twitharshil committed Oct 6, 2022
1 parent 50a4131 commit f57a813
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 8 deletions.
104 changes: 96 additions & 8 deletions src/crypto/crypto_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
#include <openssl/engine.h>
#endif // !OPENSSL_NO_ENGINE

#ifdef _WIN32
#include <Windows.h>
#include <wincrypt.h>

#include "base64-inl.h"
#endif

namespace node {

using v8::Array;
Expand Down Expand Up @@ -190,18 +197,88 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,

} // namespace

void ReadSystemStoreCertificates(
std::vector<std::string>* system_root_certificates) {
const HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
CHECK_NE(hStore, NULLPTR);

auto cleanup =
OnScopeLeave([hStore]() { CHECK_EQ(CertCloseStore(hStore, 0), TRUE); });

PCCERT_CONTEXT pCtx = nullptr;

while ((pCtx = CertEnumCertificatesInStore(hStore, pCtx)) != nullptr) {
const DWORD cbSize = CertGetNameStringW(
pCtx, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0);

CHECK_GT(cbSize, 0);

std::vector<wchar_t> pszName(cbSize);

CHECK_GT(CertGetNameStringW(pCtx,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
nullptr,
pszName.data(),
cbSize),
0);

const char* certificate_src_ptr =
reinterpret_cast<const char*>(pCtx->pbCertEncoded);
const size_t slen = pCtx->cbCertEncoded;
const size_t dlen = base64_encoded_size(slen);

char* certificate_dst_ptr = UncheckedMalloc(dlen);

CHECK_NOT_NULL(certificate_dst_ptr);

auto cleanup =
OnScopeLeave([certificate_dst_ptr]() { free(certificate_dst_ptr); });

const size_t written =
base64_encode(certificate_src_ptr, slen, certificate_dst_ptr, dlen);
CHECK_EQ(written, dlen);

std::string base64_string_output(certificate_dst_ptr, dlen);

constexpr size_t distance = 72;
size_t pos = distance;

while (pos < base64_string_output.size()) {
base64_string_output.insert(pos, "\n");
pos += distance + 1;
}

base64_string_output = "-----BEGIN CERTIFICATE-----\n" +
base64_string_output + "\n-----END CERTIFICATE-----";

system_root_certificates->emplace_back(std::move(base64_string_output));
}
}

X509_STORE* NewRootCertStore() {
static std::vector<X509*> root_certs_vector;
static Mutex root_certs_vector_mutex;
Mutex::ScopedLock lock(root_certs_vector_mutex);

if (root_certs_vector.empty() &&
per_process::cli_options->ssl_openssl_cert_store == false) {
std::vector<std::string> combined_root_certs;

for (size_t i = 0; i < arraysize(root_certs); i++) {
combined_root_certs.emplace_back(root_certs[i]);
}

if (per_process::cli_options->node_use_system_ca) {
ReadSystemStoreCertificates(&combined_root_certs);
}

for (size_t i = 0; i < combined_root_certs.size(); i++) {
X509* x509 =
PEM_read_bio_X509(NodeBIO::NewFixed(root_certs[i],
strlen(root_certs[i])).get(),
nullptr, // no re-use of X509 structure
PEM_read_bio_X509(NodeBIO::NewFixed(combined_root_certs[i].c_str(),
combined_root_certs[i].length())
.get(),
nullptr, // no re-use of X509 structure
NoPasswordCallback,
nullptr); // no callback data

Expand Down Expand Up @@ -234,19 +311,30 @@ X509_STORE* NewRootCertStore() {

void GetRootCertificates(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Value> result[arraysize(root_certs)];

std::vector<std::string> combined_root_certs;

for (size_t i = 0; i < arraysize(root_certs); i++) {
combined_root_certs.emplace_back(root_certs[i]);
}

if (per_process::cli_options->node_use_system_ca) {
ReadSystemStoreCertificates(&combined_root_certs);
}

std::vector<Local<Value>> result(combined_root_certs.size());

for (size_t i = 0; i < combined_root_certs.size(); i++) {
if (!String::NewFromOneByte(
env->isolate(),
reinterpret_cast<const uint8_t*>(root_certs[i]))
.ToLocal(&result[i])) {
env->isolate(),
reinterpret_cast<const uint8_t*>(combined_root_certs[i].c_str()))
.ToLocal(&result[i])) {
return;
}
}

args.GetReturnValue().Set(
Array::New(env->isolate(), result, arraysize(root_certs)));
Array::New(env->isolate(), result.data(), combined_root_certs.size()));
}

bool SecureContext::HasInstance(Environment* env, const Local<Value>& value) {
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,10 @@ PerProcessOptionsParser::PerProcessOptionsParser(
"use an alternative default TLS cipher list",
&PerProcessOptions::tls_cipher_list,
kAllowedInEnvironment);
AddOption("--node-use-system-ca",
"use system's store CA",
&PerProcessOptions::node_use_system_ca,
kAllowedInEnvironment);
AddOption("--use-openssl-ca",
"use OpenSSL's default CA store"
#if defined(NODE_OPENSSL_CERT_STORE)
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ class PerProcessOptions : public Options {
#else
bool ssl_openssl_cert_store = false;
#endif
bool node_use_system_ca = false;
bool use_openssl_ca = false;
bool use_bundled_ca = false;
bool enable_fips_crypto = false;
Expand Down

0 comments on commit f57a813

Please sign in to comment.