Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

crypto changes to support pfx(p12) format for issue #2845 #2847

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
2 changes: 1 addition & 1 deletion lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
100 changes: 100 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
# include <pthread.h>
#endif


#if OPENSSL_VERSION_NUMBER >= 0x10000000L
# define OPENSSL_CONST const
#else
Expand Down Expand Up @@ -149,6 +150,7 @@ void SecureContext::Initialize(Handle<Object> 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());
}
Expand Down Expand Up @@ -575,6 +577,104 @@ Handle<Value> SecureContext::Close(const Arguments& args) {
return False();
}

//Takes .pfx or .p12 and password in string or buffer format
Handle<Value> 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<SecureContext>(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';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Off-by-one error, pass[passlen] is outside the buffer.

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__)
Expand Down
3 changes: 3 additions & 0 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#include <openssl/x509v3.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/pkcs12.h>


#ifdef OPENSSL_NPN_NEGOTIATED
#include <node_buffer.h>
Expand Down Expand Up @@ -68,6 +70,7 @@ class SecureContext : ObjectWrap {
static v8::Handle<v8::Value> SetOptions(const v8::Arguments& args);
static v8::Handle<v8::Value> SetSessionIdContext(const v8::Arguments& args);
static v8::Handle<v8::Value> Close(const v8::Arguments& args);
static v8::Handle<v8::Value> LoadPKCS12(const v8::Arguments& args);

SecureContext() : ObjectWrap() {
ctx_ = NULL;
Expand Down
Binary file added test/fixtures/test_cert.pfx
Binary file not shown.
27 changes: 27 additions & 0 deletions test/simple/test-crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down