From 0a9c6814a7e0e1d7c614ad172621a9dee4fb5753 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Sat, 18 Apr 2015 10:19:23 +0200 Subject: [PATCH] tls: use `SSL_set_cert_cb` for async SNI/OCSP Do not enable ClientHello parser for async SNI/OCSP. Use new OpenSSL-1.0.2's API `SSL_set_cert_cb` to pause the handshake process and load the cert/OCSP response asynchronously. Hopefuly this will make whole async SNI/OCSP process much faster and will eventually let us remove the ClientHello parser itself (which is currently used only for async session, see #1462 for the discussion of removing it). NOTE: Ported our code to `SSL_CTX_add1_chain_cert` to use `SSL_CTX_get0_chain_certs` in `CertCbDone`. Test provided for this feature. Fix: https://github.com/iojs/io.js/issues/1423 --- lib/_tls_wrap.js | 49 ++++--- src/env.h | 1 + src/node_crypto.cc | 140 +++++++++++++++++++- src/node_crypto.h | 29 +++- src/tls_wrap.cc | 19 +-- src/tls_wrap.h | 6 +- test/fixtures/keys/Makefile | 47 +++++++ test/fixtures/keys/agent1-cert.pem | 18 +-- test/fixtures/keys/ca1-cert.pem | 27 ++-- test/fixtures/keys/ca1-cert.srl | 2 +- test/fixtures/keys/ca1-key.pem | 30 ++--- test/fixtures/keys/ca1.cnf | 3 + test/parallel/test-tls-sni-server-client.js | 27 ++-- 13 files changed, 298 insertions(+), 100 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 3e091b0fc1be0d..4e6497137ae70c 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -141,29 +141,23 @@ function onclienthello(hello) { if (err) return self.destroy(err); - // Servername came from SSL session - // NOTE: TLS Session ticket doesn't include servername information - // - // Another note, From RFC3546: - // - // If, on the other hand, the older - // session is resumed, then the server MUST ignore extensions appearing - // in the client hello, and send a server hello containing no - // extensions; in this case the extension functionality negotiated - // during the original session initiation is applied to the resumed - // session. - // - // Therefore we should account session loading when dealing with servername - var servername = session && session.servername || hello.servername; - loadSNI(self, servername, function(err, ctx) { + self._handle.endParser(); + }); +} + + +function oncertcb(info) { + var self = this; + var servername = info.servername; + + loadSNI(self, servername, function(err, ctx) { + if (err) + return self.destroy(err); + requestOCSP(self, info, ctx, function(err) { if (err) return self.destroy(err); - requestOCSP(self, hello, ctx, function(err) { - if (err) - return self.destroy(err); - self._handle.endParser(); - }); + self._handle.certCbDone(); }); }); } @@ -325,15 +319,18 @@ TLSSocket.prototype._init = function(socket, wrap) { ssl.onhandshakestart = onhandshakestart.bind(this); ssl.onhandshakedone = onhandshakedone.bind(this); ssl.onclienthello = onclienthello.bind(this); + ssl.oncertcb = oncertcb.bind(this); ssl.onnewsession = onnewsession.bind(this); ssl.lastHandshakeTime = 0; ssl.handshakes = 0; - if (this.server && - (listenerCount(this.server, 'resumeSession') > 0 || - listenerCount(this.server, 'newSession') > 0 || - listenerCount(this.server, 'OCSPRequest') > 0)) { - ssl.enableSessionCallbacks(); + if (this.server) { + if (listenerCount(this.server, 'resumeSession') > 0 || + listenerCount(this.server, 'newSession') > 0) { + ssl.enableSessionCallbacks(); + } + if (listenerCount(this.server, 'OCSPRequest') > 0) + ssl.enableCertCb(); } } else { ssl.onhandshakestart = function() {}; @@ -374,7 +371,7 @@ TLSSocket.prototype._init = function(socket, wrap) { options.server._contexts.length)) { assert(typeof options.SNICallback === 'function'); this._SNICallback = options.SNICallback; - ssl.enableHelloParser(); + ssl.enableCertCb(); } if (process.features.tls_npn && options.NPNProtocols) diff --git a/src/env.h b/src/env.h index 9099b3b931717a..8a926340673c6e 100644 --- a/src/env.h +++ b/src/env.h @@ -55,6 +55,7 @@ namespace node { V(bytes_parsed_string, "bytesParsed") \ V(callback_string, "callback") \ V(change_string, "change") \ + V(oncertcb_string, "oncertcb") \ V(onclose_string, "_onclose") \ V(code_string, "code") \ V(compare_string, "compare") \ diff --git a/src/node_crypto.cc b/src/node_crypto.cc index b6cb4f10e5698f..50b43f8cafa756 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -104,7 +104,7 @@ X509_STORE* root_cert_store; template class SSLWrap; template void SSLWrap::AddMethods(Environment* env, Handle t); -template void SSLWrap::InitNPN(SecureContext* sc); +template void SSLWrap::InitNPN(TLSWrap* w, SecureContext* sc); template SSL_SESSION* SSLWrap::GetSessionCallback( SSL* s, unsigned char* key, @@ -131,6 +131,8 @@ template int SSLWrap::SelectNextProtoCallback( void* arg); #endif template int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg); +template int SSLWrap::SSLCertCallback(SSL* s, void* arg); +template void SSLWrap::WaitForCertCb(CertCb cb, void* arg); static void crypto_threadid_cb(CRYPTO_THREADID* tid) { @@ -509,7 +511,8 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, } while ((ca = PEM_read_bio_X509(in, nullptr, CryptoPemCallback, nullptr))) { - r = SSL_CTX_add_extra_chain_cert(ctx, ca); + // NOTE: Increments reference count on `ca` + r = SSL_CTX_add1_chain_cert(ctx, ca); if (!r) { X509_free(ca); @@ -978,6 +981,7 @@ void SSLWrap::AddMethods(Environment* env, Handle t) { env->SetProtoMethod(t, "verifyError", VerifyError); env->SetProtoMethod(t, "getCurrentCipher", GetCurrentCipher); env->SetProtoMethod(t, "endParser", EndParser); + env->SetProtoMethod(t, "certCbDone", CertCbDone); env->SetProtoMethod(t, "renegotiate", Renegotiate); env->SetProtoMethod(t, "shutdownSSL", Shutdown); env->SetProtoMethod(t, "getTLSTicket", GetTLSTicket); @@ -1008,7 +1012,7 @@ void SSLWrap::AddMethods(Environment* env, Handle t) { template -void SSLWrap::InitNPN(SecureContext* sc) { +void SSLWrap::InitNPN(Base* w, SecureContext* sc) { #ifdef OPENSSL_NPN_NEGOTIATED // Server should advertise NPN protocols SSL_CTX_set_next_protos_advertised_cb(sc->ctx_, @@ -1024,6 +1028,8 @@ void SSLWrap::InitNPN(SecureContext* sc) { SSL_CTX_set_tlsext_status_cb(sc->ctx_, TLSExtStatusCallback); SSL_CTX_set_tlsext_status_arg(sc->ctx_, nullptr); #endif // NODE__HAVE_TLSEXT_STATUS_CB + + SSL_set_cert_cb(w->ssl_, SSLWrap::SSLCertCallback, w); } @@ -1860,6 +1866,122 @@ int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg) { #endif // NODE__HAVE_TLSEXT_STATUS_CB +template +void SSLWrap::WaitForCertCb(CertCb cb, void* arg) { + cert_cb_ = cb; + cert_cb_arg_ = arg; +} + + +template +int SSLWrap::SSLCertCallback(SSL* s, void* arg) { + Base* w = static_cast(SSL_get_app_data(s)); + + if (!w->is_server()) + return 1; + + if (!w->is_waiting_cert_cb()) + return 1; + + if (w->cert_cb_running_) + return -1; + + Environment* env = w->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + w->cert_cb_running_ = true; + + Local info = Object::New(env->isolate()); + + SSL_SESSION* sess = SSL_get_session(s); + if (sess != nullptr) { + if (sess->tlsext_hostname == nullptr) { + info->Set(env->servername_string(), String::Empty(env->isolate())); + } else { + Local servername = OneByteString(env->isolate(), + sess->tlsext_hostname, + strlen(sess->tlsext_hostname)); + info->Set(env->servername_string(), servername); + } + info->Set(env->tls_ticket_string(), + Boolean::New(env->isolate(), sess->tlsext_ticklen != 0)); + } + bool ocsp = s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp; + info->Set(env->ocsp_request_string(), Boolean::New(env->isolate(), ocsp)); + + Local argv[] = { info }; + w->MakeCallback(env->oncertcb_string(), ARRAY_SIZE(argv), argv); + + if (!w->cert_cb_running_) + return 1; + + // Performing async action, wait... + return -1; +} + + +template +void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { + Base* w = Unwrap(args.Holder()); + Environment* env = w->env(); + + CHECK(w->is_waiting_cert_cb() && w->cert_cb_running_); + + Local object = w->object(); + Local ctx = object->Get(env->sni_context_string()); + Local cons = env->secure_context_constructor_template(); + + // Not an object, probably undefined or null + if (!ctx->IsObject()) + goto fire_cb; + + if (cons->HasInstance(ctx)) { + SecureContext* sc = Unwrap(ctx.As()); + w->sni_context_.Reset(); + w->sni_context_.Reset(env->isolate(), ctx); + + int rv; + + // NOTE: reference count is not increased by this API methods + X509* x509 = SSL_CTX_get0_certificate(sc->ctx_); + EVP_PKEY* pkey = SSL_CTX_get0_privatekey(sc->ctx_); + STACK_OF(X509)* chain; + + rv = SSL_CTX_get0_chain_certs(sc->ctx_, &chain); + if (rv) + rv = SSL_use_certificate(w->ssl_, x509); + if (rv) + rv = SSL_use_PrivateKey(w->ssl_, pkey); + if (rv && chain != nullptr) + rv = SSL_set1_chain(w->ssl_, chain); + if (!rv) { + unsigned long err = ERR_get_error(); + if (!err) + return env->ThrowError("CertCbDone"); + return ThrowCryptoError(env, err); + } + } else { + // Failure: incorrect SNI context object + Local err = Exception::TypeError(env->sni_context_err_string()); + w->MakeCallback(env->onerror_string(), 1, &err); + return; + } + + fire_cb: + CertCb cb; + void* arg; + + cb = w->cert_cb_; + arg = w->cert_cb_arg_; + + w->cert_cb_running_ = false; + w->cert_cb_ = nullptr; + w->cert_cb_arg_ = nullptr; + + cb(arg); +} + + template void SSLWrap::SSLGetter(Local property, const PropertyCallbackInfo& info) { @@ -1955,6 +2077,10 @@ int Connection::HandleSSLError(const char* func, DEBUG_PRINT("[%p] SSL: %s want read\n", ssl_, func); return 0; + } else if (err == SSL_ERROR_WANT_X509_LOOKUP) { + DEBUG_PRINT("[%p] SSL: %s want x509 lookup\n", ssl_, func); + return 0; + } else if (err == SSL_ERROR_ZERO_RETURN) { HandleScope scope(ssl_env()->isolate()); @@ -2120,7 +2246,7 @@ int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) { // Call the SNI callback and use its return value as context if (!conn->sniObject_.IsEmpty()) { - conn->sniContext_.Reset(); + conn->sni_context_.Reset(); Local arg = PersistentToLocal(env->isolate(), conn->servername_); Local ret = conn->MakeCallback(env->onselect_string(), 1, &arg); @@ -2129,9 +2255,9 @@ int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) { Local secure_context_constructor_template = env->secure_context_constructor_template(); if (secure_context_constructor_template->HasInstance(ret)) { - conn->sniContext_.Reset(env->isolate(), ret); + conn->sni_context_.Reset(env->isolate(), ret); SecureContext* sc = Unwrap(ret.As()); - InitNPN(sc); + InitNPN(conn, sc); SSL_set_SSL_CTX(s, sc->ctx_); } else { return SSL_TLSEXT_ERR_NOACK; @@ -2166,7 +2292,7 @@ void Connection::New(const FunctionCallbackInfo& args) { if (is_server) SSL_set_info_callback(conn->ssl_, SSLInfoCallback); - InitNPN(sc); + InitNPN(conn, sc); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB if (is_server) { diff --git a/src/node_crypto.h b/src/node_crypto.h index 75ffe4f31ddeea..00b0aeec2f93c7 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -138,7 +138,10 @@ class SSLWrap { kind_(kind), next_sess_(nullptr), session_callbacks_(false), - new_session_wait_(false) { + new_session_wait_(false), + cert_cb_(nullptr), + cert_cb_arg_(nullptr), + cert_cb_running_(false) { ssl_ = SSL_new(sc->ctx_); CHECK_NE(ssl_, nullptr); } @@ -157,6 +160,9 @@ class SSLWrap { npn_protos_.Reset(); selected_npn_proto_.Reset(); #endif +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB + sni_context_.Reset(); +#endif #ifdef NODE__HAVE_TLSEXT_STATUS_CB ocsp_response_.Reset(); #endif // NODE__HAVE_TLSEXT_STATUS_CB @@ -167,9 +173,12 @@ class SSLWrap { inline bool is_server() const { return kind_ == kServer; } inline bool is_client() const { return kind_ == kClient; } inline bool is_waiting_new_session() const { return new_session_wait_; } + inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } protected: - static void InitNPN(SecureContext* sc); + typedef void (*CertCb)(void* arg); + + static void InitNPN(Base* w, SecureContext* sc); static void AddMethods(Environment* env, v8::Handle t); static SSL_SESSION* GetSessionCallback(SSL* s, @@ -190,6 +199,7 @@ class SSLWrap { static void VerifyError(const v8::FunctionCallbackInfo& args); static void GetCurrentCipher(const v8::FunctionCallbackInfo& args); static void EndParser(const v8::FunctionCallbackInfo& args); + static void CertCbDone(const v8::FunctionCallbackInfo& args); static void Renegotiate(const v8::FunctionCallbackInfo& args); static void Shutdown(const v8::FunctionCallbackInfo& args); static void GetTLSTicket(const v8::FunctionCallbackInfo& args); @@ -218,9 +228,12 @@ class SSLWrap { void* arg); #endif // OPENSSL_NPN_NEGOTIATED static int TLSExtStatusCallback(SSL* s, void* arg); + static int SSLCertCallback(SSL* s, void* arg); static void SSLGetter(v8::Local property, const v8::PropertyCallbackInfo& info); + void WaitForCertCb(CertCb cb, void* arg); + inline Environment* ssl_env() const { return env_; } @@ -231,6 +244,12 @@ class SSLWrap { SSL* ssl_; bool session_callbacks_; bool new_session_wait_; + + // SSL_set_cert_cb + CertCb cert_cb_; + void* cert_cb_arg_; + bool cert_cb_running_; + ClientHelloParser hello_parser_; #ifdef NODE__HAVE_TLSEXT_STATUS_CB @@ -242,6 +261,10 @@ class SSLWrap { v8::Persistent selected_npn_proto_; #endif // OPENSSL_NPN_NEGOTIATED +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB + v8::Persistent sni_context_; +#endif + friend class SecureContext; }; @@ -253,7 +276,6 @@ class Connection : public SSLWrap, public AsyncWrap { ~Connection() override { #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB sniObject_.Reset(); - sniContext_.Reset(); servername_.Reset(); #endif } @@ -268,7 +290,6 @@ class Connection : public SSLWrap, public AsyncWrap { #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB v8::Persistent sniObject_; - v8::Persistent sniContext_; v8::Persistent servername_; #endif diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index c774a8490b54f2..2e775e7b03203f 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -148,7 +148,7 @@ void TLSWrap::InitSSL() { } #endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB - InitNPN(sc_); + InitNPN(this, sc_); if (is_server()) { SSL_set_accept_state(ssl_); @@ -353,6 +353,7 @@ Local TLSWrap::GetSSLError(int status, int* err, const char** msg) { case SSL_ERROR_NONE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_X509_LOOKUP: break; case SSL_ERROR_ZERO_RETURN: return scope.Escape(env()->zero_return_string()); @@ -722,12 +723,6 @@ void TLSWrap::EnableSessionCallbacks( const FunctionCallbackInfo& args) { TLSWrap* wrap = Unwrap(args.Holder()); wrap->enable_session_callbacks(); - EnableHelloParser(args); -} - - -void TLSWrap::EnableHelloParser(const FunctionCallbackInfo& args) { - TLSWrap* wrap = Unwrap(args.Holder()); NodeBIO::FromBIO(wrap->enc_in_)->set_initial(kMaxHelloLength); wrap->hello_parser_.Start(SSLWrap::OnClientHello, OnClientHelloParseEnd, @@ -735,6 +730,12 @@ void TLSWrap::EnableHelloParser(const FunctionCallbackInfo& args) { } +void TLSWrap::EnableCertCb(const FunctionCallbackInfo& args) { + TLSWrap* wrap = Unwrap(args.Holder()); + wrap->WaitForCertCb(OnClientHelloParseEnd, wrap); +} + + void TLSWrap::OnClientHelloParseEnd(void* arg) { TLSWrap* c = static_cast(arg); c->Cycle(); @@ -807,7 +808,7 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { p->sni_context_.Reset(env->isolate(), ctx); SecureContext* sc = Unwrap(ctx.As()); - InitNPN(sc); + InitNPN(p, sc); SSL_set_SSL_CTX(s, sc->ctx_); return SSL_TLSEXT_ERR_OK; } @@ -829,7 +830,7 @@ void TLSWrap::Initialize(Handle target, env->SetProtoMethod(t, "start", Start); env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode); env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks); - env->SetProtoMethod(t, "enableHelloParser", EnableHelloParser); + env->SetProtoMethod(t, "enableCertCb", EnableCertCb); StreamBase::AddMethods(env, t, StreamBase::kFlagHasWritev); SSLWrap::AddMethods(env, t); diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 9f095355bb58bd..2faa1f9282234d 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -130,7 +130,7 @@ class TLSWrap : public crypto::SSLWrap, static void SetVerifyMode(const v8::FunctionCallbackInfo& args); static void EnableSessionCallbacks( const v8::FunctionCallbackInfo& args); - static void EnableHelloParser( + static void EnableCertCb( const v8::FunctionCallbackInfo& args); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB @@ -158,10 +158,6 @@ class TLSWrap : public crypto::SSLWrap, // If true - delivered EOF to the js-land, either after `close_notify`, or // after the `UV_EOF` on socket. bool eof_; - -#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB - v8::Persistent sni_context_; -#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB }; } // namespace node diff --git a/test/fixtures/keys/Makefile b/test/fixtures/keys/Makefile index a096431a8c3307..1e3d40ebf1f91a 100644 --- a/test/fixtures/keys/Makefile +++ b/test/fixtures/keys/Makefile @@ -17,6 +17,28 @@ ca2-cert.pem: ca2.cnf echo '01' > ca2-serial touch ca2-database.txt +# +# Create Subordinate Certificate Authority: ca3 +# ('password' is used for the CA password.) +# +ca3-key.pem: + openssl genrsa -out ca3-key.pem 1024 + +ca3-csr.pem: ca3.cnf ca3-key.pem + openssl req -new \ + -extensions v3_ca -config ca3.cnf -key ca3-key.pem -out ca3-csr.pem + +ca3-cert.pem: ca3-csr.pem ca3-key.pem ca3.cnf ca1-cert.pem ca1-key.pem + openssl x509 -req \ + -extfile ca3.cnf \ + -extensions v3_ca \ + -days 9999 \ + -passin "pass:password" \ + -in ca3-csr.pem \ + -CA ca1-cert.pem \ + -CAkey ca1-key.pem \ + -CAcreateserial \ + -out ca3-cert.pem # # agent1 is signed by ca1. @@ -157,6 +179,31 @@ agent5-cert.pem: agent5-csr.pem ca2-cert.pem ca2-key.pem agent5-verify: agent5-cert.pem ca2-cert.pem openssl verify -CAfile ca2-cert.pem agent5-cert.pem +# +# agent6 is signed by ca3 +# + +agent6-key.pem: + openssl genrsa -out agent6-key.pem 1024 + +agent6-csr.pem: agent6.cnf agent6-key.pem + openssl req -new -config agent6.cnf -key agent6-key.pem -out agent6-csr.pem + +agent6-cert.pem: agent6-csr.pem ca3-cert.pem ca3-key.pem + openssl x509 -req \ + -days 9999 \ + -passin "pass:password" \ + -in agent6-csr.pem \ + -CA ca3-cert.pem \ + -CAkey ca3-key.pem \ + -CAcreateserial \ + -extfile agent6.cnf \ + -out agent6-cert.pem + cat ca3-cert.pem >> agent6-cert.pem + +agent6-verify: agent6-cert.pem ca3-cert.pem + openssl verify -CAfile ca3-cert.pem agent6-cert.pem + ec-key.pem: openssl ecparam -genkey -out ec-key.pem -name prime256v1 diff --git a/test/fixtures/keys/agent1-cert.pem b/test/fixtures/keys/agent1-cert.pem index c5bfb18e089e85..9c5c2ca4c62ed2 100644 --- a/test/fixtures/keys/agent1-cert.pem +++ b/test/fixtures/keys/agent1-cert.pem @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIC1jCCAj+gAwIBAgIJAJqEq8+4pyq+MA0GCSqGSIb3DQEBBQUAMHoxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzANBgNVBAoTBkpveWVu -dDEQMA4GA1UECxMHTm9kZS5qczEMMAoGA1UEAxMDY2ExMSAwHgYJKoZIhvcNAQkB -FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNDA0MTUyMTMxMzFaFw00MTA4MzAyMTMx -MzFaMH0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzAN +MIIC1jCCAj+gAwIBAgIJAJqEq8+4pyrAMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVu +dDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZIhvcNAQkB +FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNTA0MTgxMzI5MDhaFw00MjA5MDIxMzI5 +MDhaMH0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzAN BgNVBAoTBkpveWVudDEQMA4GA1UECxMHTm9kZS5qczEPMA0GA1UEAxMGYWdlbnQx MSAwHgYJKoZIhvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEAuOs3hW8rF+7xx5iB9wjmIgd+HTqRFUeKxG+mWV35Hl6A @@ -11,8 +11,8 @@ AQEFAAOBjQAwgYkCgYEAuOs3hW8rF+7xx5iB9wjmIgd+HTqRFUeKxG+mWV35Hl6A lRxqJGXTjx+vG/0nDCXLBhoDKO00zEccdjGS8xEjjieQQr+KeASmIm0kQmuN5YcC AwEAAaNhMF8wXQYIKwYBBQUHAQEEUTBPMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz cC5ub2RlanMub3JnLzAoBggrBgEFBQcwAoYcaHR0cDovL2NhLm5vZGVqcy5vcmcv -Y2EuY2VydDANBgkqhkiG9w0BAQUFAAOBgQAx6rhnYbPygJwIm6nidyx+ydJQC4Gk -JD+pzbdJkTS+01r+xjVY/Wckn4JAsIlo/MMn055rs2cfdjoQtlj6yjEU6AP/7bfr -Mju4lBxDLACJ2y5/rfj3wO4q4Knd4Q4mPWjlS2SwmkHZ21QOqJ6Ig9ps6HPM7syw -ZYQ3WQ1LOPAxMg== +Y2EuY2VydDANBgkqhkiG9w0BAQsFAAOBgQA45MmH28Gns+1yu9w9MR/oR8hKDMnG +E4yDZ+9SofWdqRsGe5MNeMbp9c+FxIxODcNmdhV5Ao6+ZCRX4N9GjLqUL1jQoFAs +pT/U80ZU+4bz2EwGMBQt7CJZb/u+j8/vXheyGFZkCWEQj6AgZQFTniRRQJLwbiy5 +uDktGqnhvamyrg== -----END CERTIFICATE----- diff --git a/test/fixtures/keys/ca1-cert.pem b/test/fixtures/keys/ca1-cert.pem index 638c803c7fb3fb..8e45f8891c14e0 100644 --- a/test/fixtures/keys/ca1-cert.pem +++ b/test/fixtures/keys/ca1-cert.pem @@ -1,15 +1,16 @@ -----BEGIN CERTIFICATE----- -MIICazCCAdQCCQC1CQyJn8L/kzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV -UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO -BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA -dGlueWNsb3Vkcy5vcmcwHhcNMTQwNDE1MjEzMTMxWhcNNDEwODMwMjEzMTMxWjB6 -MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK -EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqG -SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A -MIGJAoGBALBXMk/xFR2GN2v/wKreZKyIitGphxYGoJ2d//s/wM6qqyIW94aiq3sm -1zpmOTPTorT9Pk32A7uKKHfrafB+yA07QXgCYXgzcn17nfFInncDyGdggNFGAO13 -5JuC3JC8pRJpEokkMszpHJxPdR6gKXIT05blUnpwGT/AmYJ8S59lAgMBAAEwDQYJ -KoZIhvcNAQEFBQADgYEAAb+Pye0I+k927Qi2+cUowLS5MtmrEosUbTYwI4rqYSR2 -aiibqmC3Z55N72ktQ2pJKP8I1t3Rk+j8/yIKWzSn5Jd2GT4ZzqbANrdLKeAsfVDK -pnsUR1IV/sdIvuELm+P4kyK5wafJytUjD+A4SH2oWN4EozDR1OidAhJrraXQksw= +MIICgjCCAeugAwIBAgIJAI3yHAFGivOTMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVu +dDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZIhvcNAQkB +FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNTA0MTgxMzI4NDFaFw00MjA5MDIxMzI4 +NDFaMHoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzAN +BgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAw +HgYJKoZIhvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAwbF7gKfk7nGLcH0lbok1UJEBpMiQ49YxUqT/oIfXBaRjMODX +RknQxpARWw4R8qj+Zeu9zZZ8Hzv3dAxtcpnMTgeoPUL3HCStk0bK8QrFdkFrBxQD +mF92r9Mgr/fz+x7rSZuCIKhATwB5iJOz63fRctTL5tBmzG15JUG1GN5HPZECAwEA +AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAnAHtchz5FGqod +8twiFF3yQdGN3WE3VC3A6VrcmjKUp+M7f0uRDYw4uKUhadyZdYhMn39fe9DVN6rC +6wUUoe4hSs+0SWi6Ora7DFpCbm6fNpooSr0K0OUMZ2opwDmAEPdVOSPRhzQJ/cNp +s3mxIrkAQ5kgJSSGlPETMEumQmXDfA== -----END CERTIFICATE----- diff --git a/test/fixtures/keys/ca1-cert.srl b/test/fixtures/keys/ca1-cert.srl index ab9c1c224ad882..c9650b0529c100 100644 --- a/test/fixtures/keys/ca1-cert.srl +++ b/test/fixtures/keys/ca1-cert.srl @@ -1 +1 @@ -9A84ABCFB8A72ABE +9A84ABCFB8A72AC0 diff --git a/test/fixtures/keys/ca1-key.pem b/test/fixtures/keys/ca1-key.pem index a253989934ce10..3a619fbf2484ff 100644 --- a/test/fixtures/keys/ca1-key.pem +++ b/test/fixtures/keys/ca1-key.pem @@ -1,17 +1,17 @@ -----BEGIN ENCRYPTED PRIVATE KEY----- -MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIJ36Z9tFiv2oCAggA -MBQGCCqGSIb3DQMHBAjCAyUwdFh+7gSCAoD9Sv9009MmLb8+wHhYhxmrPrpeYZOt -W8xfELfal/2XWFe41WV41Qsa2HyN03VeyQjb+cnKvZX6HUCe5zVMUsf+B91yQBqR -jOZgsgoflEC3tFrhW5ogmRnjuok3CbTYLx0f8GMucSRNa9ZPhpfUjrVcCUrKKRJ+ -owk0VxWQkRqMF/8WJJgHxnm8/jLbNKuHbwY8SSQO/pStyKxqx5rBXAA/YCrAx7EZ -aDvc2q9EwRHX+hWIhpIkYna7PwnNAX03Ghv4iiwUlwHoomvgxExdUc9T8HBQu/xP -2pmUqwTglAdRb0tNiYrF+yIesAlqBc1qoi7tv2SM3gTowdw2PeHq5MA/ShV21oC8 -51bc0OxRkEFsHGLIs1v5qzs21JhX0IKvb5LDHAaze2RwODfnCTcQb8jYaFZNmwpt -ZrHgBQBwOHSvZ9CCqInDDtGBhYRlQIQRGgj9Ju5fruRYycn8vE58OcIfF52dwLKB -7sjTFX0O0b58wpiQSJzbHGbjNlQNRXxdk9v1qfP8vx8rFHLoFSScaZomavp4uijU -yLKVwquzEOVOMP8cC+yRk+zkcb9EE2pE1CqG0Mj6MHH1Lha98kakTIAS/VG59/Mp -EqZoRtJ6n/DzscrRpHIm+170ufLGmivmTeOXcMyHv1pGp35c5VbmUTZCxcE7A/0f -WPLVB3lBPgsvR/4NVx9A6mvXbkp2yngdfbvQgzRDG6pfrE1xeiUAyRCts7dR3Q9/ -dNHIM2wsiO3A/8Up1vrY6d/dcJt7cwjHRkx2eQFbpeE+a4CCNZ7gXFcwHLigBH8G -uTTVZf4HJavjXiYlkY3OFnPuz4KzanJzuqluKeIdJwSEkp9Kra/Id0eO +MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI4tZeEUDNwLoCAggA +MBQGCCqGSIb3DQMHBAglCttpxF+UrASCAoCOiXoMG1+CJFtRnovzp0wSCeEtieI3 +jntJkCBF+NNUZrbWaQqWO+Id8KjXNFQdUo7lYT0d0w5lf52OHTyswjN0ILCOp/WN +eVMDfgmaJXtaah2l++hDFhcbGsOdRcwM0+yComF02Qxr13ftYnIPzUH4+Ix1i1Tr +6eBGDBJv0eXDZj/dLrploA7/pepKlytRw2stIL99TSICR54UQ7fyZ/oS6NvoQU7U +iBCVsrkjyE6jHJZ6vNe/ZjM8ZdLM9K2inoldpFaavDTh6GwHC6e3e9FJuJk6X30e +s2lhrVLOmxLuFS168iApyA8XvVTg/RG08DvkzUUFv8+HNETH0Qkb5kpJM+pPzwyu +c5et2fX8YuQRc8SdIdd+Z0lZJga1IciEXlsfzGeUtcZKUjBZ5yPTx+1InfcNFDKR +wCU2p0Haj9OCcvSBzU53I57RYXkENMEHQ46/FytGULXHimIoIR/SEbrqbKX0xDmT +rc/4c4vI5+tQPYMjo0rqydLAb5YjWCivrXDVXVHrG0YFsAoJkvTBLXlPiqTcnkea +KACuq7B+ymdVmpjV891OLuN3Yah+HgrvTkMlNexsFWvGpis03UYwpI6bppe4dHtt +rLBlgtyxFLJ9hu/YnkR6HcrjKaW5kGFDX06elAZBdPD/6foghzTb4jw3OK6a4ue0 +PE/zF6d7QWW+w5xKtNO62WWKABi9OtLhdUNVAFyBlaA43zYbnXkyfmv0l7LXPZ9t +Ps+BmG4r/gxO6GmZZ40sRXFjpkj230bTjbN6sUrU3WgOszMy0uFAph+zBUIMZWSP +wqZWmQi8MHQ4+Qm8N5GdXUTXw32eZ60bj82QGPso/NNxzDQsk5wd+bR3 -----END ENCRYPTED PRIVATE KEY----- diff --git a/test/fixtures/keys/ca1.cnf b/test/fixtures/keys/ca1.cnf index ea4312752d303f..afd3066eb9daa8 100644 --- a/test/fixtures/keys/ca1.cnf +++ b/test/fixtures/keys/ca1.cnf @@ -5,6 +5,7 @@ distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = password +x509_extensions = v3_ca [ req_distinguished_name ] C = US @@ -18,3 +19,5 @@ emailAddress = ry@tinyclouds.org [ req_attributes ] challengePassword = A challenge password +[ v3_ca ] +basicConstraints = CA:TRUE diff --git a/test/parallel/test-tls-sni-server-client.js b/test/parallel/test-tls-sni-server-client.js index 7a1d09b9c2ef07..a1d867233b047a 100644 --- a/test/parallel/test-tls-sni-server-client.js +++ b/test/parallel/test-tls-sni-server-client.js @@ -35,6 +35,11 @@ var SNIContexts = { 'asterisk.test.com': { key: loadPEM('agent3-key'), cert: loadPEM('agent3-cert') + }, + 'chain.example.com': { + key: loadPEM('agent6-key'), + // NOTE: Contains ca3 chain cert + cert: loadPEM('agent6-cert') } }; @@ -42,32 +47,29 @@ var serverPort = common.PORT; var clientsOptions = [{ port: serverPort, - key: loadPEM('agent1-key'), - cert: loadPEM('agent1-cert'), ca: [loadPEM('ca1-cert')], servername: 'a.example.com', rejectUnauthorized: false }, { port: serverPort, - key: loadPEM('agent2-key'), - cert: loadPEM('agent2-cert'), ca: [loadPEM('ca2-cert')], servername: 'b.test.com', rejectUnauthorized: false }, { port: serverPort, - key: loadPEM('agent2-key'), - cert: loadPEM('agent2-cert'), ca: [loadPEM('ca2-cert')], servername: 'a.b.test.com', rejectUnauthorized: false }, { port: serverPort, - key: loadPEM('agent3-key'), - cert: loadPEM('agent3-cert'), ca: [loadPEM('ca1-cert')], servername: 'c.wrong.com', rejectUnauthorized: false +}, { + port: serverPort, + ca: [loadPEM('ca1-cert')], + servername: 'chain.example.com', + rejectUnauthorized: false }]; var serverResults = [], @@ -79,6 +81,7 @@ var server = tls.createServer(serverOptions, function(c) { server.addContext('a.example.com', SNIContexts['a.example.com']); server.addContext('*.test.com', SNIContexts['asterisk.test.com']); +server.addContext('chain.example.com', SNIContexts['chain.example.com']); server.listen(serverPort, startTest); @@ -105,7 +108,9 @@ function startTest() { } process.on('exit', function() { - assert.deepEqual(serverResults, ['a.example.com', 'b.test.com', - 'a.b.test.com', 'c.wrong.com']); - assert.deepEqual(clientResults, [true, true, false, false]); + assert.deepEqual(serverResults, [ + 'a.example.com', 'b.test.com', 'a.b.test.com', 'c.wrong.com', + 'chain.example.com' + ]); + assert.deepEqual(clientResults, [true, true, false, false, true]); });