tls, crypto: add DHE support
In case of an invalid DH parameter file, it is sliently discarded. To use auto DH parameter in a server and DHE key length check in a client, we need to wait for the next release of OpenSSL-1.0.2. Reviewed-By: Fedor Indutny <fedor@indutny.com>
This commit is contained in:
parent
6e453fad87
commit
0dfedb7127
@ -165,6 +165,10 @@ automatically set as a listener for the [secureConnection][] event. The
|
||||
|
||||
Defaults to `prime256v1`. Consult [RFC 4492] for more details.
|
||||
|
||||
- `dhparam`: DH parameter file to use for DHE key agreement. Use
|
||||
`openssl dhparam` command to create it. If the file is invalid to
|
||||
load, it is silently discarded.
|
||||
|
||||
- `handshakeTimeout`: Abort the connection if the SSL/TLS handshake does not
|
||||
finish in this many milliseconds. The default is 120 seconds.
|
||||
|
||||
|
@ -97,6 +97,8 @@ exports.createSecureContext = function createSecureContext(options, context) {
|
||||
else if (options.ecdhCurve)
|
||||
c.context.setECDHCurve(options.ecdhCurve);
|
||||
|
||||
if (options.dhparam) c.context.setDHParam(options.dhparam);
|
||||
|
||||
if (options.crl) {
|
||||
if (util.isArray(options.crl)) {
|
||||
for (var i = 0, len = options.crl.length; i < len; i++) {
|
||||
|
@ -600,6 +600,7 @@ function Server(/* [options], listener */) {
|
||||
ca: self.ca,
|
||||
ciphers: self.ciphers,
|
||||
ecdhCurve: self.ecdhCurve,
|
||||
dhparam: self.dhparam,
|
||||
secureProtocol: self.secureProtocol,
|
||||
secureOptions: self.secureOptions,
|
||||
honorCipherOrder: self.honorCipherOrder,
|
||||
@ -718,6 +719,7 @@ Server.prototype.setOptions = function(options) {
|
||||
if (options.ciphers) this.ciphers = options.ciphers;
|
||||
if (!util.isUndefined(options.ecdhCurve))
|
||||
this.ecdhCurve = options.ecdhCurve;
|
||||
if (options.dhparam) this.dhparam = options.dhparam;
|
||||
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
|
||||
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
|
||||
var secureOptions = options.secureOptions || 0;
|
||||
|
@ -270,6 +270,7 @@ void SecureContext::Initialize(Environment* env, Handle<Object> target) {
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "addRootCerts", SecureContext::AddRootCerts);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "setCiphers", SecureContext::SetCiphers);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "setECDHCurve", SecureContext::SetECDHCurve);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "setDHParam", SecureContext::SetDHParam);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "setOptions", SecureContext::SetOptions);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "setSessionIdContext",
|
||||
SecureContext::SetSessionIdContext);
|
||||
@ -746,6 +747,37 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::SetDHParam(const FunctionCallbackInfo<Value>& args) {
|
||||
HandleScope scope(args.GetIsolate());
|
||||
|
||||
SecureContext* sc = Unwrap<SecureContext>(args.This());
|
||||
Environment* env = sc->env();
|
||||
|
||||
// Auto DH is not supported in openssl 1.0.1, so dhparam needs
|
||||
// to be specifed explicitly
|
||||
if (args.Length() != 1)
|
||||
return env->ThrowTypeError("Bad parameter");
|
||||
|
||||
// Invalid dhparam is silently discarded and DHE is no longer used.
|
||||
BIO* bio = LoadBIO(env, args[0]);
|
||||
if (!bio)
|
||||
return;
|
||||
|
||||
DH* dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
|
||||
BIO_free_all(bio);
|
||||
|
||||
if (dh == NULL)
|
||||
return;
|
||||
|
||||
SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_DH_USE);
|
||||
int r = SSL_CTX_set_tmp_dh(sc->ctx_, dh);
|
||||
DH_free(dh);
|
||||
|
||||
if (!r)
|
||||
return env->ThrowTypeError("Error setting temp DH parameter");
|
||||
}
|
||||
|
||||
|
||||
void SecureContext::SetOptions(const FunctionCallbackInfo<Value>& args) {
|
||||
HandleScope scope(args.GetIsolate());
|
||||
|
||||
|
@ -95,6 +95,7 @@ class SecureContext : public BaseObject {
|
||||
static void AddRootCerts(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetCiphers(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetECDHCurve(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetDHParam(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetOptions(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetSessionIdContext(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
3
test/fixtures/dherror.pem
vendored
Normal file
3
test/fixtures/dherror.pem
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
-----END DH PARAMETERS-----
|
11
test/fixtures/keys/Makefile
vendored
11
test/fixtures/keys/Makefile
vendored
@ -1,4 +1,4 @@
|
||||
all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem ca2-crl.pem ec-cert.pem
|
||||
all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem ca2-crl.pem ec-cert.pem dh512.pem dh1024.pem dh2048.pem
|
||||
|
||||
|
||||
#
|
||||
@ -145,6 +145,15 @@ ec-cert.pem: ec-csr.pem ec-key.pem
|
||||
-signkey ec-key.pem \
|
||||
-out ec-cert.pem
|
||||
|
||||
dh512.pem:
|
||||
openssl dhparam -out dh512.pem 512
|
||||
|
||||
dh1024.pem:
|
||||
openssl dhparam -out dh1024.pem 1024
|
||||
|
||||
dh2048.pem:
|
||||
openssl dhparam -out dh2048.pem 2048
|
||||
|
||||
clean:
|
||||
rm -f *.pem *.srl ca2-database.txt ca2-serial
|
||||
|
||||
|
5
test/fixtures/keys/dh1024.pem
vendored
Normal file
5
test/fixtures/keys/dh1024.pem
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIGHAoGBAO2Ij3VgqTKsxLZiK0uW0xWVszBzNbdjgLkriicMcZuUj1fQOSM6CPwv
|
||||
5kdrRV8kHCK9q8dU1Dpf2dgh3l4fFFLpjIuUmUx3b7Q+GfHZ1UepNxr1NHSJaCl+
|
||||
wA0gwSDYhy8xRAsZ3bFVsLfDCuxuzPNC0yjtS5CVqci//vq0NTM7AgEC
|
||||
-----END DH PARAMETERS-----
|
8
test/fixtures/keys/dh2048.pem
vendored
Normal file
8
test/fixtures/keys/dh2048.pem
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIIBCAKCAQEAg3ytm4B8bqS4KmLTw7eeqrzp8Y4Ew65weXKL9eY2FmudR0VUkoti
|
||||
fs7/fKDsxMVgLyL+9UpbTs18CNslwWgMCSkrXe/NtXWlQQBLgXhEHOaeK/6j9zyt
|
||||
S6rlSvGK68NF0/e7o6jZXOSktIeflJQXoUyeds65+la/l5O2iuX0tgnGfNjB2Pdt
|
||||
RnoAPNJW+SBvyfcmiWjfd5Lh67SBgckqFMiH+CPiw54U2CeDPB343DUEPpTcnLJB
|
||||
aJs4uuxDnskz/0ZVidNBpUBs1wPQ8ruVNZx3hG2+PIqNPvOfYUPXIgn1ABj3mGAR
|
||||
sgtN63KUBX322zkTVPJnt30mrWp/F62GSwIBAg==
|
||||
-----END DH PARAMETERS-----
|
4
test/fixtures/keys/dh512.pem
vendored
Normal file
4
test/fixtures/keys/dh512.pem
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MEYCQQDpl3okBAjG92NSOaQEsIyqzvJRN06yHuGXunxYVIqxg7TnU8DBZW0ZYyiJ
|
||||
rJLRA/9b9dCk5DXpq1pFGoAkYLoDAgEC
|
||||
-----END DH PARAMETERS-----
|
107
test/simple/test-tls-dhe.js
Normal file
107
test/simple/test-tls-dhe.js
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
|
||||
if (!common.opensslCli) {
|
||||
console.error('Skipping because node compiled without OpenSSL CLI.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var assert = require('assert');
|
||||
var spawn = require('child_process').spawn;
|
||||
var tls = require('tls');
|
||||
var fs = require('fs');
|
||||
var key = fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem');
|
||||
var cert = fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem');
|
||||
var nsuccess = 0;
|
||||
var ntests = 0;
|
||||
var ciphers = 'DHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
|
||||
|
||||
|
||||
function loadDHParam(n) {
|
||||
var path = common.fixturesDir;
|
||||
if (n !== 'error') path += '/keys';
|
||||
return fs.readFileSync(path + '/dh' + n + '.pem');
|
||||
}
|
||||
|
||||
function test(keylen, expectedCipher, cb) {
|
||||
var options = {
|
||||
key: key,
|
||||
cert: cert,
|
||||
dhparam: loadDHParam(keylen)
|
||||
};
|
||||
|
||||
var server = tls.createServer(options, function(conn) {
|
||||
conn.end();
|
||||
});
|
||||
|
||||
server.on('close', function(err) {
|
||||
assert(!err);
|
||||
if (cb) cb();
|
||||
});
|
||||
|
||||
server.listen(common.PORT, '127.0.0.1', function() {
|
||||
var args = ['s_client', '-connect', '127.0.0.1:' + common.PORT,
|
||||
'-cipher', ciphers];
|
||||
var client = spawn(common.opensslCli, args);
|
||||
var out = '';
|
||||
client.stdout.setEncoding('utf8');
|
||||
client.stdout.on('data', function(d) {
|
||||
out += d;
|
||||
});
|
||||
client.stdout.on('end', function() {
|
||||
// DHE key length can be checked -brief option in s_client but it
|
||||
// is only supported in openssl 1.0.2 so we cannot check it.
|
||||
var reg = new RegExp('Cipher : ' + expectedCipher);
|
||||
if (reg.test(out)) {
|
||||
nsuccess++;
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function test512() {
|
||||
test(512, 'DHE-RSA-AES128-SHA256', test1024);
|
||||
ntests++;
|
||||
}
|
||||
|
||||
function test1024() {
|
||||
test(1024, 'DHE-RSA-AES128-SHA256', test2048);
|
||||
ntests++;
|
||||
}
|
||||
|
||||
function test2048() {
|
||||
test(2048, 'DHE-RSA-AES128-SHA256', testError);
|
||||
ntests++;
|
||||
}
|
||||
|
||||
function testError() {
|
||||
test('error', 'ECDHE-RSA-AES128-SHA256', null);
|
||||
ntests++;
|
||||
}
|
||||
|
||||
test512();
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.equal(ntests, nsuccess);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user