Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Crypto: support of .pfx or .p12

fixes #2845
  • Loading branch information...
commit 876618671ef5149e4a78039e8044f8e23dff4a16 1 parent a475e62
ssuda authored
View
2  doc/api/crypto.markdown
@@ -14,8 +14,10 @@ It also offers a set of wrappers for OpenSSL's hash, hmac, cipher, decipher, sig
Creates a credentials object, with the optional details being a dictionary with keys:
+* `pfx` : A string or buffer holding the PFX or PKCS12 encoded private key, certificate and CA certificates
* `key` : a string holding the PEM encoded private key
* `cert` : a string holding the PEM encoded certificate
+* `passphrase` : A string of passphrase for the private key or pfx
* `ca` : either a string or list of strings of PEM encoded CA certificates to trust.
* `ciphers`: a string describing the ciphers to use or exclude. Consult
<http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT> for details
View
15 doc/api/https.markdown
@@ -32,7 +32,19 @@ Example:
res.end("hello world\n");
}).listen(8000);
+Or
+ var https = require('https');
+ var fs = require('fs');
+
+ var options = {
+ pfx: fs.readFileSync('server.pfx')
+ };
+
+ https.createServer(options, function (req, res) {
+ res.writeHead(200);
+ res.end("hello world\n");
+ }).listen(8000);
## https.request(options, callback)
Makes a request to a secure web server.
@@ -91,8 +103,9 @@ The options argument has the following options
The following options from [tls.connect()](tls.html#tls.connect) can also be
specified. However, a [globalAgent](#https.globalAgent) silently ignores these.
+- `pfx`: Certificate, Private key and CA certificates to use for SSL. Default `null`.
- `key`: Private key to use for SSL. Default `null`.
-- `passphrase`: A string of passphrase for the private key. Default `null`.
+- `passphrase`: A string of passphrase for the private key or pfx. Default `null`.
- `cert`: Public x509 certificate to use. Default `null`.
- `ca`: An authority certificate or array of authority certificates to check
the remote host against.
View
63 doc/api/tls.markdown
@@ -28,6 +28,15 @@ Alternatively you can send the CSR to a Certificate Authority for signing.
(TODO: docs on creating a CA, for now interested users should just look at
`test/fixtures/keys/Makefile` in the Node source code)
+To create .pfx or .p12, do this:
+
+ openssl pkcs12 -export -in agent5-cert.pem -inkey agent5-key.pem -certfile ca-cert.pem -out agent5.pfx
+
+ - `in`: certificate
+ - `inkey`: private key
+ - `certfile`: all CA certs concatenated in one file like `cat ca1-cert.pem ca2-cert.pem > ca-cert.pem`
+
+
## Client-initiated renegotiation attack mitigation
<!-- type=misc -->
@@ -72,10 +81,13 @@ The `connectionListener` argument is automatically set as a listener for the
[secureConnection](#event_secureConnection_) event.
The `options` object has these possibilities:
+ - `pfx`: A string or `Buffer` containing the private key, certificate and CA certs of the server in
+ PFX or PKCS12 format. (Required)
+
- `key`: A string or `Buffer` containing the private key of the server in
PEM format. (Required)
- - `passphrase`: A string of passphrase for the private key.
+ - `passphrase`: A string of passphrase for the private key or pfx.
- `cert`: A string or `Buffer` containing the certificate key of the server in
PEM format. (Required)
@@ -137,7 +149,29 @@ Here is a simple example echo server:
console.log('server bound');
});
+Or
+
+ var tls = require('tls');
+ var fs = require('fs');
+
+ var options = {
+ pfx: fs.readFileSync('server.pfx'),
+ // This is necessary only if using the client certificate authentication.
+ requestCert: true,
+
+ };
+
+ var server = tls.createServer(options, function(cleartextStream) {
+ console.log('server connected',
+ cleartextStream.authorized ? 'authorized' : 'unauthorized');
+ cleartextStream.write("welcome!\n");
+ cleartextStream.setEncoding('utf8');
+ cleartextStream.pipe(cleartextStream);
+ });
+ server.listen(8000, function() {
+ console.log('server bound');
+ });
You can test this server by connecting to it with `openssl s_client`:
@@ -149,10 +183,13 @@ You can test this server by connecting to it with `openssl s_client`:
Creates a new client connection to the given `port` and `host`. (If `host`
defaults to `localhost`.) `options` should be an object which specifies
+ - `pfx`: A string or `Buffer` containing the private key, certificate and CA certs of the server in
+ PFX or PKCS12 format.
+
- `key`: A string or `Buffer` containing the private key of the client in
PEM format.
- - `passphrase`: A string of passphrase for the private key.
+ - `passphrase`: A string of passphrase for the private key or pfx.
- `cert`: A string or `Buffer` containing the certificate key of the client in
PEM format.
@@ -206,6 +243,28 @@ Here is an example of a client of echo server as described previously:
server.close();
});
+Or
+
+ var tls = require('tls');
+ var fs = require('fs');
+
+ var options = {
+ pfx: fs.readFileSync('client.pfx')
+ };
+
+ var cleartextStream = tls.connect(8000, options, function() {
+ console.log('client connected',
+ cleartextStream.authorized ? 'authorized' : 'unauthorized');
+ process.stdin.pipe(cleartextStream);
+ process.stdin.resume();
+ });
+ cleartextStream.setEncoding('utf8');
+ cleartextStream.on('data', function(data) {
+ console.log(data);
+ });
+ cleartextStream.on('end', function() {
+ server.close();
+ });
## tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized])
View
7 lib/crypto.js
@@ -114,6 +114,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;
};
View
2  lib/tls.js
@@ -912,6 +912,7 @@ function Server(/* [options], listener */) {
this.setOptions(options);
var sharedCreds = crypto.createCredentials({
+ pfx : self.pfx,
key: self.key,
passphrase: self.passphrase,
cert: self.cert,
@@ -996,6 +997,7 @@ Server.prototype.setOptions = function(options) {
this.rejectUnauthorized = false;
}
+ if (options.pfx) this.pfx = options.pfx;
if (options.key) this.key = options.key;
if (options.passphrase) this.passphrase = options.passphrase;
if (options.cert) this.cert = options.cert;
View
94 src/node_crypto.cc
@@ -43,6 +43,7 @@
# include <pthread.h>
#endif
+
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
# define OPENSSL_CONST const
#else
@@ -169,6 +170,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());
}
@@ -595,6 +597,98 @@ 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 * in = NULL;
+ STACK_OF(X509) *extraCerts = 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")));
+ }
+
+ in = LoadBIO(args[0]);
+ if (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);
+
+ assert(pass_written == passlen);
+ pass[passlen] = '\0';
+ }
+
+ if (d2i_PKCS12_bio(in, &p12)) {
+ if (PKCS12_parse(p12, pass, &pkey, &cert, &extraCerts)) {
+
+ //set cert
+ if (SSL_CTX_use_certificate(sc->ctx_, cert)) {
+
+ //set key
+ if (SSL_CTX_use_PrivateKey(sc->ctx_, pkey)) {
+
+ bool newCAStore = false;
+
+ //set extra certs
+ while (true) {
+ X509 *x509 = sk_X509_pop(extraCerts);
+
+ if (!x509) break;
+
+ if (!sc->ca_store_) {
+ sc->ca_store_ = X509_STORE_new();
+ newCAStore = true;
+ }
+
+ X509_STORE_add_cert(sc->ca_store_, x509);
+ SSL_CTX_add_client_CA(sc->ctx_, x509);
+ }
+
+ if (newCAStore) {
+ SSL_CTX_set_cert_store(sc->ctx_, sc->ca_store_);
+ }
+ ret = true;
+ }
+ }
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ sk_X509_free(extraCerts);
+ }
+ PKCS12_free(p12);
+ }
+ BIO_free(in);
+ 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__)
View
2  src/node_crypto.h
@@ -35,6 +35,7 @@
#include <openssl/x509v3.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
+#include <openssl/pkcs12.h>
#ifdef OPENSSL_NPN_NEGOTIATED
#include <node_buffer.h>
@@ -68,6 +69,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;
Please sign in to comment.
Something went wrong with that request. Please try again.