Skip to content

Commit 4bc2ed8

Browse files
mcollinaaduh95
authored andcommitted
tls: fix SNICallback certificate selection
Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: #64700 Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: Tim Perry <pimterry@gmail.com>
1 parent 3da9d77 commit 4bc2ed8

2 files changed

Lines changed: 78 additions & 1 deletion

File tree

deps/ncrypto/ncrypto.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4083,7 +4083,13 @@ bool SSLPointer::setSniContext(const SSLCtxPointer& ctx) const {
40834083
EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx.get());
40844084
STACK_OF(X509) * chain;
40854085
int err = SSL_CTX_get0_chain_certs(ctx.get(), &chain);
4086-
if (err == 1) err = SSL_use_certificate(get(), x509);
4086+
if (err == 1) {
4087+
// SSL_use_certificate replaces only the certificate matching the key
4088+
// type. Clear all existing certificates so credentials from the default
4089+
// context cannot be selected for a different key type.
4090+
SSL_certs_clear(get());
4091+
err = SSL_use_certificate(get(), x509);
4092+
}
40874093
if (err == 1) err = SSL_use_PrivateKey(get(), pkey);
40884094
if (err == 1 && chain != nullptr) err = SSL_set1_chain(get(), chain);
40894095
return err == 1;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const { X509Certificate } = require('crypto');
9+
const https = require('https');
10+
const tls = require('tls');
11+
const fixtures = require('../common/fixtures');
12+
13+
const defaultCredentials = {
14+
cert: fixtures.readKey('ca5-cert.pem'),
15+
key: fixtures.readKey('ca5-key.pem'),
16+
};
17+
const sniCredentials = {
18+
cert: fixtures.readKey('agent1-cert.pem'),
19+
key: fixtures.readKey('agent1-key.pem'),
20+
};
21+
22+
const defaultCertificate = new X509Certificate(defaultCredentials.cert);
23+
const sniCertificate = new X509Certificate(sniCredentials.cert);
24+
const sniContext = tls.createSecureContext(sniCredentials);
25+
26+
function request(port, servername, expectedCertificate) {
27+
return new Promise((resolve, reject) => {
28+
const req = https.get({
29+
host: '127.0.0.1',
30+
port,
31+
servername,
32+
rejectUnauthorized: false,
33+
agent: false,
34+
}, common.mustCall((response) => {
35+
try {
36+
const certificate = response.socket.getPeerX509Certificate();
37+
assert.strictEqual(certificate.fingerprint256,
38+
expectedCertificate.fingerprint256);
39+
} catch (err) {
40+
reject(err);
41+
return;
42+
}
43+
44+
response.resume();
45+
response.once('end', resolve);
46+
response.once('error', reject);
47+
}));
48+
req.once('error', reject);
49+
});
50+
}
51+
52+
const server = https.createServer({
53+
cert: defaultCredentials.cert,
54+
key: defaultCredentials.key,
55+
SNICallback: common.mustCall((servername, callback) => {
56+
assert.strictEqual(servername, 'agent1.com');
57+
callback(null, sniContext);
58+
}, 1),
59+
}, (_request, response) => {
60+
response.end('ok');
61+
});
62+
63+
server.listen(0, common.mustCall(async () => {
64+
try {
65+
const { port } = server.address();
66+
await request(port, undefined, defaultCertificate);
67+
await request(port, 'agent1.com', sniCertificate);
68+
} finally {
69+
server.close(common.mustCall());
70+
}
71+
}));

0 commit comments

Comments
 (0)