mirror of https://github.com/nodejs/node.git
tls: add setKeyCert() to tls.Socket
PR-URL: https://github.com/nodejs/node/pull/53636 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com>
This commit is contained in:
parent
4b4a9319d9
commit
6ba0af1354
|
@ -1574,6 +1574,20 @@ When running as the server, the socket will be destroyed with an error after
|
|||
For TLSv1.3, renegotiation cannot be initiated, it is not supported by the
|
||||
protocol.
|
||||
|
||||
### `tlsSocket.setKeyCert(context)`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `context` {Object|tls.SecureContext} An object containing at least `key` and
|
||||
`cert` properties from the [`tls.createSecureContext()`][] `options`, or a
|
||||
TLS context object created with [`tls.createSecureContext()`][] itself.
|
||||
|
||||
The `tlsSocket.setKeyCert()` method sets the private key and certificate to use
|
||||
for the socket. This is mainly useful if you wish to select a server certificate
|
||||
from a TLS server's `ALPNCallback`.
|
||||
|
||||
### `tlsSocket.setMaxSendFragment(size)`
|
||||
|
||||
<!-- YAML
|
||||
|
|
|
@ -1144,6 +1144,17 @@ TLSSocket.prototype.getX509Certificate = function() {
|
|||
return cert ? new InternalX509Certificate(cert) : undefined;
|
||||
};
|
||||
|
||||
TLSSocket.prototype.setKeyCert = function(context) {
|
||||
if (this._handle) {
|
||||
let secureContext;
|
||||
if (context instanceof common.SecureContext)
|
||||
secureContext = context;
|
||||
else
|
||||
secureContext = tls.createSecureContext(context);
|
||||
this._handle.setKeyCert(secureContext.context);
|
||||
}
|
||||
};
|
||||
|
||||
// Proxy TLSSocket handle methods
|
||||
function makeSocketMethodProxy(name) {
|
||||
return function socketMethodProxy(...args) {
|
||||
|
|
|
@ -1595,6 +1595,33 @@ void TLSWrap::SetALPNProtocols(const FunctionCallbackInfo<Value>& args) {
|
|||
}
|
||||
}
|
||||
|
||||
void TLSWrap::SetKeyCert(const FunctionCallbackInfo<Value>& args) {
|
||||
TLSWrap* w;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
|
||||
Environment* env = w->env();
|
||||
|
||||
if (w->is_client()) return;
|
||||
|
||||
if (args.Length() < 1 || !args[0]->IsObject())
|
||||
return env->ThrowTypeError("Must give a SecureContext as first argument");
|
||||
|
||||
Local<Value> ctx = args[0];
|
||||
if (UNLIKELY(ctx.IsEmpty())) return;
|
||||
|
||||
Local<FunctionTemplate> cons = env->secure_context_constructor_template();
|
||||
if (cons->HasInstance(ctx)) {
|
||||
SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
|
||||
CHECK_NOT_NULL(sc);
|
||||
if (!UseSNIContext(w->ssl_, BaseObjectPtr<SecureContext>(sc)) ||
|
||||
!w->SetCACerts(sc)) {
|
||||
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
|
||||
return ThrowCryptoError(env, err, "SetKeyCert");
|
||||
}
|
||||
} else {
|
||||
return env->ThrowTypeError("Must give a SecureContext as first argument");
|
||||
}
|
||||
}
|
||||
|
||||
void TLSWrap::GetPeerCertificate(const FunctionCallbackInfo<Value>& args) {
|
||||
TLSWrap* w;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
|
||||
|
@ -2130,6 +2157,7 @@ void TLSWrap::Initialize(
|
|||
SetProtoMethod(isolate, t, "renegotiate", Renegotiate);
|
||||
SetProtoMethod(isolate, t, "requestOCSP", RequestOCSP);
|
||||
SetProtoMethod(isolate, t, "setALPNProtocols", SetALPNProtocols);
|
||||
SetProtoMethod(isolate, t, "setKeyCert", SetKeyCert);
|
||||
SetProtoMethod(isolate, t, "setOCSPResponse", SetOCSPResponse);
|
||||
SetProtoMethod(isolate, t, "setServername", SetServername);
|
||||
SetProtoMethod(isolate, t, "setSession", SetSession);
|
||||
|
|
|
@ -213,6 +213,7 @@ class TLSWrap : public AsyncWrap,
|
|||
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void RequestOCSP(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetALPNProtocols(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetKeyCert(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetOCSPResponse(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetServername(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetSession(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const { X509Certificate } = require('crypto');
|
||||
const tls = require('tls');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const altKeyCert = {
|
||||
key: fixtures.readKey('agent2-key.pem'),
|
||||
cert: fixtures.readKey('agent2-cert.pem'),
|
||||
minVersion: 'TLSv1.2',
|
||||
};
|
||||
|
||||
const altKeyCertVals = [
|
||||
altKeyCert,
|
||||
tls.createSecureContext(altKeyCert),
|
||||
];
|
||||
|
||||
(function next() {
|
||||
if (!altKeyCertVals.length)
|
||||
return;
|
||||
const altKeyCertVal = altKeyCertVals.shift();
|
||||
const options = {
|
||||
key: fixtures.readKey('agent1-key.pem'),
|
||||
cert: fixtures.readKey('agent1-cert.pem'),
|
||||
minVersion: 'TLSv1.3',
|
||||
ALPNCallback: common.mustCall(function({ servername, protocols }) {
|
||||
this.setKeyCert(altKeyCertVal);
|
||||
assert.deepStrictEqual(protocols, ['acme-tls/1']);
|
||||
return protocols[0];
|
||||
}),
|
||||
};
|
||||
|
||||
tls.createServer(options, (s) => s.end()).listen(0, function() {
|
||||
this.on('connection', common.mustCall((socket) => this.close()));
|
||||
|
||||
tls.connect({
|
||||
port: this.address().port,
|
||||
rejectUnauthorized: false,
|
||||
ALPNProtocols: ['acme-tls/1'],
|
||||
}, common.mustCall(function() {
|
||||
assert.strictEqual(this.getProtocol(), 'TLSv1.3');
|
||||
const altCert = new X509Certificate(altKeyCert.cert);
|
||||
assert.strictEqual(
|
||||
this.getPeerX509Certificate().raw.equals(altCert.raw),
|
||||
true
|
||||
);
|
||||
this.end();
|
||||
next();
|
||||
}));
|
||||
});
|
||||
})();
|
Loading…
Reference in New Issue