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();
});
});
}
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() {};
options.server._contexts.length)) {
assert(typeof options.SNICallback === 'function');
this._SNICallback = options.SNICallback;
- ssl.enableHelloParser();
+ ssl.enableCertCb();
}
if (process.features.tls_npn && options.NPNProtocols)
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") \
#endif
template int SSLWrap<TLSWrap>::TLSExtStatusCallback(SSL* s, void* arg);
template void SSLWrap<TLSWrap>::DestroySSL();
+template int SSLWrap<TLSWrap>::SSLCertCallback(SSL* s, void* arg);
+template void SSLWrap<TLSWrap>::WaitForCertCb(CertCb cb, void* arg);
static void crypto_threadid_cb(CRYPTO_THREADID* tid) {
}
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);
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);
template <class Base>
+void SSLWrap<Base>::WaitForCertCb(CertCb cb, void* arg) {
+ cert_cb_ = cb;
+ cert_cb_arg_ = arg;
+}
+
+
+template <class Base>
+int SSLWrap<Base>::SSLCertCallback(SSL* s, void* arg) {
+ Base* w = static_cast<Base*>(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<Object> 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<String> 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<Value> 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 <class Base>
+void SSLWrap<Base>::CertCbDone(const FunctionCallbackInfo<Value>& args) {
+ Base* w = Unwrap<Base>(args.Holder());
+ Environment* env = w->env();
+
+ CHECK(w->is_waiting_cert_cb() && w->cert_cb_running_);
+
+ Local<Object> object = w->object();
+ Local<Value> ctx = object->Get(env->sni_context_string());
+ Local<FunctionTemplate> 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<SecureContext>(ctx.As<Object>());
+ 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<Value> 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 <class Base>
void SSLWrap<Base>::SSLGetter(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
HandleScope scope(info.GetIsolate());
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());
// Call the SNI callback and use its return value as context
if (!conn->sniObject_.IsEmpty()) {
- conn->sniContext_.Reset();
+ conn->sni_context_.Reset();
Local<Value> arg = PersistentToLocal(env->isolate(), conn->servername_);
Local<Value> ret = conn->MakeCallback(env->onselect_string(), 1, &arg);
Local<FunctionTemplate> 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<SecureContext>(ret.As<Object>());
InitNPN(sc);
SSL_set_SSL_CTX(s, sc->ctx_);
InitNPN(sc);
+ SSL_set_cert_cb(conn->ssl_, SSLWrap<Connection>::SSLCertCallback, conn);
+
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
if (is_server) {
SSL_CTX_set_tlsext_servername_callback(sc->ctx_, SelectSNIContextCallback_);
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_);
env_->isolate()->AdjustAmountOfExternalAllocatedMemory(kExternalSize);
CHECK_NE(ssl_, nullptr);
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
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:
+ typedef void (*CertCb)(void* arg);
+
// Size allocated by OpenSSL: one for SSL structure, one for SSL3_STATE and
// some for buffers.
// NOTE: Actually it is much more than this
static void VerifyError(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetCurrentCipher(const v8::FunctionCallbackInfo<v8::Value>& args);
static void EndParser(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void CertCbDone(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetTLSTicket(const v8::FunctionCallbackInfo<v8::Value>& args);
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<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info);
void DestroySSL();
+ void WaitForCertCb(CertCb cb, void* arg);
inline Environment* ssl_env() const {
return env_;
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
v8::Persistent<v8::Value> selected_npn_proto_;
#endif // OPENSSL_NPN_NEGOTIATED
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+ v8::Persistent<v8::Value> sni_context_;
+#endif
+
friend class SecureContext;
};
~Connection() override {
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
sniObject_.Reset();
- sniContext_.Reset();
servername_.Reset();
#endif
}
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
v8::Persistent<v8::Object> sniObject_;
- v8::Persistent<v8::Value> sniContext_;
v8::Persistent<v8::String> servername_;
#endif
InitNPN(sc_);
+ SSL_set_cert_cb(ssl_, SSLWrap<TLSWrap>::SSLCertCallback, this);
+
if (is_server()) {
SSL_set_accept_state(ssl_);
} else if (is_client()) {
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());
const FunctionCallbackInfo<Value>& args) {
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
wrap->enable_session_callbacks();
- EnableHelloParser(args);
-}
-
-
-void TLSWrap::EnableHelloParser(const FunctionCallbackInfo<Value>& args) {
- TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
NodeBIO::FromBIO(wrap->enc_in_)->set_initial(kMaxHelloLength);
wrap->hello_parser_.Start(SSLWrap<TLSWrap>::OnClientHello,
OnClientHelloParseEnd,
}
+void TLSWrap::EnableCertCb(const FunctionCallbackInfo<Value>& args) {
+ TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
+ wrap->WaitForCertCb(OnClientHelloParseEnd, wrap);
+}
+
+
void TLSWrap::OnClientHelloParseEnd(void* arg) {
TLSWrap* c = static_cast<TLSWrap*>(arg);
c->Cycle();
env->SetProtoMethod(t, "start", Start);
env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode);
env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks);
- env->SetProtoMethod(t, "enableHelloParser", EnableHelloParser);
env->SetProtoMethod(t, "destroySSL", DestroySSL);
+ env->SetProtoMethod(t, "enableCertCb", EnableCertCb);
StreamBase::AddMethods<TLSWrap>(env, t, StreamBase::kFlagHasWritev);
SSLWrap<TLSWrap>::AddMethods(env, t);
static void SetVerifyMode(const v8::FunctionCallbackInfo<v8::Value>& args);
static void EnableSessionCallbacks(
const v8::FunctionCallbackInfo<v8::Value>& args);
- static void EnableHelloParser(
+ static void EnableCertCb(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void DestroySSL(const v8::FunctionCallbackInfo<v8::Value>& args);
// 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<v8::Value> sni_context_;
-#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
};
} // namespace node
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.
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
-----BEGIN CERTIFICATE-----
-MIIC1jCCAj+gAwIBAgIJAJqEq8+4pyq+MA0GCSqGSIb3DQEBBQUAMHoxCzAJBgNV
-BAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzANBgNVBAoTBkpveWVu
-dDEQMA4GA1UECxMHTm9kZS5qczEMMAoGA1UEAxMDY2ExMSAwHgYJKoZIhvcNAQkB
-FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNDA0MTUyMTMxMzFaFw00MTA4MzAyMTMx
-MzFaMH0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzAN
+MIIC1jCCAj+gAwIBAgIJAJqEq8+4pyrAMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVu
+dDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZIhvcNAQkB
+FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNTA0MTgxMzI5MDhaFw00MjA5MDIxMzI5
+MDhaMH0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzAN
BgNVBAoTBkpveWVudDEQMA4GA1UECxMHTm9kZS5qczEPMA0GA1UEAxMGYWdlbnQx
MSAwHgYJKoZIhvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzCBnzANBgkqhkiG9w0B
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-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICajCCAdOgAwIBAgIJAMTNiT75p13MMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVu
+dDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2EzMSAwHgYJKoZIhvcNAQkB
+FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNTA0MTgxMzI4NDFaFw00MjA5MDIxMzI4
+NDFaMHQxCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDERMA8GA1UECgwI
+VHJlc29yaXQxFjAUBgNVBAMMDcOBZMOhbSBMaXBwYWkxJzAlBgkqhkiG9w0BCQEW
+GGFkYW0ubGlwcGFpQHRyZXNvcml0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEA3Iwmwd6gdWH1AlSFeuVsEY/2MQm3XluOyHR9HNtXkWqwcQqVL8FX3NHt
+//1jaSTMJjkR4FhC9R0hX6wyUuBp11J4GzoDqd02JUkCeUISq/3/2G+ynaZCx5Eo
+GNHhcN0gALTCET/1QMD9h4aBjRbij3iHUghcbgverfkasp59WWcCAwEAATANBgkq
+hkiG9w0BAQsFAAOBgQAmfrCJY+FPeOraPTUQTYf9rXqfVRQEVc/yyVygPbtg3gtA
+yST0wI/g6sBjQ6Mm39yMf4rkWmwOKGtrKcqs9o9NdM5g5QQSWeg925Ex6aB+REgz
+qjaAsLM88BJ0QU76VPi6K0hDSpeuQ6Zrcp93VkdGdVZzna3FSCMTNRnSq/GuMQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIJAJqEq8+4pyq/MA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVu
+dDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZIhvcNAQkB
+FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNTA0MTgxMzI4NDFaFw00MjA5MDIxMzI4
+NDFaMHoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzAN
+BgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2EzMSAw
+HgYJKoZIhvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAqs4MKn9saUIu/9EfHQPouC3kL9Mo5sd1WR6RBeSd8cqeFxXW
+EWEq/P0hUeAH1sY0u8RFOccJmSJg8KTyRGc+VZzWimopz17mTuQY4hPW4bFzqmQm
+7STfJz5eHzynBTU8jk5omi8hjbnRA38jOm4D7rN/vqtB+RG+vEhxONnq4DMCAwEA
+AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBo8rX1uZWHvKHG
+gWw+LXrY24Pkg8NdDRmfqEVyuaR4GoGGOXCqlVaFa6x+4/eqOUzHoC9uGfPtjrvW
+BYQ1o/l0JZWW4KZYuXoVuMUSj+sel82mf9zLDeq5WYTPECgJDMfgVpXOmhHfyezn
+SkUTX7XJUohjET+X5BqTFlqRT/RfIw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIIB2TCCAUICAQAwdDELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MREw
+DwYDVQQKDAhUcmVzb3JpdDEWMBQGA1UEAwwNw4Fkw6FtIExpcHBhaTEnMCUGCSqG
+SIb3DQEJARYYYWRhbS5saXBwYWlAdHJlc29yaXQuY29tMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDcjCbB3qB1YfUCVIV65WwRj/YxCbdeW47IdH0c21eRarBx
+CpUvwVfc0e3//WNpJMwmORHgWEL1HSFfrDJS4GnXUngbOgOp3TYlSQJ5QhKr/f/Y
+b7KdpkLHkSgY0eFw3SAAtMIRP/VAwP2HhoGNFuKPeIdSCFxuC96t+Rqynn1ZZwID
+AQABoCUwIwYJKoZIhvcNAQkHMRYMFEEgY2hhbGxlbmdlIHBhc3N3b3JkMA0GCSqG
+SIb3DQEBCwUAA4GBAEU4gmRyeeh5TMYG3bI0biXr+9CvkYBaHwZD5o4TUo8AenIR
+NTrJdy9Pg9B23eOnEnCDB+KMfl08UuaPxbKRXRtYm1rTC8v5wmJEpZdWxum4c3hL
+3o7J8/LmjRGQImr5vnS5zmsVrBLtjW+jVpSg5xnXFKQmpXPfgRwhvbu0lXf7
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDcjCbB3qB1YfUCVIV65WwRj/YxCbdeW47IdH0c21eRarBxCpUv
+wVfc0e3//WNpJMwmORHgWEL1HSFfrDJS4GnXUngbOgOp3TYlSQJ5QhKr/f/Yb7Kd
+pkLHkSgY0eFw3SAAtMIRP/VAwP2HhoGNFuKPeIdSCFxuC96t+Rqynn1ZZwIDAQAB
+AoGBAIL3AsjbL8OksL56fG0XMY5YQ6SpFWeFzQsCCY2KPrzOcwodc6vRDyDE1KTP
+zimQvV3xQ8lKADDX5IqQka2fL5mgF+LighVvGHDm6M4ILJb46SDbuINwnqqvVuye
++OwjHBGEmKu18K+eL/YoCh3+sFTKP/18F7c7DGskCyzyub5RAkEA+Fs1ROx5w8AH
+cbIH4fMU/QBGQVnuKgNXGSPcT6NHqFLbrhvNn5HwoF1SiJKkML1h3gVpj3T8kquw
+Y1FcTVB9eQJBAONV1qXFo7i5gl2FyPuXvpgdzIXxzzr6q3seDkCR7q/vfBo+kKAx
+zyG2xjJrCc9+ox4Vh257qK9b57W6R6sWNd8CQQCeAHjNVpzI2nxh6t908k8h/nCz
+1uDcPa/FwLjCuaA3CC/Wfr28jP5HJ9gAJzrp/zIqK8tShxzAuxXGudY9Ib4RAkEA
+v+3elIIx4WktOQwUTOUmEoNGAufOD3tGf2E2oykRnRPRcM7Vh4nF2C7ZUgOweq/t
+wx5mAs7/8VzkWTb1/ul3fQJACLBXTChgyA77i5C/035tLwQbeLOjexLblEI0dgkW
+HIa8q4ZL0M7L+/oziQ8zIT0bTAqEG1Q00PgFLl3m8gDuNg==
+-----END RSA PRIVATE KEY-----
--- /dev/null
+[ req ]
+string_mask = utf8only
+utf8 = yes
+default_bits = 1024
+days = 999
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+prompt = no
+
+[ req_distinguished_name ]
+C = HU
+L = Budapest
+O = Tresorit
+CN = Ádám Lippai
+emailAddress = adam.lippai@tresorit.com
+
+[ req_attributes ]
+challengePassword = A challenge password
-----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-----
-9A84ABCFB8A72ABE
+9A84ABCFB8A72AC0
-----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-----
attributes = req_attributes
prompt = no
output_password = password
+x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
[ req_attributes ]
challengePassword = A challenge password
+[ v3_ca ]
+basicConstraints = CA:TRUE
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIJAJqEq8+4pyq/MA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVu
+dDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZIhvcNAQkB
+FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNTA0MTgxMzI4NDFaFw00MjA5MDIxMzI4
+NDFaMHoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzAN
+BgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2EzMSAw
+HgYJKoZIhvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAqs4MKn9saUIu/9EfHQPouC3kL9Mo5sd1WR6RBeSd8cqeFxXW
+EWEq/P0hUeAH1sY0u8RFOccJmSJg8KTyRGc+VZzWimopz17mTuQY4hPW4bFzqmQm
+7STfJz5eHzynBTU8jk5omi8hjbnRA38jOm4D7rN/vqtB+RG+vEhxONnq4DMCAwEA
+AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBo8rX1uZWHvKHG
+gWw+LXrY24Pkg8NdDRmfqEVyuaR4GoGGOXCqlVaFa6x+4/eqOUzHoC9uGfPtjrvW
+BYQ1o/l0JZWW4KZYuXoVuMUSj+sel82mf9zLDeq5WYTPECgJDMfgVpXOmhHfyezn
+SkUTX7XJUohjET+X5BqTFlqRT/RfIw==
+-----END CERTIFICATE-----
--- /dev/null
+C4CD893EF9A75DCC
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIIB3zCCAUgCAQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQH
+DAJTRjEPMA0GA1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQD
+DANjYTMxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQCqzgwqf2xpQi7/0R8dA+i4LeQv0yjmx3VZHpEF
+5J3xyp4XFdYRYSr8/SFR4AfWxjS7xEU5xwmZImDwpPJEZz5VnNaKainPXuZO5Bji
+E9bhsXOqZCbtJN8nPl4fPKcFNTyOTmiaLyGNudEDfyM6bgPus3++q0H5Eb68SHE4
+2ergMwIDAQABoCUwIwYJKoZIhvcNAQkHMRYMFEEgY2hhbGxlbmdlIHBhc3N3b3Jk
+MA0GCSqGSIb3DQEBCwUAA4GBABMaKC7NVVdfoQeKwIy5lYo17mOr4WcWHPNRcoIy
+rAHLcAzFOp0RCSZ7ROVRR6O/QIBYapUmPmdYRhKfz1g35xCX3+T28cWXngALV5v0
+XzMYJiew+97/LlNnBwoTRafAorviugdbFgJeMpYHRkG7/zXQsBz+hwgymKZnHW9D
+Dl4h
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCqzgwqf2xpQi7/0R8dA+i4LeQv0yjmx3VZHpEF5J3xyp4XFdYR
+YSr8/SFR4AfWxjS7xEU5xwmZImDwpPJEZz5VnNaKainPXuZO5BjiE9bhsXOqZCbt
+JN8nPl4fPKcFNTyOTmiaLyGNudEDfyM6bgPus3++q0H5Eb68SHE42ergMwIDAQAB
+AoGBAJkcc5N0/j2s8mynjXh5FJhlqvOkGjol+m+VEvNxaJRiySxwiqCxtdNrJf87
+EEvbCVJ4MoYEgfof8z5E3lerJRgqrhY2RSfiQrSUA89Lw9uYzcx28zhWpwwmuLHY
+5gjz+LCDDS5okLsXnl2awHXADEmcx29sZnRS6dGRFcf8F0FhAkEA1c7HrW8Vghu2
+FlRaY6LOuoFNAHM++ugoWrC85/moYevLG8wAJCuSIp/RuWrx1FdJoa7rfhyS649v
+cMGN0m1yHwJBAMyC1S1QoqXSdoqN8OrXyHJmaSbWG8IMLcT2FXA8Mk3Tk0zWSjiz
+sk/O85NsmUQQnkRgbtSS+w0Kc0OMWXbfl20CQH+igFsNjEZuaoXr90WxhD2cQK57
+HebEvopdJXhJ9nX2P/qpDpCJHiTjSVyp9hFvxjnp5RUU07QhnUIvmY073rsCQFMN
+ovNHNvZutVNpd3h372B+NJ/f/d/dQE0nvucYmzk9/ikLMZM7buO4YPTy+n9I3G1a
+WEgd9LSEFPFOsxpyjTUCQGn9XTyeSo1EoVuV21DE0Cnx30YsnPKMT1YRS7QgjDPK
+RA3fSsvnhtTzT53kfJ/ZurBV+RKbePL1JVqDtGvJVeE=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+[ req ]
+default_bits = 1024
+days = 999
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+prompt = no
+output_password = password
+x509_extensions = v3_ca
+
+[ req_distinguished_name ]
+C = US
+ST = CA
+L = SF
+O = Joyent
+OU = Node.js
+CN = ca3
+emailAddress = ry@tinyclouds.org
+
+[ req_attributes ]
+challengePassword = A challenge password
+
+[ v3_ca ]
+basicConstraints = CA:TRUE
common.debug(util.inspect(peerCert));
assert.equal(peerCert.subject.emailAddress, 'ry@tinyclouds.org');
- assert.equal(peerCert.serialNumber, '9A84ABCFB8A72ABE');
+ assert.equal(peerCert.serialNumber, '9A84ABCFB8A72AC0');
assert.deepEqual(peerCert.infoAccess['OCSP - URI'],
[ 'http://ocsp.nodejs.org/' ]);
var issuer = peerCert.issuerCertificate;
assert.ok(issuer.issuerCertificate === issuer);
- assert.equal(issuer.serialNumber, 'B5090C899FC2FF93');
+ assert.equal(issuer.serialNumber, '8DF21C01468AF393');
verified = true;
server.close();
});
'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')
}
};
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 = [],
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);
}
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]);
});