GeezerVison is showing github.com — tls: add setKeyCert() to tls.Socket · nodejs/node@8660d48
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/tls.md +14Lines changed: 14 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1533,6 +1533,20 @@ When running as the server, the socket will be destroyed with an error after | 1533 | 1533 | For TLSv1.3, renegotiation cannot be initiated, it is not supported by the | 1534 | 1534 | protocol. | 1535 | 1535 |
| | 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 | + | 1536 | 1550 | ### `tlsSocket.setMaxSendFragment(size)` | 1537 | 1551 |
| 1538 | 1552 | <!-- YAML | |
Collapse file Copy file name to clipboardExpand all lines: lib/_tls_wrap.js +11Lines changed: 11 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1151,6 +1151,17 @@ TLSSocket.prototype.getX509Certificate = function() { | 1151 | 1151 | return cert ? new InternalX509Certificate(cert) : undefined; | 1152 | 1152 | }; | 1153 | 1153 |
| | 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 | + | 1154 | 1165 | // Proxy TLSSocket handle methods | 1155 | 1166 | function makeSocketMethodProxy(name) { | 1156 | 1167 | return function socketMethodProxy(...args) { | |
Collapse file Copy file name to clipboardExpand all lines: src/crypto/crypto_tls.cc +28Lines changed: 28 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1595,6 +1595,33 @@ void TLSWrap::SetALPNProtocols(const FunctionCallbackInfo<Value>& args) { | 1595 | 1595 | } | 1596 | 1596 | } | 1597 | 1597 |
| | 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 | + | 1598 | 1625 | void TLSWrap::GetPeerCertificate(const FunctionCallbackInfo<Value>& args) { | 1599 | 1626 | TLSWrap* w; | 1600 | 1627 | ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); | @@ -2130,6 +2157,7 @@ void TLSWrap::Initialize( | 2130 | 2157 | SetProtoMethod(isolate, t, "renegotiate", Renegotiate); | 2131 | 2158 | SetProtoMethod(isolate, t, "requestOCSP", RequestOCSP); | 2132 | 2159 | SetProtoMethod(isolate, t, "setALPNProtocols", SetALPNProtocols); | | 2160 | + SetProtoMethod(isolate, t, "setKeyCert", SetKeyCert); | 2133 | 2161 | SetProtoMethod(isolate, t, "setOCSPResponse", SetOCSPResponse); | 2134 | 2162 | SetProtoMethod(isolate, t, "setServername", SetServername); | 2135 | 2163 | SetProtoMethod(isolate, t, "setSession", SetSession); | |
Collapse file Copy file name to clipboardExpand all lines: src/crypto/crypto_tls.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -213,6 +213,7 @@ class TLSWrap : public AsyncWrap, | 213 | 213 | static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args); | 214 | 214 | static void RequestOCSP(const v8::FunctionCallbackInfo<v8::Value>& args); | 215 | 215 | static void SetALPNProtocols(const v8::FunctionCallbackInfo<v8::Value>& args); | | 216 | + static void SetKeyCert(const v8::FunctionCallbackInfo<v8::Value>& args); | 216 | 217 | static void SetOCSPResponse(const v8::FunctionCallbackInfo<v8::Value>& args); | 217 | 218 | static void SetServername(const v8::FunctionCallbackInfo<v8::Value>& args); | 218 | 219 | static void SetSession(const v8::FunctionCallbackInfo<v8::Value>& args); | |
Collapse file Copy file name to clipboard +56Lines changed: 56 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 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 | +})(); |
|