diff --git a/.travis.yml b/.travis.yml index c45c402..37c49b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,9 @@ addons: language: node_js node_js: - "stable" + - "9.0" + - "8.0" + - "6.0" - "4.0" - "0.12" - "0.10" diff --git a/include/x509.h b/include/x509.h index ce1198b..adbbdf1 100644 --- a/include/x509.h +++ b/include/x509.h @@ -3,19 +3,20 @@ // Include header for addon version, node/v8 inclusions, etc. #include -#include #include +#include #include // OpenSSL headers #include #include +#include #include +#include #include #include -#include #include -#include +#include using namespace v8; @@ -25,13 +26,13 @@ NAN_METHOD(get_issuer); NAN_METHOD(parse_cert); NAN_METHOD(verify); -Local try_parse(const std::string& dataString); -Local verify(const std::string& dataString); +Local try_parse(const std::string &dataString); +Local verify(const std::string &dataString); Local parse_date(ASN1_TIME *date); Local parse_serial(ASN1_INTEGER *serial); Local parse_name(X509_NAME *subject); -char* real_name(char *data); -char* trim(char *data, int len); +char *real_name(char *data); +char *trim(char *data, int len); #endif diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c887769 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "x509", + "version": "0.3.3", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + } + } +} diff --git a/package.json b/package.json index 904298c..1706617 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,6 @@ }, "license": "MIT", "dependencies": { - "nan": "2.2.0" + "nan": "2.10.0" } } diff --git a/src/addon.cc b/src/addon.cc index f0aab0c..e4c8f2d 100644 --- a/src/addon.cc +++ b/src/addon.cc @@ -1,5 +1,5 @@ -#include #include +#include #include #include @@ -7,26 +7,20 @@ using namespace v8; void init(Local exports) { - Nan::Set(exports, - Nan::New("version").ToLocalChecked(), - Nan::New(VERSION).ToLocalChecked()); + Nan::Set(exports, Nan::New("version").ToLocalChecked(), + Nan::New(VERSION).ToLocalChecked()); - Nan::Set(exports, - Nan::New("verify").ToLocalChecked(), - Nan::New(verify)->GetFunction()); + Nan::Set(exports, Nan::New("verify").ToLocalChecked(), + Nan::New(verify)->GetFunction()); - Nan::Set(exports, - Nan::New("getAltNames").ToLocalChecked(), - Nan::New(get_altnames)->GetFunction()); - Nan::Set(exports, - Nan::New("getSubject").ToLocalChecked(), - Nan::New(get_subject)->GetFunction()); - Nan::Set(exports, - Nan::New("getIssuer").ToLocalChecked(), - Nan::New(get_issuer)->GetFunction()); - Nan::Set(exports, - Nan::New("parseCert").ToLocalChecked(), - Nan::New(parse_cert)->GetFunction()); + Nan::Set(exports, Nan::New("getAltNames").ToLocalChecked(), + Nan::New(get_altnames)->GetFunction()); + Nan::Set(exports, Nan::New("getSubject").ToLocalChecked(), + Nan::New(get_subject)->GetFunction()); + Nan::Set(exports, Nan::New("getIssuer").ToLocalChecked(), + Nan::New(get_issuer)->GetFunction()); + Nan::Set(exports, Nan::New("parseCert").ToLocalChecked(), + Nan::New(parse_cert)->GetFunction()); } NODE_MODULE(x509, init) diff --git a/src/x509.cc b/src/x509.cc index a836301..9b121d4 100644 --- a/src/x509.cc +++ b/src/x509.cc @@ -1,482 +1,488 @@ +#include #include #include -#include using namespace v8; // Field names that OpenSSL is missing. static const char *MISSING[4][2] = { - { - "1.2.840.113533.7.65.0", - "entrustVersionInfo" - }, - - { - "1.3.6.1.4.1.311.60.2.1.1", - "jurisdictionOfIncorpationLocalityName" - }, - - { - "1.3.6.1.4.1.311.60.2.1.2", - "jurisdictionOfIncorporationStateOrProvinceName" - }, - - { - "1.3.6.1.4.1.311.60.2.1.3", - "jurisdictionOfIncorporationCountryName" - } -}; - -std::string parse_args(const Nan::FunctionCallbackInfo& info) { - if (info.Length() == 0) { - Nan::ThrowTypeError("Must provide a certificate string."); - return std::string(); - } - - if (!info[0]->IsString()) { - Nan::ThrowTypeError("Certificate must be a string."); - return std::string(); - } - - if (info[0]->ToString()->Length() == 0) { - Nan::ThrowTypeError("Certificate argument provided, but left blank."); - return std::string(); - } - - return *String::Utf8Value(info[0]->ToString()); -} + {"1.2.840.113533.7.65.0", "entrustVersionInfo"}, + {"1.3.6.1.4.1.311.60.2.1.1", "jurisdictionOfIncorpationLocalityName"}, + {"1.3.6.1.4.1.311.60.2.1.2", + "jurisdictionOfIncorporationStateOrProvinceName"}, -NAN_METHOD(verify) { - Nan::HandleScope scope; - OpenSSL_add_all_algorithms(); - - std::string cert_path = *String::Utf8Value(info[0]->ToString()); - std::string ca_bundlestr = *String::Utf8Value(info[1]->ToString()); - - X509_STORE *store = NULL; - X509_STORE_CTX *verify_ctx = NULL; - X509 *cert = NULL; - BIO *cert_bio = NULL; - const char *error = NULL; - - do { - store = X509_STORE_new(); - if (store == NULL) { - error = "Failed to create X509 certificate store."; - break; - } - verify_ctx = X509_STORE_CTX_new(); - if (verify_ctx == NULL) { - error = "Failed to create X509 verification context."; - break; - } - cert_bio = BIO_new(BIO_s_file()); - int ret = BIO_read_filename(cert_bio, cert_path.c_str()); - if (ret != 1) { - error = "Error reading file"; - break; - } - cert = PEM_read_bio_X509(cert_bio, NULL, 0, NULL); - if (cert == NULL) { - error = "Failed to load cert"; - break; + {"1.3.6.1.4.1.311.60.2.1.3", "jurisdictionOfIncorporationCountryName"}}; + +std::string parse_args(const Nan::FunctionCallbackInfo &info) { + if (info.Length() == 0) { + Nan::ThrowTypeError("Must provide a certificate string."); + return std::string(); } - ret = X509_STORE_load_locations(store, ca_bundlestr.c_str(), NULL); - if (ret != 1) { - error = "Error loading CA chain file"; - break; + + if (!info[0]->IsString()) { + Nan::ThrowTypeError("Certificate must be a string."); + return std::string(); } - X509_STORE_CTX_init(verify_ctx, store, cert, NULL); - ret = X509_verify_cert(verify_ctx); - if (ret <= 0) { - error = X509_verify_cert_error_string(verify_ctx->error); - break; + + if (info[0]->ToString()->Length() == 0) { + Nan::ThrowTypeError("Certificate argument provided, but left blank."); + return std::string(); } - } while(0); - - X509_STORE_free(store); - X509_free(cert); - X509_STORE_CTX_free(verify_ctx); - BIO_free_all(cert_bio); - if (error != NULL) { - Nan::ThrowError(error); - } else { - info.GetReturnValue().Set(Nan::New(true)); - } + + return *Nan::Utf8String(info[0]->ToString()); } +NAN_METHOD(verify) { + Nan::HandleScope scope; + OpenSSL_add_all_algorithms(); + + std::string cert_path = *Nan::Utf8String(info[0]->ToString()); + std::string ca_bundlestr = *Nan::Utf8String(info[1]->ToString()); + + X509_STORE *store = NULL; + X509_STORE_CTX *verify_ctx = NULL; + X509 *cert = NULL; + BIO *cert_bio = NULL; + const char *error = NULL; + + do { + store = X509_STORE_new(); + if (store == NULL) { + error = "Failed to create X509 certificate store."; + break; + } + verify_ctx = X509_STORE_CTX_new(); + if (verify_ctx == NULL) { + error = "Failed to create X509 verification context."; + break; + } + cert_bio = BIO_new(BIO_s_file()); + int ret = BIO_read_filename(cert_bio, cert_path.c_str()); + if (ret != 1) { + error = "Error reading file"; + break; + } + cert = PEM_read_bio_X509(cert_bio, NULL, 0, NULL); + if (cert == NULL) { + error = "Failed to load cert"; + break; + } + ret = X509_STORE_load_locations(store, ca_bundlestr.c_str(), NULL); + if (ret != 1) { + error = "Error loading CA chain file"; + break; + } + X509_STORE_CTX_init(verify_ctx, store, cert, NULL); + ret = X509_verify_cert(verify_ctx); + if (ret <= 0) { + error = X509_verify_cert_error_string( + X509_STORE_CTX_get_error(verify_ctx)); + break; + } + } while (0); + X509_STORE_free(store); + X509_free(cert); + X509_STORE_CTX_free(verify_ctx); + BIO_free_all(cert_bio); + if (error != NULL) { + Nan::ThrowError(error); + } else { + info.GetReturnValue().Set(Nan::New(true)); + } +} NAN_METHOD(get_altnames) { - Nan::HandleScope scope; - std::string parsed_arg = parse_args(info); - if(parsed_arg.size() == 0) { - info.GetReturnValue().SetUndefined(); - } - Local exports(try_parse(parsed_arg)->ToObject()); - Local key = Nan::New("altNames").ToLocalChecked(); - info.GetReturnValue().Set( - Nan::Get(exports, key).ToLocalChecked()); - ERR_clear_error(); + Nan::HandleScope scope; + std::string parsed_arg = parse_args(info); + if (parsed_arg.size() == 0) { + info.GetReturnValue().SetUndefined(); + } + Local exports(try_parse(parsed_arg)->ToObject()); + Local key = Nan::New("altNames").ToLocalChecked(); + info.GetReturnValue().Set(Nan::Get(exports, key).ToLocalChecked()); + ERR_clear_error(); } NAN_METHOD(get_subject) { - Nan::HandleScope scope; - std::string parsed_arg = parse_args(info); - if(parsed_arg.size() == 0) { - info.GetReturnValue().SetUndefined(); - } - Local exports(try_parse(parsed_arg)->ToObject()); - Local key = Nan::New("subject").ToLocalChecked(); - info.GetReturnValue().Set( - Nan::Get(exports, key).ToLocalChecked()); - ERR_clear_error(); + Nan::HandleScope scope; + std::string parsed_arg = parse_args(info); + if (parsed_arg.size() == 0) { + info.GetReturnValue().SetUndefined(); + } + Local exports(try_parse(parsed_arg)->ToObject()); + Local key = Nan::New("subject").ToLocalChecked(); + info.GetReturnValue().Set(Nan::Get(exports, key).ToLocalChecked()); + ERR_clear_error(); } NAN_METHOD(get_issuer) { - Nan::HandleScope scope; - std::string parsed_arg = parse_args(info); - if(parsed_arg.size() == 0) { - info.GetReturnValue().SetUndefined(); - } - Local exports(try_parse(parsed_arg)->ToObject()); - Local key = Nan::New("issuer").ToLocalChecked(); - info.GetReturnValue().Set( - Nan::Get(exports, key).ToLocalChecked()); - ERR_clear_error(); + Nan::HandleScope scope; + std::string parsed_arg = parse_args(info); + if (parsed_arg.size() == 0) { + info.GetReturnValue().SetUndefined(); + } + Local exports(try_parse(parsed_arg)->ToObject()); + Local key = Nan::New("issuer").ToLocalChecked(); + info.GetReturnValue().Set(Nan::Get(exports, key).ToLocalChecked()); + ERR_clear_error(); } NAN_METHOD(parse_cert) { - Nan::HandleScope scope; - std::string parsed_arg = parse_args(info); - if(parsed_arg.size() == 0) { - info.GetReturnValue().SetUndefined(); - } - Local exports(try_parse(parsed_arg)->ToObject()); - info.GetReturnValue().Set(exports); - ERR_clear_error(); + Nan::HandleScope scope; + std::string parsed_arg = parse_args(info); + if (parsed_arg.size() == 0) { + info.GetReturnValue().SetUndefined(); + } + + Local exports(try_parse(parsed_arg)->ToObject()); + info.GetReturnValue().Set(exports); + ERR_clear_error(); } /* * This is where everything is handled for both -0.11.2 and 0.11.3+. */ -Local try_parse(const std::string& dataString) { - Nan::EscapableHandleScope scope; - const char* data = dataString.c_str(); - - Local exports = Nan::New(); - X509 *cert; - - BIO *bio = BIO_new(BIO_s_mem()); - int result = BIO_puts(bio, data); - - if (result == -2) { - Nan::ThrowError("BIO doesn't support BIO_puts."); - BIO_free(bio); - return scope.Escape(exports); - } - else if (result <= 0) { - Nan::ThrowError("No data was written to BIO."); - BIO_free(bio); - return scope.Escape(exports); - } - - // Try raw read - cert = PEM_read_bio_X509(bio, NULL, 0, NULL); - - if (cert == NULL) { - BIO_free_all(bio); - // Switch to file BIO - bio = BIO_new(BIO_s_file()); - - // If raw read fails, try reading the input as a filename. - if (!BIO_read_filename(bio, data)) { - ERR_clear_error(); - Nan::ThrowError("File doesn't exist."); - BIO_free(bio); - return scope.Escape(exports); +Local try_parse(const std::string &dataString) { + Nan::EscapableHandleScope scope; + const char *data = dataString.c_str(); + + Local exports = Nan::New(); + X509 *cert; + + BIO *bio = BIO_new(BIO_s_mem()); + int result = BIO_puts(bio, data); + + if (result == -2) { + Nan::ThrowError("BIO doesn't support BIO_puts."); + BIO_free(bio); + return scope.Escape(exports); + } else if (result <= 0) { + Nan::ThrowError("No data was written to BIO."); + BIO_free(bio); + return scope.Escape(exports); } - // Try reading the bio again with the file in it. + // Try raw read cert = PEM_read_bio_X509(bio, NULL, 0, NULL); if (cert == NULL) { - ERR_clear_error(); - Nan::ThrowError("Unable to parse certificate."); - BIO_free(bio); - return scope.Escape(exports); - } - } - - Nan::Set(exports, - Nan::New("version").ToLocalChecked(), - Nan::New((int) X509_get_version(cert))); - Nan::Set(exports, - Nan::New("subject").ToLocalChecked(), - parse_name(X509_get_subject_name(cert))); - Nan::Set(exports, - Nan::New("issuer").ToLocalChecked(), - parse_name(X509_get_issuer_name(cert))); - Nan::Set(exports, - Nan::New("serial").ToLocalChecked(), - parse_serial(X509_get_serialNumber(cert))); - Nan::Set(exports, - Nan::New("notBefore").ToLocalChecked(), - parse_date(X509_get_notBefore(cert))); - Nan::Set(exports, - Nan::New("notAfter").ToLocalChecked(), - parse_date(X509_get_notAfter(cert))); - - // Subject hash - std::stringstream stream; - stream << std::hex << X509_subject_name_hash(cert); - Nan::Set(exports, - Nan::New("subjectHash").ToLocalChecked(), - Nan::New(stream.str()).ToLocalChecked()); - - // Signature Algorithm - int sig_alg_nid = OBJ_obj2nid(cert->sig_alg->algorithm); - if (sig_alg_nid == NID_undef) { - ERR_clear_error(); - Nan::ThrowError("unable to find specified signature algorithm name."); - X509_free(cert); - BIO_free(bio); - return scope.Escape(exports); - } - Nan::Set(exports, - Nan::New("signatureAlgorithm").ToLocalChecked(), - Nan::New(OBJ_nid2ln(sig_alg_nid)).ToLocalChecked()); - - // fingerPrint - unsigned int md_size, idx; - 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]; - for (idx = 0; idx < md_size; idx++) { - fingerprint[3*idx] = hex[(md[idx] & 0xf0) >> 4]; - fingerprint[(3*idx)+1] = hex[(md[idx] & 0x0f)]; - fingerprint[(3*idx)+2] = ':'; - } + BIO_free_all(bio); + // Switch to file BIO + bio = BIO_new(BIO_s_file()); + + // If raw read fails, try reading the input as a filename. + if (!BIO_read_filename(bio, data)) { + ERR_clear_error(); + Nan::ThrowError("File doesn't exist."); + BIO_free(bio); + return scope.Escape(exports); + } - if (md_size > 0) { - fingerprint[(3*(md_size-1))+2] = '\0'; - } else { - fingerprint[0] = '\0'; + // Try reading the bio again with the file in it. + cert = PEM_read_bio_X509(bio, NULL, 0, NULL); + + if (cert == NULL) { + ERR_clear_error(); + Nan::ThrowError("Unable to parse certificate."); + BIO_free(bio); + return scope.Escape(exports); + } } - Nan::Set(exports, - Nan::New("fingerPrint").ToLocalChecked(), - Nan::New(fingerprint).ToLocalChecked()); - } - - // public key - int pkey_nid = OBJ_obj2nid(cert->cert_info->key->algor->algorithm); - if (pkey_nid == NID_undef) { - ERR_clear_error(); - Nan::ThrowError("unable to find specified public key algorithm name."); - X509_free(cert); - BIO_free(bio); - return scope.Escape(exports); - } - EVP_PKEY *pkey = X509_get_pubkey(cert); - Local publicKey = Nan::New(); - Nan::Set(publicKey, - Nan::New("algorithm").ToLocalChecked(), - Nan::New(OBJ_nid2ln(pkey_nid)).ToLocalChecked()); - - if (pkey_nid == NID_rsaEncryption) { - char *rsa_e_dec, *rsa_n_hex; - uint32_t rsa_key_length_int; - RSA *rsa_key; - rsa_key = pkey->pkey.rsa; - rsa_e_dec = BN_bn2dec(rsa_key->e); - rsa_n_hex = BN_bn2hex(rsa_key->n); - rsa_key_length_int = RSA_size(rsa_key) * 8; - Nan::Set(publicKey, - Nan::New("e").ToLocalChecked(), - Nan::New(rsa_e_dec).ToLocalChecked()); - OPENSSL_free(rsa_e_dec); - Nan::Set(publicKey, - Nan::New("n").ToLocalChecked(), - Nan::New(rsa_n_hex).ToLocalChecked()); - OPENSSL_free(rsa_n_hex); - Nan::Set(publicKey, - Nan::New("bitSize").ToLocalChecked(), - Nan::New(rsa_key_length_int)); - } - Nan::Set(exports, Nan::New("publicKey").ToLocalChecked(), publicKey); - EVP_PKEY_free(pkey); - - // alt names - Local altNames(Nan::New()); - STACK_OF(GENERAL_NAME) *names = NULL; - int i; - - names = (STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - - if (names != NULL) { - int length = sk_GENERAL_NAME_num(names); - for (i = 0; i < length; i++) { - GENERAL_NAME *current = sk_GENERAL_NAME_value(names, i); - if (current->type == GEN_DNS) { - char *name = (char*) ASN1_STRING_data(current->d.dNSName); + Nan::Set(exports, Nan::New("version").ToLocalChecked(), + Nan::New((int)X509_get_version(cert))); + Nan::Set(exports, Nan::New("subject").ToLocalChecked(), + parse_name(X509_get_subject_name(cert))); + Nan::Set(exports, Nan::New("issuer").ToLocalChecked(), + parse_name(X509_get_issuer_name(cert))); + Nan::Set(exports, Nan::New("serial").ToLocalChecked(), + parse_serial(X509_get_serialNumber(cert))); + Nan::Set(exports, Nan::New("notBefore").ToLocalChecked(), + parse_date(X509_get_notBefore(cert))); + Nan::Set(exports, Nan::New("notAfter").ToLocalChecked(), + parse_date(X509_get_notAfter(cert))); + // Subject hash + std::stringstream stream; + stream << std::hex << X509_subject_name_hash(cert); + Nan::Set(exports, Nan::New("subjectHash").ToLocalChecked(), + Nan::New(stream.str()).ToLocalChecked()); + + // Signature Algorithm +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int sig_alg_nid = X509_get_signature_nid(cert); +#else + int sig_alg_nid = OBJ_obj2nid(cert->sig_alg->algorithm); +#endif + if (sig_alg_nid == NID_undef) { + ERR_clear_error(); + Nan::ThrowError("unable to find specified signature algorithm name."); + X509_free(cert); + BIO_free(bio); + return scope.Escape(exports); + } + Nan::Set(exports, Nan::New("signatureAlgorithm").ToLocalChecked(), + Nan::New(OBJ_nid2ln(sig_alg_nid)).ToLocalChecked()); + + // fingerPrint + unsigned int md_size, idx; + 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]; + for (idx = 0; idx < md_size; idx++) { + fingerprint[3 * idx] = hex[(md[idx] & 0xf0) >> 4]; + fingerprint[(3 * idx) + 1] = hex[(md[idx] & 0x0f)]; + fingerprint[(3 * idx) + 2] = ':'; + } - if (ASN1_STRING_length(current->d.dNSName) != (int) strlen(name)) { - ERR_clear_error(); - Nan::ThrowError("Malformed alternative names field."); - X509_free(cert); - BIO_free(bio); - return scope.Escape(exports); + if (md_size > 0) { + fingerprint[(3 * (md_size - 1)) + 2] = '\0'; + } else { + fingerprint[0] = '\0'; } - Nan::Set(altNames, i, Nan::New(name).ToLocalChecked()); - } + Nan::Set(exports, Nan::New("fingerPrint").ToLocalChecked(), + Nan::New(fingerprint).ToLocalChecked()); } - sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); - } - Nan::Set(exports, Nan::New("altNames").ToLocalChecked(), altNames); - - // Extensions - Local extensions(Nan::New()); - STACK_OF(X509_EXTENSION) *exts = cert->cert_info->extensions; - int num_of_exts; - int index_of_exts; - if (exts) { - num_of_exts = sk_X509_EXTENSION_num(exts); - } else { - num_of_exts = 0; - } - - // IFNEG_FAIL(num_of_exts, "error parsing number of X509v3 extensions."); - - for (index_of_exts = 0; index_of_exts < num_of_exts; index_of_exts++) { - X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, index_of_exts); - // IFNULL_FAIL(ext, "unable to extract extension from stack"); - ASN1_OBJECT *obj = X509_EXTENSION_get_object(ext); - // IFNULL_FAIL(obj, "unable to extract ASN1 object from extension"); - - BIO *ext_bio = BIO_new(BIO_s_mem()); - // IFNULL_FAIL(ext_bio, "unable to allocate memory for extension value BIO"); - if (!X509V3_EXT_print(ext_bio, ext, 0, 0)) { - M_ASN1_OCTET_STRING_print(ext_bio, ext->value); + // public key +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int pkey_nid = X509_get_signature_nid(cert); +#else + int pkey_nid = OBJ_obj2nid(cert->cert_info->key->algor->algorithm); +#endif + if (pkey_nid == NID_undef) { + ERR_clear_error(); + Nan::ThrowError("unable to find specified public key algorithm name."); + X509_free(cert); + BIO_free(bio); + return scope.Escape(exports); } + EVP_PKEY *pkey = X509_get_pubkey(cert); + Local publicKey = Nan::New(); + Nan::Set(publicKey, Nan::New("algorithm").ToLocalChecked(), + Nan::New(OBJ_nid2ln(pkey_nid)).ToLocalChecked()); + + if (pkey_nid == NID_rsaEncryption) { + char *rsa_e_dec, *rsa_n_hex; + uint32_t rsa_key_length_int; + RSA *rsa_key; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + rsa_key = EVP_PKEY_get1_RSA(pkey); + const BIGNUM *n; + const BIGNUM *e; + RSA_get0_key(rsa_key, &n, &e, NULL); + rsa_e_dec = BN_bn2dec(e); + rsa_n_hex = BN_bn2hex(n); +#else + rsa_key = pkey->pkey.rsa; + rsa_e_dec = BN_bn2dec(rsa_key->e); + rsa_n_hex = BN_bn2hex(rsa_key->n); +#endif + rsa_key_length_int = RSA_size(rsa_key) * 8; + Nan::Set(publicKey, Nan::New("e").ToLocalChecked(), + Nan::New(rsa_e_dec).ToLocalChecked()); + OPENSSL_free(rsa_e_dec); + Nan::Set(publicKey, Nan::New("n").ToLocalChecked(), + Nan::New(rsa_n_hex).ToLocalChecked()); + OPENSSL_free(rsa_n_hex); + Nan::Set(publicKey, Nan::New("bitSize").ToLocalChecked(), + Nan::New(rsa_key_length_int)); + } + Nan::Set(exports, Nan::New("publicKey").ToLocalChecked(), + publicKey); + EVP_PKEY_free(pkey); + + // alt names + Local altNames(Nan::New()); + STACK_OF(GENERAL_NAME) *names = NULL; + int i; + + names = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i( + cert, NID_subject_alt_name, NULL, NULL); + + if (names != NULL) { + int length = sk_GENERAL_NAME_num(names); + for (i = 0; i < length; i++) { + GENERAL_NAME *current = sk_GENERAL_NAME_value(names, i); + + if (current->type == GEN_DNS) { + char *name = NULL; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + name = (char *)ASN1_STRING_get0_data(current->d.dNSName); +#else + name = (char *)ASN1_STRING_data(current->d.dNSName); +#endif + + if (ASN1_STRING_length(current->d.dNSName) != + (int)strlen(name)) { + ERR_clear_error(); + Nan::ThrowError("Malformed alternative names field."); + X509_free(cert); + BIO_free(bio); + return scope.Escape(exports); + } + Nan::Set(altNames, i, Nan::New(name).ToLocalChecked()); + } + } + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + } + Nan::Set(exports, Nan::New("altNames").ToLocalChecked(), altNames); + + // Extensions + Local extensions(Nan::New()); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const STACK_OF(X509_EXTENSION) *exts = X509_get0_extensions(cert); +#else + STACK_OF(X509_EXTENSION) *exts = cert->cert_info->extensions; +#endif + + int num_of_exts = X509v3_get_ext_count(exts); + int index_of_exts; + + // IFNEG_FAIL(num_of_exts, "error parsing number of X509v3 extensions."); + + for (index_of_exts = 0; index_of_exts < num_of_exts; index_of_exts++) { + X509_EXTENSION *ext = X509v3_get_ext(exts, index_of_exts); + ASN1_OBJECT *obj = X509_EXTENSION_get_object(ext); + + // IFNULL_FAIL(obj, "unable to extract ASN1 object from extension"); + + BIO *ext_bio = BIO_new(BIO_s_mem()); + // IFNULL_FAIL(ext_bio, "unable to allocate memory for extension value + // BIO"); + if (!X509V3_EXT_print(ext_bio, ext, 0, 0)) { + unsigned char *buf = NULL; + int len = i2d_ASN1_OCTET_STRING(X509_EXTENSION_get_data(ext), &buf); + if (len >= 0) { + BIO_write(ext_bio, static_cast(buf), len); + } + } - BUF_MEM *bptr; - BIO_get_mem_ptr(ext_bio, &bptr); - BIO_set_close(ext_bio, BIO_CLOSE); - - char *data = new char[bptr->length + 1]; - BUF_strlcpy(data, bptr->data, bptr->length + 1); - char *trimmed_data = trim(data, bptr->length); - - BIO_free(ext_bio); + BUF_MEM *bptr; + BIO_get_mem_ptr(ext_bio, &bptr); - unsigned nid = OBJ_obj2nid(obj); - if (nid == NID_undef) { - char extname[100]; - OBJ_obj2txt(extname, 100, (const ASN1_OBJECT *) obj, 1); - Nan::Set(extensions, - Nan::New(real_name(extname)).ToLocalChecked(), - Nan::New(trimmed_data).ToLocalChecked()); + char *data = new char[bptr->length + 1]; + char *trimmed_data; + if (bptr->data == NULL) { + trimmed_data = (char *)""; + } else { + BUF_strlcpy(data, bptr->data, bptr->length + 1); + trimmed_data = trim(data, bptr->length); + } + BIO_free(ext_bio); + + unsigned nid = OBJ_obj2nid(obj); + + if (nid == NID_undef) { + char extname[100]; + OBJ_obj2txt(extname, 100, (const ASN1_OBJECT *)obj, 1); + Nan::Set(extensions, + Nan::New(real_name(extname)).ToLocalChecked(), + Nan::New(trimmed_data).ToLocalChecked()); + } else { + const char *c_ext_name = OBJ_nid2ln(nid); + // IFNULL_FAIL(c_ext_name, "invalid X509v3 extension name"); + Nan::Set(extensions, + Nan::New(real_name((char *)c_ext_name)) + .ToLocalChecked(), + Nan::New(trimmed_data).ToLocalChecked()); + } - } else { - const char *c_ext_name = OBJ_nid2ln(nid); - // IFNULL_FAIL(c_ext_name, "invalid X509v3 extension name"); - Nan::Set(extensions, - Nan::New(real_name((char*)c_ext_name)).ToLocalChecked(), - Nan::New(trimmed_data).ToLocalChecked()); + delete[] data; } - delete[] data; - } - Nan::Set(exports, - Nan::New("extensions").ToLocalChecked(), extensions); - ERR_clear_error(); - X509_free(cert); - BIO_free(bio); + Nan::Set(exports, Nan::New("extensions").ToLocalChecked(), + extensions); - return scope.Escape(exports); + ERR_clear_error(); + X509_free(cert); + BIO_free(bio); + + return scope.Escape(exports); } Local parse_serial(ASN1_INTEGER *serial) { - Nan::EscapableHandleScope scope; - Local serialNumber; - BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL); - char *hex = BN_bn2hex(bn); - - serialNumber = Nan::New(hex).ToLocalChecked(); - BN_free(bn); - OPENSSL_free(hex); - return scope.Escape(serialNumber); + Nan::EscapableHandleScope scope; + Local serialNumber; + BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL); + char *hex = BN_bn2hex(bn); + + serialNumber = Nan::New(hex).ToLocalChecked(); + BN_free(bn); + OPENSSL_free(hex); + return scope.Escape(serialNumber); } Local parse_date(ASN1_TIME *date) { - Nan::EscapableHandleScope scope; - BIO *bio; - BUF_MEM *bm; - char formatted[64]; - Local args[1]; - - formatted[0] = '\0'; - bio = BIO_new(BIO_s_mem()); - ASN1_TIME_print(bio, date); - BIO_get_mem_ptr(bio, &bm); - BUF_strlcpy(formatted, bm->data, bm->length + 1); - BIO_free(bio); - args[0] = Nan::New(formatted).ToLocalChecked(); - - Local global = Nan::GetCurrentContext()->Global(); - Local DateObject = Nan::Get(global, - Nan::New("Date").ToLocalChecked()).ToLocalChecked()->ToObject(); - return scope.Escape(DateObject->CallAsConstructor(1, args)); + Nan::EscapableHandleScope scope; + BUF_MEM *bm; + char formatted[64]; + Local args[1]; + formatted[0] = '\0'; + BIO *bio = BIO_new(BIO_s_mem()); + ASN1_TIME_print(bio, date); + BIO_get_mem_ptr(bio, &bm); + BUF_strlcpy(formatted, bm->data, bm->length + 1); + BIO_free(bio); + args[0] = Nan::New(formatted).ToLocalChecked(); + Local global = Nan::GetCurrentContext()->Global(); + Local DateObject = + Nan::Get(global, Nan::New("Date").ToLocalChecked()) + .ToLocalChecked() + ->ToObject(); + return scope.Escape( + Nan::CallAsConstructor(DateObject, 1, args).ToLocalChecked()); } Local parse_name(X509_NAME *subject) { - Nan::EscapableHandleScope scope; - Local cert = Nan::New(); - int i, length; - ASN1_OBJECT *entry; - unsigned char *value; - char buf[255]; - length = X509_NAME_entry_count(subject); - for (i = 0; i < length; i++) { - entry = X509_NAME_ENTRY_get_object(X509_NAME_get_entry(subject, i)); - OBJ_obj2txt(buf, 255, entry, 0); - value = ASN1_STRING_data(X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i))); - Nan::Set(cert, - Nan::New(real_name(buf)).ToLocalChecked(), - Nan::New((const char*) value).ToLocalChecked()); - } - return scope.Escape(cert); + Nan::EscapableHandleScope scope; + Local cert = Nan::New(); + int i, length; + ASN1_OBJECT *entry; + const unsigned char *value; + char buf[255]; + length = X509_NAME_entry_count(subject); + for (i = 0; i < length; i++) { + entry = X509_NAME_ENTRY_get_object(X509_NAME_get_entry(subject, i)); + OBJ_obj2txt(buf, 255, entry, 0); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + value = ASN1_STRING_get0_data( + X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i))); +#else + value = ASN1_STRING_data( + X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i))); +#endif + + Nan::Set(cert, Nan::New(real_name(buf)).ToLocalChecked(), + Nan::New((const char *)value).ToLocalChecked()); + } + return scope.Escape(cert); } // Fix for missing fields in OpenSSL. -char* real_name(char *data) { - int i, length = (int) sizeof(MISSING) / sizeof(MISSING[0]); +char *real_name(char *data) { + int i, length = (int)sizeof(MISSING) / sizeof(MISSING[0]); - for (i = 0; i < length; i++) { - if (strcmp(data, MISSING[i][0]) == 0) - return (char*) MISSING[i][1]; - } + for (i = 0; i < length; i++) { + if (strcmp(data, MISSING[i][0]) == 0) return (char *)MISSING[i][1]; + } - return data; + return data; } -char* trim(char *data, int len) { - if (data[0] == '\n' || data[0] == '\r') { - data = data+1; - } - else if (len > 1 && (data[len-1] == '\n' || data[len-1] == '\r')) { - data[len-1] = (char) 0; - } - else if (len > 0 && (data[len] == '\n' || data[len] == '\r')) { - data[len] = (char) 0; - } - else { - return data; - } +char *trim(char *data, int len) { + if (data[0] == '\n' || data[0] == '\r') { + data = data + 1; + } else if (len > 1 && (data[len - 1] == '\n' || data[len - 1] == '\r')) { + data[len - 1] = (char)0; + } else if (len > 0 && (data[len] == '\n' || data[len] == '\r')) { + data[len] = (char)0; + } else { + return data; + } - return trim(data, len - 1); + return trim(data, len - 1); }