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

Commit 4dcb018

Browse files
author
Shigeki Ohtsu
committed
tls, crypto: add DHE support
This also changes the default cipher list as `DHE-RSA-AES128-SHA256` is added and '!EDH' is explicitly written to `!EDH-RSA-DES:!EDH-DSS-DES`. 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.
1 parent 6adf3ec commit 4dcb018

File tree

12 files changed

+197
-9
lines changed

12 files changed

+197
-9
lines changed

doc/api/tls.markdown

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,16 @@ automatically set as a listener for the [secureConnection][] event. The
142142
conjunction with the `honorCipherOrder` option described below to
143143
prioritize the non-CBC cipher.
144144

145-
Defaults to `ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH`.
146-
Consult the [OpenSSL cipher list format documentation] for details on the
147-
format.
145+
Defaults to
146+
`ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH-RSA-DES:!EDH-DSS-DES`.
147+
Consult the [OpenSSL cipher list format documentation] for details
148+
on the format.
148149

149-
`ECDHE-RSA-AES128-SHA256` and `AES128-GCM-SHA256` are TLS v1.2 ciphers and
150-
used when node.js is linked against OpenSSL 1.0.1 or newer, such as the
151-
bundled version of OpenSSL. Note that it is still possible for a TLS v1.2
152-
client to negotiate a weaker cipher unless `honorCipherOrder` is enabled.
150+
`ECDHE-RSA-AES128-SHA256`, `DHE-RSA-AES128-SHA256` and
151+
`AES128-GCM-SHA256` are TLS v1.2 ciphers and used when node.js is
152+
linked against OpenSSL 1.0.1 or newer, such as the bundled version
153+
of OpenSSL. Note that it is still possible for a TLS v1.2 client
154+
to negotiate a weaker cipher unless `honorCipherOrder` is enabled.
153155

154156
`RC4` is used as a fallback for clients that speak on older version of
155157
the TLS protocol. `RC4` has in recent years come under suspicion and
@@ -165,6 +167,10 @@ automatically set as a listener for the [secureConnection][] event. The
165167

166168
Defaults to `prime256v1`. Consult [RFC 4492] for more details.
167169

170+
- `dhparam`: DH parameter file to use for DHE key agreement. Use
171+
`openssl dhparam` command to create it. If the file is invalid to
172+
load, it is silently discarded.
173+
168174
- `handshakeTimeout`: Abort the connection if the SSL/TLS handshake does not
169175
finish in this many milliseconds. The default is 120 seconds.
170176

lib/_tls_common.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ exports.createSecureContext = function createSecureContext(options, context) {
9797
else if (options.ecdhCurve)
9898
c.context.setECDHCurve(options.ecdhCurve);
9999

100+
if (options.dhparam) c.context.setDHParam(options.dhparam);
101+
100102
if (options.crl) {
101103
if (util.isArray(options.crl)) {
102104
for (var i = 0, len = options.crl.length; i < len; i++) {

lib/_tls_wrap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ function Server(/* [options], listener */) {
600600
ca: self.ca,
601601
ciphers: self.ciphers,
602602
ecdhCurve: self.ecdhCurve,
603+
dhparam: self.dhparam,
603604
secureProtocol: self.secureProtocol,
604605
secureOptions: self.secureOptions,
605606
honorCipherOrder: self.honorCipherOrder,
@@ -718,6 +719,7 @@ Server.prototype.setOptions = function(options) {
718719
if (options.ciphers) this.ciphers = options.ciphers;
719720
if (!util.isUndefined(options.ecdhCurve))
720721
this.ecdhCurve = options.ecdhCurve;
722+
if (options.dhparam) this.dhparam = options.dhparam;
721723
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
722724
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
723725
var secureOptions = options.secureOptions || 0;

lib/tls.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ exports.CLIENT_RENEG_WINDOW = 600;
3333
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
3434

3535
exports.DEFAULT_CIPHERS =
36-
'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' + // TLS 1.2
37-
'RC4:HIGH:!MD5:!aNULL:!EDH'; // TLS 1.0
36+
// TLS 1.2
37+
'ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' +
38+
// TLS 1.0
39+
'RC4:HIGH:!MD5:!aNULL:!EDH-RSA-DES:!EDH-DSS-DES';
3840

3941
exports.DEFAULT_ECDH_CURVE = 'prime256v1';
4042

src/node_crypto.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ void SecureContext::Initialize(Environment* env, Handle<Object> target) {
270270
NODE_SET_PROTOTYPE_METHOD(t, "addRootCerts", SecureContext::AddRootCerts);
271271
NODE_SET_PROTOTYPE_METHOD(t, "setCiphers", SecureContext::SetCiphers);
272272
NODE_SET_PROTOTYPE_METHOD(t, "setECDHCurve", SecureContext::SetECDHCurve);
273+
NODE_SET_PROTOTYPE_METHOD(t, "setDHParam", SecureContext::SetDHParam);
273274
NODE_SET_PROTOTYPE_METHOD(t, "setOptions", SecureContext::SetOptions);
274275
NODE_SET_PROTOTYPE_METHOD(t, "setSessionIdContext",
275276
SecureContext::SetSessionIdContext);
@@ -746,6 +747,38 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo<Value>& args) {
746747
}
747748

748749

750+
void SecureContext::SetDHParam(const FunctionCallbackInfo<Value>& args) {
751+
HandleScope scope(args.GetIsolate());
752+
753+
SecureContext* sc = Unwrap<SecureContext>(args.This());
754+
Environment* env = sc->env();
755+
756+
// Auto DH is not supported in openssl 1.0.1, so dhparam needs
757+
// to be specifed explicitly
758+
if (args.Length() != 1) {
759+
return env->ThrowTypeError("Bad parameter");
760+
}
761+
762+
// Invalid dhparam is silently discarded and DHE is no longer used.
763+
BIO *bio = LoadBIO(env, args[0]);
764+
if (!bio)
765+
return;
766+
767+
DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
768+
769+
if(dh == NULL) {
770+
goto exit;
771+
}
772+
773+
SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_DH_USE);
774+
SSL_CTX_set_tmp_dh(sc->ctx_, dh);
775+
776+
exit:
777+
DH_free(dh);
778+
BIO_free(bio);
779+
}
780+
781+
749782
void SecureContext::SetOptions(const FunctionCallbackInfo<Value>& args) {
750783
HandleScope scope(args.GetIsolate());
751784

src/node_crypto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class SecureContext : public BaseObject {
9393
static void AddRootCerts(const v8::FunctionCallbackInfo<v8::Value>& args);
9494
static void SetCiphers(const v8::FunctionCallbackInfo<v8::Value>& args);
9595
static void SetECDHCurve(const v8::FunctionCallbackInfo<v8::Value>& args);
96+
static void SetDHParam(const v8::FunctionCallbackInfo<v8::Value>& args);
9697
static void SetOptions(const v8::FunctionCallbackInfo<v8::Value>& args);
9798
static void SetSessionIdContext(
9899
const v8::FunctionCallbackInfo<v8::Value>& args);

test/fixtures/test_dh1024.pem

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN DH PARAMETERS-----
2+
MIGHAoGBALDYoJasveNejxRyF3d9p7+OF6ALf9MzJD4FJqEO1YDEfrfE4I75HTcU
3+
y+h72WZXgGyI8ySUPvRs8t7W2N+0SRTKJmQnd2uKVyp0pFx5DG7TTy8DK+MFRMeh
4+
8Tz7TptcSHktZNmLo4Cxda7IsfaKUagNhku3Ks6HS2Gg6FHkdb4zAgEC
5+
-----END DH PARAMETERS-----

test/fixtures/test_dh2048.pem

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN DH PARAMETERS-----
2+
MIIBCAKCAQEAxhRvhunS9qXXGuT1OqTfiFzDgqfyzFWt4uCN38tDODkf6z57MyZJ
3+
6WsKExHeuNpfxS6XI0GOEWnYJqZP1+cMm8Gg2VNX0pI634tt/861ZnoWXQvOf2xa
4+
KOBYMW/e8DmiIfQ9szqECCuY2FBG7CJC9RqI4FTYEkbR3MGAfVqxm5/4nRdUwQIj
5+
V6jakbZ8EpKkk44iA4LBuLiR6BaLCbawK0rvdjaTTXWItMtRx5iMcSxrqaqBgA0v
6+
Vp7EMqmKKDGJVzq5+Rbir9HUwZxBo95SolpL5D3scJoVTqYf2oTSHxqkdqtzadVl
7+
Lq0OvBEfhX1oTPnJBH6GW+zmh462dfmlGwIBAg==
8+
-----END DH PARAMETERS-----

test/fixtures/test_dh4096.pem

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN DH PARAMETERS-----
2+
MIICCAKCAgEA1pNgDdQsDa8NTlIkV91Ye7whOPdDrd5wHFSIzTSfJn+N6u+BBsYX
3+
T+RMMHjDg4aWjauzIS7ElF9krh1qMF5KoI+u1+a7PHPvgGzZKCLZYVq6DwrTnyXV
4+
TBAoKHGIoTI0f3VbXjr6vOWdlMszu6fiI43XRIlVtsdoJY4rPWYJ2NQRQpOC6RAD
5+
LOp92Y15no3PHz8ur+3syqYaIDqRmOCZis/NTp3NTvcN0TH/CrHlKxgguijNZ4CA
6+
3kV6vACuztaefFMHUy7NKhOP2aXqydloJjAk4slXJn71SKhl0Me8kyDhALjYblqF
7+
HtMD3p6J4FVQzRmnNmWpKjfjZ87Xn3RPdrZc8AipFM7AHLYo/V6J3iFIh0T0lq0I
8+
QfCzeQUjxeLDaRQ7VRVGGY+P33lYk1zQbFsG+gGEkwQyRm3WfHRHJWC9pfsTWGuH
9+
tYlZqRgQ4o3t3S6NDFajkB8HoyU90pR4gFpcsxMO/kccmIpz84rhJSedoEv6Rnnk
10+
8sVkglWXtLvhgsw3/yGkiOKtA4MkAf6rOKh0JZfGwNXk8vymWGOxxk4thv0JDQrw
11+
EZvyvVWZfy5EjLRJ3UdLp9AgVCY2A5HlqeOLWTYQeJ1l9RGEL+6UBN/Mu8fdh3+S
12+
N42BBE0WUudU52hsCbZ8/TLTU6i6dco/FX5XQ4BDq1Tpbxm5k/iDXnMCAQI=
13+
-----END DH PARAMETERS-----

test/fixtures/test_dh512.pem

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN DH PARAMETERS-----
2+
MEYCQQDGhoPGQ171hbuBJoGSPxqcmJbBt0ZaVL3ZjZy79/9vlkQHtXrXbxKhj2vg
3+
SLNu/Sfav5RTZjJpj21cKBWYDZB7AgEC
4+
-----END DH PARAMETERS-----

test/fixtures/test_dherror.pem

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-----BEGIN DH PARAMETERS-----
2+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3+
-----END DH PARAMETERS-----

test/simple/test-tls-dhe.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
var common = require('../common');
23+
24+
if (!common.opensslCli) {
25+
console.error('Skipping because node compiled without OpenSSL CLI.');
26+
process.exit(0);
27+
}
28+
29+
var assert = require('assert');
30+
var spawn = require('child_process').spawn;
31+
var tls = require('tls');
32+
var fs = require('fs');
33+
var key = fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem');
34+
var cert = fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem');
35+
var nsuccess = 0;
36+
var ntests = 0;
37+
var ciphers = 'DHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
38+
39+
function loadDHParam(n) {
40+
return fs.readFileSync(common.fixturesDir + '/test_dh' + n + '.pem');
41+
}
42+
43+
function test(keylen, expectedCipher, cb) {
44+
var options = {
45+
key: key,
46+
cert: cert,
47+
dhparam: loadDHParam(keylen)
48+
};
49+
50+
var server = tls.createServer(options, function(conn) {
51+
conn.end();
52+
});
53+
54+
server.on('close', function(err) {
55+
assert(!err);
56+
if (cb) cb();
57+
});
58+
59+
server.listen(common.PORT, '127.0.0.1', function() {
60+
var args = ['s_client', '-connect', '127.0.0.1:' + common.PORT,
61+
'-cipher', ciphers];
62+
var client = spawn(common.opensslCli, args);
63+
var out = '';
64+
client.stdout.setEncoding('utf8');
65+
client.stdout.on('data', function(d) {
66+
out += d;
67+
});
68+
client.stdout.on('end', function() {
69+
// DHE key length can be checked -brief option in s_client but it
70+
// is only supported in openssl 1.0.2 so we cannot check it.
71+
var reg = new RegExp('Cipher : ' + expectedCipher);
72+
if (reg.test(out)) {
73+
nsuccess++;
74+
server.close();
75+
}
76+
});
77+
});
78+
}
79+
80+
function test512() {
81+
test(512, 'DHE-RSA-AES128-SHA256', test1024);
82+
ntests++;
83+
}
84+
85+
function test1024() {
86+
test(1024, 'DHE-RSA-AES128-SHA256', test2048);
87+
ntests++;
88+
}
89+
90+
function test2048() {
91+
test(2048, 'DHE-RSA-AES128-SHA256', test4096);
92+
ntests++;
93+
}
94+
95+
function test4096() {
96+
test(4096, 'DHE-RSA-AES128-SHA256', testError);
97+
ntests++;
98+
}
99+
100+
function testError() {
101+
test('error', 'ECDHE-RSA-AES128-SHA256', null);
102+
ntests++;
103+
}
104+
105+
test512();
106+
107+
process.on('exit', function() {
108+
assert.equal(ntests, nsuccess);
109+
});

0 commit comments

Comments
 (0)