summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFedor Indutny <fedor@indutny.com>2014-04-17 15:57:36 +0400
committerFedor Indutny <fedor@indutny.com>2014-04-18 02:21:16 +0400
commit345c40b6615c499b586b6f7f5528bcbea28ca12e (patch)
tree4eda064476220fd51c6c714d739c5f5624f6ef02 /src
parentb3ef289ffb7db476d284866658213f04415ea92d (diff)
downloadandroid-node-v8-345c40b6615c499b586b6f7f5528bcbea28ca12e.tar.gz
android-node-v8-345c40b6615c499b586b6f7f5528bcbea28ca12e.tar.bz2
android-node-v8-345c40b6615c499b586b6f7f5528bcbea28ca12e.zip
tls: `getPeerCertificate(detailed)`
Add `raw` property to certificate, add mode to output full certificate chain.
Diffstat (limited to 'src')
-rw-r--r--src/env.h2
-rw-r--r--src/node_crypto.cc382
2 files changed, 248 insertions, 136 deletions
diff --git a/src/env.h b/src/env.h
index 44054149a4..d355b4899b 100644
--- a/src/env.h
+++ b/src/env.h
@@ -120,6 +120,7 @@ namespace node {
V(ipv6_lc_string, "ipv6") \
V(ipv6_string, "IPv6") \
V(issuer_string, "issuer") \
+ V(issuercert_string, "issuerCertificate") \
V(kill_signal_string, "killSignal") \
V(mac_string, "mac") \
V(mark_sweep_compact_string, "mark-sweep-compact") \
@@ -169,6 +170,7 @@ namespace node {
V(priority_string, "priority") \
V(processed_string, "processed") \
V(prototype_string, "prototype") \
+ V(raw_string, "raw") \
V(rdev_string, "rdev") \
V(readable_string, "readable") \
V(received_shutdown_string, "receivedShutdown") \
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 0d685c9f51..e29948520c 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -76,6 +76,7 @@ namespace crypto {
using v8::Array;
using v8::Boolean;
using v8::Context;
+using v8::EscapableHandleScope;
using v8::Exception;
using v8::False;
using v8::FunctionCallbackInfo;
@@ -471,17 +472,35 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) {
}
+int SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer) {
+ int ret;
+
+ X509_STORE* store = SSL_CTX_get_cert_store(ctx);
+ X509_STORE_CTX store_ctx;
+
+ ret = X509_STORE_CTX_init(&store_ctx, store, NULL, NULL);
+ if (!ret)
+ goto end;
+
+ ret = X509_STORE_CTX_get1_issuer(issuer, &store_ctx, cert);
+ X509_STORE_CTX_cleanup(&store_ctx);
+
+end:
+ return ret;
+}
+
+
// Read a file that contains our certificate in "PEM" format,
// possibly followed by a sequence of CA certificates that should be
// sent to the peer in the Certificate message.
//
// Taken from OpenSSL - editted for style.
-int SSL_CTX_use_certificate_chain(SSL_CTX *ctx,
- BIO *in,
+int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
+ BIO* in,
X509** cert,
X509** issuer) {
int ret = 0;
- X509 *x = NULL;
+ X509* x = NULL;
x = PEM_read_bio_X509_AUX(in, NULL, CryptoPemCallback, NULL);
@@ -542,16 +561,7 @@ int SSL_CTX_use_certificate_chain(SSL_CTX *ctx,
// Try getting issuer from a cert store
if (ret) {
if (*issuer == NULL) {
- X509_STORE* store = SSL_CTX_get_cert_store(ctx);
- X509_STORE_CTX store_ctx;
-
- ret = X509_STORE_CTX_init(&store_ctx, store, NULL, NULL);
- if (!ret)
- goto end;
-
- ret = X509_STORE_CTX_get1_issuer(issuer, &store_ctx, x);
- X509_STORE_CTX_cleanup(&store_ctx);
-
+ ret = SSL_CTX_get_issuer(ctx, x, issuer);
ret = ret < 0 ? 0 : 1;
// NOTE: get_cert_store doesn't increment reference count,
// no need to free `store`
@@ -1081,160 +1091,260 @@ void SSLWrap<Base>::OnClientHello(void* arg,
}
-// TODO(indutny): Split it into multiple smaller functions
-template <class Base>
-void SSLWrap<Base>::GetPeerCertificate(
- const FunctionCallbackInfo<Value>& args) {
- HandleScope scope(args.GetIsolate());
+static Local<Object> X509ToObject(Environment* env, X509* cert) {
+ EscapableHandleScope scope(env->isolate());
- Base* w = Unwrap<Base>(args.Holder());
- Environment* env = w->ssl_env();
+ Local<Object> info = Object::New(env->isolate());
- ClearErrorOnReturn clear_error_on_return;
- (void) &clear_error_on_return; // Silence unused variable warning.
+ BIO* bio = BIO_new(BIO_s_mem());
+ BUF_MEM* mem;
+ if (X509_NAME_print_ex(bio,
+ X509_get_subject_name(cert),
+ 0,
+ X509_NAME_FLAGS) > 0) {
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(env->subject_string(),
+ OneByteString(env->isolate(), mem->data, mem->length));
+ }
+ (void) BIO_reset(bio);
- Local<Object> info = Object::New(env->isolate());
- X509* peer_cert = SSL_get_peer_certificate(w->ssl_);
- if (peer_cert != NULL) {
- BIO* bio = BIO_new(BIO_s_mem());
- BUF_MEM* mem;
- if (X509_NAME_print_ex(bio,
- X509_get_subject_name(peer_cert),
- 0,
- X509_NAME_FLAGS) > 0) {
- BIO_get_mem_ptr(bio, &mem);
- info->Set(env->subject_string(),
- OneByteString(args.GetIsolate(), mem->data, mem->length));
- }
- (void) BIO_reset(bio);
+ X509_NAME* issuer_name = X509_get_issuer_name(cert);
+ if (X509_NAME_print_ex(bio, issuer_name, 0, X509_NAME_FLAGS) > 0) {
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(env->issuer_string(),
+ OneByteString(env->isolate(), mem->data, mem->length));
+ }
+ (void) BIO_reset(bio);
+
+ int nids[] = { NID_subject_alt_name, NID_info_access };
+ Local<String> keys[] = { env->subjectaltname_string(),
+ env->infoaccess_string() };
+ CHECK_EQ(ARRAY_SIZE(nids), ARRAY_SIZE(keys));
+ for (unsigned int i = 0; i < ARRAY_SIZE(nids); i++) {
+ int index = X509_get_ext_by_NID(cert, nids[i], -1);
+ if (index < 0)
+ continue;
- X509_NAME* issuer_name = X509_get_issuer_name(peer_cert);
- if (X509_NAME_print_ex(bio, issuer_name, 0, X509_NAME_FLAGS) > 0) {
- BIO_get_mem_ptr(bio, &mem);
- info->Set(env->issuer_string(),
- OneByteString(args.GetIsolate(), mem->data, mem->length));
- }
- (void) BIO_reset(bio);
+ X509_EXTENSION* ext;
+ int rv;
- int nids[] = { NID_subject_alt_name, NID_info_access };
- Local<String> keys[] = { env->subjectaltname_string(),
- env->infoaccess_string() };
- CHECK_EQ(ARRAY_SIZE(nids), ARRAY_SIZE(keys));
- for (unsigned int i = 0; i < ARRAY_SIZE(nids); i++) {
- int index = X509_get_ext_by_NID(peer_cert, nids[i], -1);
- if (index < 0)
- continue;
+ ext = X509_get_ext(cert, index);
+ assert(ext != NULL);
- X509_EXTENSION* ext;
- int rv;
+ rv = X509V3_EXT_print(bio, ext, 0, 0);
+ assert(rv == 1);
- ext = X509_get_ext(peer_cert, index);
- assert(ext != NULL);
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(keys[i],
+ OneByteString(env->isolate(), mem->data, mem->length));
- rv = X509V3_EXT_print(bio, ext, 0, 0);
- assert(rv == 1);
+ (void) BIO_reset(bio);
+ }
+ EVP_PKEY* pkey = X509_get_pubkey(cert);
+ RSA* rsa = NULL;
+ if (pkey != NULL)
+ rsa = EVP_PKEY_get1_RSA(pkey);
+
+ if (rsa != NULL) {
+ BN_print(bio, rsa->n);
BIO_get_mem_ptr(bio, &mem);
- info->Set(keys[i],
- OneByteString(args.GetIsolate(), mem->data, mem->length));
+ info->Set(env->modulus_string(),
+ OneByteString(env->isolate(), mem->data, mem->length));
+ (void) BIO_reset(bio);
+ BN_print(bio, rsa->e);
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(env->exponent_string(),
+ OneByteString(env->isolate(), mem->data, mem->length));
(void) BIO_reset(bio);
+ }
+
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+ if (rsa != NULL) {
+ RSA_free(rsa);
+ rsa = NULL;
+ }
+
+ ASN1_TIME_print(bio, X509_get_notBefore(cert));
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(env->valid_from_string(),
+ OneByteString(env->isolate(), mem->data, mem->length));
+ (void) BIO_reset(bio);
+
+ ASN1_TIME_print(bio, X509_get_notAfter(cert));
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(env->valid_to_string(),
+ OneByteString(env->isolate(), mem->data, mem->length));
+ BIO_free_all(bio);
+
+ unsigned int md_size, i;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ if (X509_digest(cert, EVP_sha1(), md, &md_size)) {
+ const char hex[] = "0123456789ABCDEF";
+ char fingerprint[EVP_MAX_MD_SIZE * 3];
+
+ // TODO(indutny): Unify it with buffer's code
+ for (i = 0; i < md_size; i++) {
+ fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4];
+ fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)];
+ fingerprint[(3*i)+2] = ':';
}
- EVP_PKEY* pkey = X509_get_pubkey(peer_cert);
- RSA* rsa = NULL;
- if (pkey != NULL)
- rsa = EVP_PKEY_get1_RSA(pkey);
-
- if (rsa != NULL) {
- BN_print(bio, rsa->n);
- BIO_get_mem_ptr(bio, &mem);
- info->Set(env->modulus_string(),
- OneByteString(args.GetIsolate(), mem->data, mem->length));
- (void) BIO_reset(bio);
-
- BN_print(bio, rsa->e);
- BIO_get_mem_ptr(bio, &mem);
- info->Set(env->exponent_string(),
- OneByteString(args.GetIsolate(), mem->data, mem->length));
- (void) BIO_reset(bio);
+ if (md_size > 0) {
+ fingerprint[(3*(md_size-1))+2] = '\0';
+ } else {
+ fingerprint[0] = '\0';
}
- if (pkey != NULL) {
- EVP_PKEY_free(pkey);
- pkey = NULL;
+ info->Set(env->fingerprint_string(),
+ OneByteString(env->isolate(), fingerprint));
+ }
+
+ STACK_OF(ASN1_OBJECT)* eku = static_cast<STACK_OF(ASN1_OBJECT)*>(
+ X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL));
+ if (eku != NULL) {
+ Local<Array> ext_key_usage = Array::New(env->isolate());
+ char buf[256];
+
+ int j = 0;
+ for (int i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
+ if (OBJ_obj2txt(buf, sizeof(buf), sk_ASN1_OBJECT_value(eku, i), 1) >= 0)
+ ext_key_usage->Set(j++, OneByteString(env->isolate(), buf));
}
- if (rsa != NULL) {
- RSA_free(rsa);
- rsa = NULL;
+
+ sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free);
+ info->Set(env->ext_key_usage_string(), ext_key_usage);
+ }
+
+ if (ASN1_INTEGER* serial_number = X509_get_serialNumber(cert)) {
+ if (BIGNUM* bn = ASN1_INTEGER_to_BN(serial_number, NULL)) {
+ if (char* buf = BN_bn2hex(bn)) {
+ info->Set(env->serial_number_string(),
+ OneByteString(env->isolate(), buf));
+ OPENSSL_free(buf);
+ }
+ BN_free(bn);
}
+ }
- ASN1_TIME_print(bio, X509_get_notBefore(peer_cert));
- BIO_get_mem_ptr(bio, &mem);
- info->Set(env->valid_from_string(),
- OneByteString(args.GetIsolate(), mem->data, mem->length));
- (void) BIO_reset(bio);
+ // Raw DER certificate
+ int size = i2d_X509(cert, NULL);
+ Local<Object> buff = Buffer::New(env, size);
+ unsigned char* serialized = reinterpret_cast<unsigned char*>(
+ Buffer::Data(buff));
+ i2d_X509(cert, &serialized);
+ info->Set(env->raw_string(), buff);
- ASN1_TIME_print(bio, X509_get_notAfter(peer_cert));
- BIO_get_mem_ptr(bio, &mem);
- info->Set(env->valid_to_string(),
- OneByteString(args.GetIsolate(), mem->data, mem->length));
- BIO_free_all(bio);
+ return scope.Escape(info);
+}
- unsigned int md_size, i;
- unsigned char md[EVP_MAX_MD_SIZE];
- if (X509_digest(peer_cert, EVP_sha1(), md, &md_size)) {
- const char hex[] = "0123456789ABCDEF";
- char fingerprint[EVP_MAX_MD_SIZE * 3];
-
- // TODO(indutny): Unify it with buffer's code
- for (i = 0; i < md_size; i++) {
- fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4];
- fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)];
- fingerprint[(3*i)+2] = ':';
- }
- if (md_size > 0) {
- fingerprint[(3*(md_size-1))+2] = '\0';
- } else {
- fingerprint[0] = '\0';
- }
+// TODO(indutny): Split it into multiple smaller functions
+template <class Base>
+void SSLWrap<Base>::GetPeerCertificate(
+ const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(args.GetIsolate());
- info->Set(env->fingerprint_string(),
- OneByteString(args.GetIsolate(), fingerprint));
- }
+ Base* w = Unwrap<Base>(args.Holder());
+ Environment* env = w->ssl_env();
+
+ ClearErrorOnReturn clear_error_on_return;
+ (void) &clear_error_on_return; // Silence unused variable warning.
- STACK_OF(ASN1_OBJECT)* eku = static_cast<STACK_OF(ASN1_OBJECT)*>(
- X509_get_ext_d2i(peer_cert, NID_ext_key_usage, NULL, NULL));
- if (eku != NULL) {
- Local<Array> ext_key_usage = Array::New(env->isolate());
- char buf[256];
+ Local<Object> result;
+ Local<Object> info;
+ X509* cert;
- int j = 0;
- for (int i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
- if (OBJ_obj2txt(buf, sizeof(buf), sk_ASN1_OBJECT_value(eku, i), 1) >= 0)
- ext_key_usage->Set(j++, OneByteString(args.GetIsolate(), buf));
- }
+ STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(w->ssl_);
+ STACK_OF(X509)* peer_certs = NULL;
+ if (ssl_certs == NULL)
+ goto done;
- sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free);
- info->Set(env->ext_key_usage_string(), ext_key_usage);
- }
+ if (sk_X509_num(ssl_certs) == 0) {
+ goto done;
+ }
- if (ASN1_INTEGER* serial_number = X509_get_serialNumber(peer_cert)) {
- if (BIGNUM* bn = ASN1_INTEGER_to_BN(serial_number, NULL)) {
- if (char* buf = BN_bn2hex(bn)) {
- info->Set(env->serial_number_string(),
- OneByteString(args.GetIsolate(), buf));
- OPENSSL_free(buf);
- }
- BN_free(bn);
- }
+ // Short result requested
+ if (args.Length() < 1 || !args[0]->IsTrue()) {
+ result = X509ToObject(env, sk_X509_value(ssl_certs, 0));
+ goto done;
+ }
+
+ // Clone `ssl_certs`, because we are going to destruct it
+ peer_certs = sk_X509_new(NULL);
+ for (int i = 0; i < sk_X509_num(ssl_certs); i++) {
+ cert = X509_dup(sk_X509_value(ssl_certs, i));
+ if (cert == NULL)
+ goto done;
+ if (!sk_X509_push(peer_certs, cert))
+ goto done;
+ }
+
+ // First and main certificate
+ cert = sk_X509_value(peer_certs, 0);
+ result = X509ToObject(env, cert);
+ info = result;
+
+ // Put issuer inside the object
+ cert = sk_X509_delete(peer_certs, 0);
+ while (sk_X509_num(peer_certs) > 0) {
+ int i;
+ for (i = 0; i < sk_X509_num(peer_certs); i++) {
+ X509* ca = sk_X509_value(peer_certs, i);
+ if (X509_check_issued(ca, cert) != X509_V_OK)
+ continue;
+
+ Local<Object> ca_info = X509ToObject(env, ca);
+ info->Set(env->issuercert_string(), ca_info);
+ info = ca_info;
+
+ // NOTE: Intentionally freeing cert that is not used anymore
+ X509_free(cert);
+
+ // Delete cert and continue aggregating issuers
+ cert = sk_X509_delete(peer_certs, i);
+ break;
}
- X509_free(peer_cert);
+ // Issuer not found, break out of the loop
+ if (i == sk_X509_num(peer_certs))
+ break;
}
- args.GetReturnValue().Set(info);
+ // Last certificate should be self-signed
+ while (X509_check_issued(cert, cert) != X509_V_OK) {
+ X509* ca;
+ if (SSL_CTX_get_issuer(w->ssl_->ctx, cert, &ca) <= 0)
+ break;
+
+ Local<Object> ca_info = X509ToObject(env, ca);
+ info->Set(env->issuercert_string(), ca_info);
+ info = ca_info;
+
+ // NOTE: Intentionally freeing cert that is not used anymore
+ X509_free(cert);
+
+ // Delete cert and continue aggregating issuers
+ cert = ca;
+ }
+
+ // Self-issued certificate
+ if (X509_check_issued(cert, cert) == X509_V_OK)
+ info->Set(env->issuercert_string(), info);
+
+ CHECK_NE(cert, NULL);
+ X509_free(cert);
+
+done:
+ if (peer_certs != NULL)
+ sk_X509_pop_free(peer_certs, X509_free);
+ if (result.IsEmpty())
+ result = Object::New(env->isolate());
+ args.GetReturnValue().Set(result);
}