diff --git a/lib/crypto.js b/lib/crypto.js index 05b19755be17..5794e045697f 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -115,6 +115,13 @@ exports.createCredentials = function(options, context) { c.context.setSessionIdContext(options.sessionIdContext); } + if (options.pfx) { + if (options.passphrase) { + c.context.loadPKCS12(options.pfx, options.passphrase); + } else { + c.context.loadPKCS12(options.pfx); + } + } return c; }; diff --git a/lib/util.js b/lib/util.js index 8de905f4dc96..185684526e87 100644 --- a/lib/util.js +++ b/lib/util.js @@ -33,12 +33,12 @@ exports.format = function(f) { var args = arguments; var len = args.length; var str = String(f).replace(formatRegExp, function(x) { + if (x == '%%') return '%'; if (i >= len) return x; switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); case '%j': return JSON.stringify(args[i++]); - case '%%': return '%'; default: return x; } diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 0403a0361432..e10d605fa983 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -44,6 +44,7 @@ # include #endif + #if OPENSSL_VERSION_NUMBER >= 0x10000000L # define OPENSSL_CONST const #else @@ -149,6 +150,7 @@ void SecureContext::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "setSessionIdContext", SecureContext::SetSessionIdContext); NODE_SET_PROTOTYPE_METHOD(t, "close", SecureContext::Close); + NODE_SET_PROTOTYPE_METHOD(t, "loadPKCS12", SecureContext::LoadPKCS12); target->Set(String::NewSymbol("SecureContext"), t->GetFunction()); } @@ -575,6 +577,104 @@ Handle SecureContext::Close(const Arguments& args) { return False(); } +//Takes .pfx or .p12 and password in string or buffer format +Handle SecureContext::LoadPKCS12(const Arguments& args) { + HandleScope scope; + + PKCS12 * p12 = NULL; + EVP_PKEY * pkey = NULL; + X509 * cert = NULL; + BIO * bio_in = NULL; + + bool ret = false; + char *pass = NULL; + + SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); + + if (args.Length() < 1) { + return ThrowException(Exception::TypeError( + String::New("Bad parameter"))); + } + + bio_in = LoadBIO(args[0]); + if (bio_in == NULL) { + return ThrowException(Exception::Error( + String::New("Unable to Load Bio"))); + } + + if (args.Length() >= 2) { + ASSERT_IS_STRING_OR_BUFFER(args[1]); + + int passlen = DecodeBytes(args[1], BINARY); + + if (passlen < 0) { + return ThrowException(Exception::TypeError( + String::New("Bad password"))); + } + pass = new char[passlen + 1]; + int pass_written = DecodeWrite(pass, passlen, args[1], BINARY); + + pass[passlen] = '\0'; + assert(pass_written == passlen); + } + + if (d2i_PKCS12_bio(bio_in, &p12)) { + if (PKCS12_parse(p12, pass, &pkey, &cert, NULL)) { + + /*Discarding additional certificates*/ + + BIO *bio_out = BIO_new(BIO_s_mem()); + if (bio_out == NULL) { + goto cleanup; + } + + if (PEM_write_bio_X509(bio_out, cert)) { + if (SSL_CTX_use_certificate_chain(sc->ctx_, bio_out) != 1) { + BIO_free(bio_out); + goto cleanup; + } + } + BIO_free(bio_out); + + bio_out = BIO_new(BIO_s_mem()); + if (bio_out == NULL) { + goto cleanup; + } + + if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) { + if(SSL_CTX_use_PrivateKey(sc->ctx_, pkey) != 1){ + BIO_free(bio_out); + goto cleanup; + } + } + BIO_free(bio_out); + ret = true; + +cleanup: + + EVP_PKEY_free(pkey); + X509_free(cert); + } + PKCS12_free(p12); + } + + if (bio_in) { + BIO_free(bio_in); + } + + if (pass) { + delete[] pass; + } + + if (!ret) { + unsigned long err = ERR_get_error(); + const char *str = ERR_reason_error_string(err); + + return ThrowException(Exception::Error(String::New(str))); + } + return True(); +} + #ifdef SSL_PRINT_DEBUG # define DEBUG_PRINT(...) fprintf (stderr, __VA_ARGS__) diff --git a/src/node_crypto.h b/src/node_crypto.h index 87a5340147c3..9d7d507b7f95 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -35,6 +35,8 @@ #include #include #include +#include + #ifdef OPENSSL_NPN_NEGOTIATED #include @@ -68,6 +70,7 @@ class SecureContext : ObjectWrap { static v8::Handle SetOptions(const v8::Arguments& args); static v8::Handle SetSessionIdContext(const v8::Arguments& args); static v8::Handle Close(const v8::Arguments& args); + static v8::Handle LoadPKCS12(const v8::Arguments& args); SecureContext() : ObjectWrap() { ctx_ = NULL; diff --git a/test/fixtures/test_cert.pfx b/test/fixtures/test_cert.pfx new file mode 100644 index 000000000000..fd28781a051e Binary files /dev/null and b/test/fixtures/test_cert.pfx differ diff --git a/test/simple/test-crypto.js b/test/simple/test-crypto.js index 800e3eb5ae13..ad347cd856d4 100644 --- a/test/simple/test-crypto.js +++ b/test/simple/test-crypto.js @@ -38,12 +38,39 @@ var path = require('path'); // Test Certificates var caPem = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii'); var certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii'); +//read into buffer +var certPfx = fs.readFileSync(common.fixturesDir + '/test_cert.pfx'); var keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); var rsaPubPem = fs.readFileSync(common.fixturesDir + '/test_rsa_pubkey.pem', 'ascii'); var rsaKeyPem = fs.readFileSync(common.fixturesDir + '/test_rsa_privkey.pem', 'ascii'); +//pfx tests +try { + var credentials = crypto.createCredentials( {pfx : certPfx, passphrase : 'sample'}); +} catch (e) { + assert.fail(); +} + +try { + var credentials = crypto.createCredentials( {pfx : certPfx}); +} catch (e) { + assert.equal(e.message, "mac verify failure"); +} + +try { + var credentials = crypto.createCredentials( {pfx : certPfx, passphrase : 'test'}); +} catch (e) { + assert.equal(e.message, "mac verify failure"); +} + +try { + var credentials = crypto.createCredentials( {pfx : 'sample', passphrase : 'test'}); +} catch (e) { + assert.equal(e.message, "not enough data"); +} + try { var credentials = crypto.createCredentials( {key: keyPem,