diff --git a/README.md b/README.md index 6a49152..177bd48 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,21 @@ x509.verify( ``` +#### x509.verifyFromStr(`certStr`, `caBundleStr`, function(err, result){ /*...*/}) + +It is the same with verify. + +```js +const x509 = require('x509'); + +x509.verify( + path.readFileSync(__dirname + '/certs/user.com.crt'), + path.readFileSync(__dirname + 'enduser-example.com.chain'), + function(err, result){ /*...*/} +); + +``` + ## Examples Checking the date to make sure the certificate is active: ```js diff --git a/include/x509.h b/include/x509.h index ce1198b..0eb05c1 100644 --- a/include/x509.h +++ b/include/x509.h @@ -24,9 +24,11 @@ NAN_METHOD(get_subject); NAN_METHOD(get_issuer); NAN_METHOD(parse_cert); NAN_METHOD(verify); +NAN_METHOD(verify_from_str); Local try_parse(const std::string& dataString); Local verify(const std::string& dataString); +Local verify_from_str(const std::string& dataString); Local parse_date(ASN1_TIME *date); Local parse_serial(ASN1_INTEGER *serial); Local parse_name(X509_NAME *subject); diff --git a/index.js b/index.js index 539a76c..35e1aff 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,32 @@ exports.getAltNames = x509.getAltNames; exports.getSubject = x509.getSubject; exports.getIssuer = x509.getIssuer; +exports.verifyFromStr = function(certStr, caBundleStr, cb) { + if (typeof cb !== 'function') { + throw new Error('cb should be function'); + } + if (certStr instanceof Buffer) { + certStr = certStr.toString(); + } else if (typeof certStr !== 'string') { + cb(new Error('certStr should be string or buffer')); + return; + } + if (caBundleStr instanceof Buffer) { + caBundleStr = caBundleStr.toString(); + } else if (typeof caBundleStr !== 'string') { + cb(new Error('caBundleStr should be string or buffer')); + return; + } + var caughtErr = null; + try { + x509.verify_from_str(certStr, caBundleStr); + } catch (verificationError) { + caughtErr = verificationError; + } finally { + cb(caughtErr); + } +}; + exports.verify = function(certPath, CABundlePath, cb) { if (!certPath) { throw new TypeError('Certificate path is required'); @@ -29,8 +55,7 @@ exports.verify = function(certPath, CABundlePath, cb) { try { x509.verify(certPath, CABundlePath); cb(null); - } - catch (verificationError) { + } catch (verificationError) { cb(verificationError); } }); diff --git a/src/addon.cc b/src/addon.cc index f0aab0c..faf0a47 100644 --- a/src/addon.cc +++ b/src/addon.cc @@ -10,6 +10,10 @@ void init(Local exports) { Nan::Set(exports, Nan::New("version").ToLocalChecked(), Nan::New(VERSION).ToLocalChecked()); + + Nan::Set(exports, + Nan::New("verify_from_str").ToLocalChecked(), + Nan::New(verify_from_str)->GetFunction()); Nan::Set(exports, Nan::New("verify").ToLocalChecked(), diff --git a/src/x509.cc b/src/x509.cc index a836301..a6fdecf 100644 --- a/src/x509.cc +++ b/src/x509.cc @@ -46,7 +46,72 @@ std::string parse_args(const Nan::FunctionCallbackInfo& info) { return *String::Utf8Value(info[0]->ToString()); } - +NAN_METHOD(verify_from_str) { + Nan::HandleScope scope; + OpenSSL_add_all_algorithms(); + std::string cert_str = *String::Utf8Value(info[0]->ToString()); + std::string ca_str = *String::Utf8Value(info[1]->ToString()); + + X509_STORE *store = NULL; + X509_STORE_CTX *verify_ctx = NULL; + X509 *ca_cert = NULL; + BIO *ca_bio = 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_mem()); + size_t ret = BIO_puts(cert_bio, cert_str.c_str()); + if (ret != cert_str.length()) { + error = "Error reading cert content"; + break; + } + cert = PEM_read_bio_X509(cert_bio, NULL, 0, NULL); + if (cert == NULL) { + error = "Failed to load cert"; + break; + } + ca_bio = BIO_new(BIO_s_mem()); + ret = BIO_puts(ca_bio, ca_str.c_str()); + if (ret != ca_str.length()) { + error = "Error reading ca content"; + break; + } + ca_cert = PEM_read_bio_X509(ca_bio, NULL, 0, NULL); + if (ca_cert == NULL) { + error = "Failed to load ca"; + break; + } + X509_STORE_CTX_init(verify_ctx, store, ca_cert, NULL); + X509_STORE_add_cert(store, cert); + ret = X509_verify_cert(verify_ctx); + if (ret < 1) { + error = X509_verify_cert_error_string(verify_ctx->error); + break; + } + } while(0); + X509_STORE_free(store); + X509_STORE_CTX_free(verify_ctx); + X509_free(ca_cert); + BIO_free_all(ca_bio); + X509_free(cert); + BIO_free_all(cert_bio); + if (error != NULL) { + Nan::ThrowError(error); + } else { + info.GetReturnValue().Set(Nan::New(true)); + } +} NAN_METHOD(verify) { Nan::HandleScope scope; diff --git a/test/test.js b/test/test.js index c98818d..2fb854c 100644 --- a/test/test.js +++ b/test/test.js @@ -53,4 +53,53 @@ x509.verify( function(err, result) { assert.throws(assert.ifError.bind(null, err), /Failed to load cert/) } -); \ No newline at end of file +); + +x509.verifyFromStr( + fs.readFileSync(path.join(__dirname, 'certs/enduser-example.com.crt')), + fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')), + function(err, result) { + assert.strictEqual(err, null) + } +); + +x509.verifyFromStr( + fs.readFileSync(path.join(__dirname, 'certs/acaline.com.crt')), + fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')), + function(err, result) { + assert.throws(assert.ifError.bind(null, err), /self signed certificate/) + } +); + +x509.verifyFromStr( + fs.readFileSync(path.join(__dirname, 'test.js')), + fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')), + function(err, result) { + assert.throws(assert.ifError.bind(null, err), /Failed to load cert/) + } +); + +x509.verifyFromStr( + fs.readFileSync(path.join(__dirname, 'certs/acaline.com.crt')), + fs.readFileSync(path.join(__dirname, 'test.js')), + function(err, result) { + assert.throws(assert.ifError.bind(null, err), /Failed to load ca/) + } +); + +x509.verifyFromStr( + 123456, + fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')), + function(err, result) { + assert.throws(assert.ifError.bind(null, err), /certStr should be string or buffer/) + } +) + +try { + x509.verifyFromStr( + fs.readFileSync(path.join(__dirname, 'certs/acaline.com.crt')), + fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')) + ) +} catch (err) { + assert.throws(assert.ifError.bind(null, err), /cb should be function/) +} \ No newline at end of file