Skip to content

Commit 6406dbb

Browse files
indutnyFishrock123
authored andcommitted
crypto: load PFX chain the same way as regular one
Load the certificate chain from the PFX file the same as we do it for a regular certificate chain. Fix: nodejs#4127 PR-URL: nodejs#4165 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent e808658 commit 6406dbb

File tree

4 files changed

+156
-55
lines changed

4 files changed

+156
-55
lines changed

src/node_crypto.cc

+120-50
Original file line numberDiff line numberDiff line change
@@ -517,46 +517,35 @@ int SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer) {
517517
}
518518

519519

520-
// Read a file that contains our certificate in "PEM" format,
521-
// possibly followed by a sequence of CA certificates that should be
522-
// sent to the peer in the Certificate message.
523-
//
524-
// Taken from OpenSSL - editted for style.
525520
int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
526-
BIO* in,
521+
X509* x,
522+
STACK_OF(X509)* extra_certs,
527523
X509** cert,
528524
X509** issuer) {
529-
int ret = 0;
530-
X509* x = nullptr;
525+
CHECK_EQ(*issuer, nullptr);
526+
CHECK_EQ(*cert, nullptr);
531527

532-
x = PEM_read_bio_X509_AUX(in, nullptr, CryptoPemCallback, nullptr);
533-
534-
if (x == nullptr) {
535-
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
536-
goto end;
537-
}
538-
539-
ret = SSL_CTX_use_certificate(ctx, x);
528+
int ret = SSL_CTX_use_certificate(ctx, x);
540529

541530
if (ret) {
542531
// If we could set up our certificate, now proceed to
543532
// the CA certificates.
544-
X509 *ca;
545533
int r;
546-
unsigned long err;
547534

548535
if (ctx->extra_certs != nullptr) {
549536
sk_X509_pop_free(ctx->extra_certs, X509_free);
550537
ctx->extra_certs = nullptr;
551538
}
552539

553-
while ((ca = PEM_read_bio_X509(in, nullptr, CryptoPemCallback, nullptr))) {
540+
for (int i = 0; i < sk_X509_num(extra_certs); i++) {
541+
X509* ca = sk_X509_value(extra_certs, i);
542+
554543
// NOTE: Increments reference count on `ca`
555544
r = SSL_CTX_add1_chain_cert(ctx, ca);
556545

557546
if (!r) {
558-
X509_free(ca);
559547
ret = 0;
548+
*issuer = nullptr;
560549
goto end;
561550
}
562551
// Note that we must not free r if it was successfully
@@ -567,17 +556,8 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
567556
// Find issuer
568557
if (*issuer != nullptr || X509_check_issued(ca, x) != X509_V_OK)
569558
continue;
570-
*issuer = ca;
571-
}
572559

573-
// When the while loop ends, it's usually just EOF.
574-
err = ERR_peek_last_error();
575-
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
576-
ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
577-
ERR_clear_error();
578-
} else {
579-
// some real error
580-
ret = 0;
560+
*issuer = ca;
581561
}
582562
}
583563

@@ -590,13 +570,88 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
590570
// no need to free `store`
591571
} else {
592572
// Increment issuer reference count
593-
CRYPTO_add(&(*issuer)->references, 1, CRYPTO_LOCK_X509);
573+
*issuer = X509_dup(*issuer);
574+
if (*issuer == nullptr) {
575+
ret = 0;
576+
goto end;
577+
}
594578
}
595579
}
596580

597581
end:
582+
if (ret && x != nullptr) {
583+
*cert = X509_dup(x);
584+
if (*cert == nullptr)
585+
ret = 0;
586+
}
587+
return ret;
588+
}
589+
590+
591+
// Read a file that contains our certificate in "PEM" format,
592+
// possibly followed by a sequence of CA certificates that should be
593+
// sent to the peer in the Certificate message.
594+
//
595+
// Taken from OpenSSL - edited for style.
596+
int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
597+
BIO* in,
598+
X509** cert,
599+
X509** issuer) {
600+
X509* x = nullptr;
601+
602+
// Just to ensure that `ERR_peek_last_error` below will return only errors
603+
// that we are interested in
604+
ERR_clear_error();
605+
606+
x = PEM_read_bio_X509_AUX(in, nullptr, CryptoPemCallback, nullptr);
607+
608+
if (x == nullptr) {
609+
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
610+
return 0;
611+
}
612+
613+
X509* extra = nullptr;
614+
int ret = 0;
615+
unsigned long err = 0;
616+
617+
// Read extra certs
618+
STACK_OF(X509)* extra_certs = sk_X509_new_null();
619+
if (extra_certs == nullptr) {
620+
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_MALLOC_FAILURE);
621+
goto done;
622+
}
623+
624+
while ((extra = PEM_read_bio_X509(in, nullptr, CryptoPemCallback, nullptr))) {
625+
if (sk_X509_push(extra_certs, extra))
626+
continue;
627+
628+
// Failure, free all certs
629+
goto done;
630+
}
631+
extra = nullptr;
632+
633+
// When the while loop ends, it's usually just EOF.
634+
err = ERR_peek_last_error();
635+
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
636+
ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
637+
ERR_clear_error();
638+
} else {
639+
// some real error
640+
goto done;
641+
}
642+
643+
ret = SSL_CTX_use_certificate_chain(ctx, x, extra_certs, cert, issuer);
644+
if (!ret)
645+
goto done;
646+
647+
done:
648+
if (extra_certs != nullptr)
649+
sk_X509_pop_free(extra_certs, X509_free);
650+
if (extra != nullptr)
651+
X509_free(extra);
598652
if (x != nullptr)
599-
*cert = x;
653+
X509_free(x);
654+
600655
return ret;
601656
}
602657

@@ -614,6 +669,16 @@ void SecureContext::SetCert(const FunctionCallbackInfo<Value>& args) {
614669
if (!bio)
615670
return;
616671

672+
// Free previous certs
673+
if (sc->issuer_ != nullptr) {
674+
X509_free(sc->issuer_);
675+
sc->issuer_ = nullptr;
676+
}
677+
if (sc->cert_ != nullptr) {
678+
X509_free(sc->cert_);
679+
sc->cert_ = nullptr;
680+
}
681+
617682
int rv = SSL_CTX_use_certificate_chain(sc->ctx_,
618683
bio,
619684
&sc->cert_,
@@ -888,7 +953,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
888953
PKCS12* p12 = nullptr;
889954
EVP_PKEY* pkey = nullptr;
890955
X509* cert = nullptr;
891-
STACK_OF(X509)* extraCerts = nullptr;
956+
STACK_OF(X509)* extra_certs = nullptr;
892957
char* pass = nullptr;
893958
bool ret = false;
894959

@@ -913,28 +978,33 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
913978
pass[passlen] = '\0';
914979
}
915980

981+
// Free previous certs
982+
if (sc->issuer_ != nullptr) {
983+
X509_free(sc->issuer_);
984+
sc->issuer_ = nullptr;
985+
}
986+
if (sc->cert_ != nullptr) {
987+
X509_free(sc->cert_);
988+
sc->cert_ = nullptr;
989+
}
990+
916991
if (d2i_PKCS12_bio(in, &p12) &&
917-
PKCS12_parse(p12, pass, &pkey, &cert, &extraCerts) &&
918-
SSL_CTX_use_certificate(sc->ctx_, cert) &&
992+
PKCS12_parse(p12, pass, &pkey, &cert, &extra_certs) &&
993+
SSL_CTX_use_certificate_chain(sc->ctx_,
994+
cert,
995+
extra_certs,
996+
&sc->cert_,
997+
&sc->issuer_) &&
919998
SSL_CTX_use_PrivateKey(sc->ctx_, pkey)) {
920-
// set extra certs
921-
while (X509* x509 = sk_X509_pop(extraCerts)) {
922-
if (!sc->ca_store_) {
923-
sc->ca_store_ = X509_STORE_new();
924-
SSL_CTX_set_cert_store(sc->ctx_, sc->ca_store_);
925-
}
926-
927-
X509_STORE_add_cert(sc->ca_store_, x509);
928-
SSL_CTX_add_client_CA(sc->ctx_, x509);
929-
X509_free(x509);
930-
}
999+
ret = true;
1000+
}
9311001

1002+
if (pkey != nullptr)
9321003
EVP_PKEY_free(pkey);
1004+
if (cert != nullptr)
9331005
X509_free(cert);
934-
sk_X509_free(extraCerts);
935-
936-
ret = true;
937-
}
1006+
if (extra_certs != nullptr)
1007+
sk_X509_free(extra_certs);
9381008

9391009
PKCS12_free(p12);
9401010
BIO_free_all(in);

test/fixtures/keys/Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ agent1-cert.pem: agent1-csr.pem ca1-cert.pem ca1-key.pem
7979
-CAcreateserial \
8080
-out agent1-cert.pem
8181

82+
agent1-pfx.pem: agent1-cert.pem agent1-key.pem ca1-cert.pem
83+
openssl pkcs12 -export \
84+
-in agent1-cert.pem \
85+
-inkey agent1-key.pem \
86+
-certfile ca1-cert.pem \
87+
-out agent1-pfx.pem \
88+
-password pass:sample
89+
8290
agent1-verify: agent1-cert.pem ca1-cert.pem
8391
openssl verify -CAfile ca1-cert.pem agent1-cert.pem
8492

test/fixtures/keys/agent1-pfx.pem

2.38 KB
Binary file not shown.

test/parallel/test-tls-ocsp-callback.js

+28-5
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,7 @@ var constants = require('constants');
2222
var fs = require('fs');
2323
var join = require('path').join;
2424

25-
test({ response: false }, function() {
26-
test({ response: 'hello world' }, function() {
27-
test({ ocsp: false });
28-
});
29-
});
25+
var pfx = fs.readFileSync(join(common.fixturesDir, 'keys', 'agent1-pfx.pem'));
3026

3127
function test(testOptions, cb) {
3228

@@ -47,6 +43,13 @@ function test(testOptions, cb) {
4743
var ocspResponse;
4844
var session;
4945

46+
if (testOptions.pfx) {
47+
delete options.key;
48+
delete options.cert;
49+
options.pfx = testOptions.pfx;
50+
options.passphrase = testOptions.passphrase;
51+
}
52+
5053
var server = tls.createServer(options, function(cleartext) {
5154
cleartext.on('error', function(er) {
5255
// We're ok with getting ECONNRESET in this test, but it's
@@ -106,3 +109,23 @@ function test(testOptions, cb) {
106109
assert.equal(ocspCount, 1);
107110
});
108111
}
112+
113+
var tests = [
114+
{ response: false },
115+
{ response: 'hello world' },
116+
{ ocsp: false }
117+
];
118+
119+
if (!common.hasFipsCrypto) {
120+
tests.push({ pfx: pfx, passphrase: 'sample', response: 'hello pfx' });
121+
}
122+
123+
function runTests(i) {
124+
if (i === tests.length) return;
125+
126+
test(tests[i], common.mustCall(function() {
127+
runTests(i + 1);
128+
}));
129+
}
130+
131+
runTests(0);

0 commit comments

Comments
 (0)