Skip to content

Commit 8660d48

Browse files
mscdexmarco-ippolito
authored andcommitted
tls: add setKeyCert() to tls.Socket
PR-URL: #53636 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Tim Perry <[email protected]>
1 parent f87eed8 commit 8660d48

File tree

5 files changed

+110
-0
lines changed

5 files changed

+110
-0
lines changed

doc/api/tls.md

+14
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,20 @@ When running as the server, the socket will be destroyed with an error after
15331533
For TLSv1.3, renegotiation cannot be initiated, it is not supported by the
15341534
protocol.
15351535

1536+
### `tlsSocket.setKeyCert(context)`
1537+
1538+
<!-- YAML
1539+
added: REPLACEME
1540+
-->
1541+
1542+
* `context` {Object|tls.SecureContext} An object containing at least `key` and
1543+
`cert` properties from the [`tls.createSecureContext()`][] `options`, or a
1544+
TLS context object created with [`tls.createSecureContext()`][] itself.
1545+
1546+
The `tlsSocket.setKeyCert()` method sets the private key and certificate to use
1547+
for the socket. This is mainly useful if you wish to select a server certificate
1548+
from a TLS server's `ALPNCallback`.
1549+
15361550
### `tlsSocket.setMaxSendFragment(size)`
15371551

15381552
<!-- YAML

lib/_tls_wrap.js

+11
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,17 @@ TLSSocket.prototype.getX509Certificate = function() {
11511151
return cert ? new InternalX509Certificate(cert) : undefined;
11521152
};
11531153

1154+
TLSSocket.prototype.setKeyCert = function(context) {
1155+
if (this._handle) {
1156+
let secureContext;
1157+
if (context instanceof common.SecureContext)
1158+
secureContext = context;
1159+
else
1160+
secureContext = tls.createSecureContext(context);
1161+
this._handle.setKeyCert(secureContext.context);
1162+
}
1163+
};
1164+
11541165
// Proxy TLSSocket handle methods
11551166
function makeSocketMethodProxy(name) {
11561167
return function socketMethodProxy(...args) {

src/crypto/crypto_tls.cc

+28
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,33 @@ void TLSWrap::SetALPNProtocols(const FunctionCallbackInfo<Value>& args) {
15951595
}
15961596
}
15971597

1598+
void TLSWrap::SetKeyCert(const FunctionCallbackInfo<Value>& args) {
1599+
TLSWrap* w;
1600+
ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1601+
Environment* env = w->env();
1602+
1603+
if (w->is_client()) return;
1604+
1605+
if (args.Length() < 1 || !args[0]->IsObject())
1606+
return env->ThrowTypeError("Must give a SecureContext as first argument");
1607+
1608+
Local<Value> ctx = args[0];
1609+
if (UNLIKELY(ctx.IsEmpty())) return;
1610+
1611+
Local<FunctionTemplate> cons = env->secure_context_constructor_template();
1612+
if (cons->HasInstance(ctx)) {
1613+
SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
1614+
CHECK_NOT_NULL(sc);
1615+
if (!UseSNIContext(w->ssl_, BaseObjectPtr<SecureContext>(sc)) ||
1616+
!w->SetCACerts(sc)) {
1617+
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
1618+
return ThrowCryptoError(env, err, "SetKeyCert");
1619+
}
1620+
} else {
1621+
return env->ThrowTypeError("Must give a SecureContext as first argument");
1622+
}
1623+
}
1624+
15981625
void TLSWrap::GetPeerCertificate(const FunctionCallbackInfo<Value>& args) {
15991626
TLSWrap* w;
16001627
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
@@ -2130,6 +2157,7 @@ void TLSWrap::Initialize(
21302157
SetProtoMethod(isolate, t, "renegotiate", Renegotiate);
21312158
SetProtoMethod(isolate, t, "requestOCSP", RequestOCSP);
21322159
SetProtoMethod(isolate, t, "setALPNProtocols", SetALPNProtocols);
2160+
SetProtoMethod(isolate, t, "setKeyCert", SetKeyCert);
21332161
SetProtoMethod(isolate, t, "setOCSPResponse", SetOCSPResponse);
21342162
SetProtoMethod(isolate, t, "setServername", SetServername);
21352163
SetProtoMethod(isolate, t, "setSession", SetSession);

src/crypto/crypto_tls.h

+1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ class TLSWrap : public AsyncWrap,
213213
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
214214
static void RequestOCSP(const v8::FunctionCallbackInfo<v8::Value>& args);
215215
static void SetALPNProtocols(const v8::FunctionCallbackInfo<v8::Value>& args);
216+
static void SetKeyCert(const v8::FunctionCallbackInfo<v8::Value>& args);
216217
static void SetOCSPResponse(const v8::FunctionCallbackInfo<v8::Value>& args);
217218
static void SetServername(const v8::FunctionCallbackInfo<v8::Value>& args);
218219
static void SetSession(const v8::FunctionCallbackInfo<v8::Value>& args);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const { X509Certificate } = require('crypto');
9+
const tls = require('tls');
10+
const fixtures = require('../common/fixtures');
11+
12+
const altKeyCert = {
13+
key: fixtures.readKey('agent2-key.pem'),
14+
cert: fixtures.readKey('agent2-cert.pem'),
15+
minVersion: 'TLSv1.2',
16+
};
17+
18+
const altKeyCertVals = [
19+
altKeyCert,
20+
tls.createSecureContext(altKeyCert),
21+
];
22+
23+
(function next() {
24+
if (!altKeyCertVals.length)
25+
return;
26+
const altKeyCertVal = altKeyCertVals.shift();
27+
const options = {
28+
key: fixtures.readKey('agent1-key.pem'),
29+
cert: fixtures.readKey('agent1-cert.pem'),
30+
minVersion: 'TLSv1.3',
31+
ALPNCallback: common.mustCall(function({ servername, protocols }) {
32+
this.setKeyCert(altKeyCertVal);
33+
assert.deepStrictEqual(protocols, ['acme-tls/1']);
34+
return protocols[0];
35+
}),
36+
};
37+
38+
tls.createServer(options, (s) => s.end()).listen(0, function() {
39+
this.on('connection', common.mustCall((socket) => this.close()));
40+
41+
tls.connect({
42+
port: this.address().port,
43+
rejectUnauthorized: false,
44+
ALPNProtocols: ['acme-tls/1'],
45+
}, common.mustCall(function() {
46+
assert.strictEqual(this.getProtocol(), 'TLSv1.3');
47+
const altCert = new X509Certificate(altKeyCert.cert);
48+
assert.strictEqual(
49+
this.getPeerX509Certificate().raw.equals(altCert.raw),
50+
true
51+
);
52+
this.end();
53+
next();
54+
}));
55+
});
56+
})();

0 commit comments

Comments
 (0)