mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
tls: more session configuration options, methods
Introduce `ticketKeys` server option, `session` client option, `getSession()` and `getTLSTicket()` methods. fix #7032
This commit is contained in:
1 parent
5ce458032f
commit
528a3ce3ed
5 files changed
+148
-1
No files matched your search
@@ -205,6 +205,12 @@ automatically set as a listener for the [secureConnection][] event. The
|
||||
session identifiers and TLS session tickets created by the server are
|
||||
timed out. See [SSL_CTX_set_timeout] for more details.
|
||||
|
||||
- `ticketKeys`: A 48-byte `Buffer` instance consisting of 16-byte prefix,
|
||||
16-byte hmac key, 16-byte AES key. You could use it to accept tls session
|
||||
tickets on multiple instances of tls server.
|
||||
|
||||
NOTE: Automatically shared between `cluster` module workers.
|
||||
|
||||
- `sessionIdContext`: A string containing a opaque identifier for session
|
||||
resumption. If `requestCert` is `true`, the default is MD5 hash value
|
||||
generated from command-line. Otherwise, the default is not provided.
|
||||
@@ -314,6 +320,8 @@ Creates a new client connection to the given `port` and `host` (old API) or
|
||||
SSL version 3. The possible values depend on your installation of
|
||||
OpenSSL and are defined in the constant [SSL_METHODS][].
|
||||
|
||||
- `session`: A `Buffer` instance, containing TLS session.
|
||||
|
||||
The `callback` parameter will be added as a listener for the
|
||||
['secureConnect'][] event.
|
||||
|
||||
@@ -398,6 +406,8 @@ Construct a new TLSSocket object from existing TCP socket.
|
||||
|
||||
- `SNICallback`: Optional, see [tls.createServer][]
|
||||
|
||||
- `session`: Optional, a `Buffer` instance, containing TLS session
|
||||
|
||||
## tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized])
|
||||
|
||||
Stability: 0 - Deprecated. Use tls.TLSSocket instead.
|
||||
@@ -646,6 +656,18 @@ and their processing can be delayed due to packet loss or reordering. However,
|
||||
smaller fragments add extra TLS framing bytes and CPU overhead, which may
|
||||
decrease overall server throughput.
|
||||
|
||||
### tlsSocket.getSession()
|
||||
|
||||
Return ASN.1 encoded TLS session or `undefined` if none was negotiated. Could
|
||||
be used to speed up handshake establishment when reconnecting to the server.
|
||||
|
||||
### tlsSocket.getTLSTicket()
|
||||
|
||||
NOTE: Works only with client TLS sockets. Useful only for debugging, for
|
||||
session reuse provide `session` option to `tls.connect`.
|
||||
|
||||
Return TLS session ticket or `undefined` if none was negotiated.
|
||||
|
||||
### tlsSocket.address()
|
||||
|
||||
Returns the bound address, the address family name and port of the
|
||||
|
||||
@@ -232,6 +232,9 @@ TLSSocket.prototype._init = function(socket) {
|
||||
} else {
|
||||
this.ssl.onhandshakestart = function() {};
|
||||
this.ssl.onhandshakedone = this._finishInit.bind(this);
|
||||
|
||||
if (options.session)
|
||||
this.ssl.setSession(options.session);
|
||||
}
|
||||
|
||||
this.ssl.onerror = function(err) {
|
||||
@@ -321,6 +324,10 @@ TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) {
|
||||
return this.ssl.setMaxSendFragment(size) == 1;
|
||||
};
|
||||
|
||||
TLSSocket.prototype.getTLSTicket = function getTLSTicket() {
|
||||
return this.ssl.getTLSTicket();
|
||||
};
|
||||
|
||||
TLSSocket.prototype._handleTimeout = function() {
|
||||
this._tlsError(new Error('TLS handshake timeout'));
|
||||
};
|
||||
@@ -620,6 +627,7 @@ Server.prototype.setOptions = function(options) {
|
||||
if (!util.isUndefined(options.ecdhCurve))
|
||||
this.ecdhCurve = options.ecdhCurve;
|
||||
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
|
||||
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
|
||||
var secureOptions = options.secureOptions || 0;
|
||||
if (options.honorCipherOrder) {
|
||||
secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE;
|
||||
@@ -747,6 +755,7 @@ exports.connect = function(/* [port, host], options, cb */) {
|
||||
isServer: false,
|
||||
requestCert: true,
|
||||
rejectUnauthorized: options.rejectUnauthorized,
|
||||
session: options.session,
|
||||
NPNProtocols: NPN.NPNProtocols
|
||||
});
|
||||
result = socket;
|
||||
|
||||
+21
-1
@@ -856,6 +856,7 @@ void SSLWrap<Base>::AddMethods(Handle<FunctionTemplate> t) {
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "endParser", EndParser);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "renegotiate", Renegotiate);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Shutdown);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "getTLSTicket", GetTLSTicket);
|
||||
|
||||
#ifdef SSL_set_max_send_fragment
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "setMaxSendFragment", SetMaxSendFragment);
|
||||
@@ -1129,7 +1130,7 @@ void SSLWrap<Base>::GetSession(const FunctionCallbackInfo<Value>& args) {
|
||||
unsigned char* sbuf = new unsigned char[slen];
|
||||
unsigned char* p = sbuf;
|
||||
i2d_SSL_SESSION(sess, &p);
|
||||
args.GetReturnValue().Set(Encode(sbuf, slen, BINARY));
|
||||
args.GetReturnValue().Set(Encode(sbuf, slen, BUFFER));
|
||||
delete[] sbuf;
|
||||
}
|
||||
|
||||
@@ -1247,6 +1248,25 @@ void SSLWrap<Base>::Shutdown(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
template <class Base>
|
||||
void SSLWrap<Base>::GetTLSTicket(const FunctionCallbackInfo<Value>& args) {
|
||||
HandleScope scope(args.GetIsolate());
|
||||
|
||||
Base* w = Unwrap<Base>(args.This());
|
||||
Environment* env = w->ssl_env();
|
||||
|
||||
SSL_SESSION* sess = SSL_get_session(w->ssl_);
|
||||
if (sess == NULL || sess->tlsext_tick == NULL)
|
||||
return;
|
||||
|
||||
Local<Object> buf = Buffer::New(env,
|
||||
reinterpret_cast<char*>(sess->tlsext_tick),
|
||||
sess->tlsext_ticklen);
|
||||
|
||||
args.GetReturnValue().Set(buf);
|
||||
}
|
||||
|
||||
|
||||
#ifdef SSL_set_max_send_fragment
|
||||
template <class Base>
|
||||
void SSLWrap<Base>::SetMaxSendFragment(
|
||||
|
||||
@@ -187,6 +187,7 @@ class SSLWrap {
|
||||
static void EndParser(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetTLSTicket(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
#ifdef SSL_set_max_send_fragment
|
||||
static void SetMaxSendFragment(
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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.
|
||||
|
||||
if (!process.versions.openssl) {
|
||||
console.error('Skipping because node compiled without OpenSSL.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var assert = require('assert');
|
||||
var fs = require('fs');
|
||||
var net = require('net');
|
||||
var tls = require('tls');
|
||||
var crypto = require('crypto');
|
||||
|
||||
var common = require('../common');
|
||||
|
||||
var keys = crypto.randomBytes(48);
|
||||
var serverLog = [];
|
||||
var ticketLog = [];
|
||||
|
||||
var serverCount = 0;
|
||||
function createServer() {
|
||||
var id = serverCount++;
|
||||
|
||||
var server = tls.createServer({
|
||||
key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'),
|
||||
cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem'),
|
||||
ticketKeys: keys
|
||||
}, function(c) {
|
||||
serverLog.push(id);
|
||||
c.end();
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
var servers = [ createServer(), createServer(), createServer(), createServer(), createServer(), createServer() ];
|
||||
|
||||
// Create one TCP server and balance sockets to multiple TLS server instances
|
||||
var shared = net.createServer(function(c) {
|
||||
servers.shift().emit('connection', c);
|
||||
}).listen(common.PORT, function() {
|
||||
start(function() {
|
||||
shared.close();
|
||||
});
|
||||
});
|
||||
|
||||
function start(callback) {
|
||||
var sess = null;
|
||||
var left = servers.length;
|
||||
|
||||
function connect() {
|
||||
var s = tls.connect(common.PORT, {
|
||||
session: sess,
|
||||
rejectUnauthorized: false
|
||||
}, function() {
|
||||
sess = s.getSession() || sess;
|
||||
ticketLog.push(s.getTLSTicket().toString('hex'));
|
||||
});
|
||||
s.on('close', function() {
|
||||
if (--left === 0)
|
||||
callback();
|
||||
else
|
||||
connect();
|
||||
});
|
||||
}
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.equal(ticketLog.length, serverLog.length);
|
||||
for (var i = 0; i < serverLog.length - 1; i++) {
|
||||
assert.notEqual(serverLog[i], serverLog[i + 1]);
|
||||
assert.equal(ticketLog[i], ticketLog[i + 1]);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user