Skip to content

Commit 4d777de

Browse files
mscdexaduh95
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 c590828 commit 4d777de

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
@@ -1574,6 +1574,20 @@ When running as the server, the socket will be destroyed with an error after
15741574
For TLSv1.3, renegotiation cannot be initiated, it is not supported by the
15751575
protocol.
15761576

1577+
### `tlsSocket.setKeyCert(context)`
1578+
1579+
<!-- YAML
1580+
added: REPLACEME
1581+
-->
1582+
1583+
* `context` {Object|tls.SecureContext} An object containing at least `key` and
1584+
`cert` properties from the [`tls.createSecureContext()`][] `options`, or a
1585+
TLS context object created with [`tls.createSecureContext()`][] itself.
1586+
1587+
The `tlsSocket.setKeyCert()` method sets the private key and certificate to use
1588+
for the socket. This is mainly useful if you wish to select a server certificate
1589+
from a TLS server's `ALPNCallback`.
1590+
15771591
### `tlsSocket.setMaxSendFragment(size)`
15781592

15791593
<!-- YAML

lib/_tls_wrap.js

+11
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,17 @@ TLSSocket.prototype.getX509Certificate = function() {
11441144
return cert ? new InternalX509Certificate(cert) : undefined;
11451145
};
11461146

1147+
TLSSocket.prototype.setKeyCert = function(context) {
1148+
if (this._handle) {
1149+
let secureContext;
1150+
if (context instanceof common.SecureContext)
1151+
secureContext = context;
1152+
else
1153+
secureContext = tls.createSecureContext(context);
1154+
this._handle.setKeyCert(secureContext.context);
1155+
}
1156+
};
1157+
11471158
// Proxy TLSSocket handle methods
11481159
function makeSocketMethodProxy(name) {
11491160
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.This());
@@ -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)