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.
|
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
|
- `handshakeTimeout`: Abort the connection if the SSL/TLS handshake does not
|
||||||
finish in this many milliseconds. The default is 120 seconds.
|
finish in this many milliseconds. The default is 120 seconds.
|
||||||
|
|
||||||
|
@ -97,6 +97,8 @@ exports.createSecureContext = function createSecureContext(options, context) {
|
|||||||
else if (options.ecdhCurve)
|
else if (options.ecdhCurve)
|
||||||
c.context.setECDHCurve(options.ecdhCurve);
|
c.context.setECDHCurve(options.ecdhCurve);
|
||||||
|
|
||||||
|
if (options.dhparam) c.context.setDHParam(options.dhparam);
|
||||||
|
|
||||||
if (options.crl) {
|
if (options.crl) {
|
||||||
if (util.isArray(options.crl)) {
|
if (util.isArray(options.crl)) {
|
||||||
for (var i = 0, len = options.crl.length; i < len; i++) {
|
for (var i = 0, len = options.crl.length; i < len; i++) {
|
||||||
|
@ -600,6 +600,7 @@ function Server(/* [options], listener */) {
|
|||||||
ca: self.ca,
|
ca: self.ca,
|
||||||
ciphers: self.ciphers,
|
ciphers: self.ciphers,
|
||||||
ecdhCurve: self.ecdhCurve,
|
ecdhCurve: self.ecdhCurve,
|
||||||
|
dhparam: self.dhparam,
|
||||||
secureProtocol: self.secureProtocol,
|
secureProtocol: self.secureProtocol,
|
||||||
secureOptions: self.secureOptions,
|
secureOptions: self.secureOptions,
|
||||||
honorCipherOrder: self.honorCipherOrder,
|
honorCipherOrder: self.honorCipherOrder,
|
||||||
@ -718,6 +719,7 @@ Server.prototype.setOptions = function(options) {
|
|||||||
if (options.ciphers) this.ciphers = options.ciphers;
|
if (options.ciphers) this.ciphers = options.ciphers;
|
||||||
if (!util.isUndefined(options.ecdhCurve))
|
if (!util.isUndefined(options.ecdhCurve))
|
||||||
this.ecdhCurve = options.ecdhCurve;
|
this.ecdhCurve = options.ecdhCurve;
|
||||||
|
if (options.dhparam) this.dhparam = options.dhparam;
|
||||||
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
|
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
|
||||||
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
|
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
|
||||||
var secureOptions = options.secureOptions || 0;
|
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, "addRootCerts", SecureContext::AddRootCerts);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "setCiphers", SecureContext::SetCiphers);
|
NODE_SET_PROTOTYPE_METHOD(t, "setCiphers", SecureContext::SetCiphers);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "setECDHCurve", SecureContext::SetECDHCurve);
|
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, "setOptions", SecureContext::SetOptions);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "setSessionIdContext",
|
NODE_SET_PROTOTYPE_METHOD(t, "setSessionIdContext",
|
||||||
SecureContext::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) {
|
void SecureContext::SetOptions(const FunctionCallbackInfo<Value>& args) {
|
||||||
HandleScope scope(args.GetIsolate());
|
HandleScope scope(args.GetIsolate());
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ class SecureContext : public BaseObject {
|
|||||||
static void AddRootCerts(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void AddRootCerts(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void SetCiphers(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 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 SetOptions(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void SetSessionIdContext(
|
static void SetSessionIdContext(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
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 \
|
-signkey ec-key.pem \
|
||||||
-out ec-cert.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:
|
clean:
|
||||||
rm -f *.pem *.srl ca2-database.txt ca2-serial
|
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