Skip to content

Commit d6f91ba

Browse files
sam-githubBridgeAR
authored andcommitted
tls: get the local certificate after tls handshake
Add an API to get the local certificate chosen during TLS handshake from the SSL context. Fix: #24095 PR-URL: #24261 Fixes: #24095 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
1 parent 753f706 commit d6f91ba

8 files changed

+69
-5
lines changed

doc/api/tls.md

+17
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,22 @@ added: v0.11.4
566566
Always returns `true`. This may be used to distinguish TLS sockets from regular
567567
`net.Socket` instances.
568568

569+
### tlsSocket.getCertificate()
570+
<!-- YAML
571+
added: REPLACEME
572+
-->
573+
574+
* Returns: {Object}
575+
576+
Returns an object representing the local certificate. The returned object has
577+
some properties corresponding to the fields of the certificate.
578+
579+
See [`tls.TLSSocket.getPeerCertificate()`][] for an example of the certificate
580+
structure.
581+
582+
If there is no local certificate, an empty object will be returned. If the
583+
socket has been destroyed, `null` will be returned.
584+
569585
### tlsSocket.getCipher()
570586
<!-- YAML
571587
added: v0.11.4
@@ -658,6 +674,7 @@ certificate.
658674
```
659675

660676
If the peer does not provide a certificate, an empty object will be returned.
677+
If the socket has been destroyed, `null` will be returned.
661678

662679
### tlsSocket.getPeerFinished()
663680
<!-- YAML

lib/_tls_common.js

+3
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ exports.createSecureContext = function createSecureContext(options, context) {
213213
return c;
214214
};
215215

216+
// Translate some fields from the handle's C-friendly format into more idiomatic
217+
// javascript object representations before passing them back to the user. Can
218+
// be used on any cert object, but changing the name would be semver-major.
216219
exports.translatePeerCertificate = function translatePeerCertificate(c) {
217220
if (!c)
218221
return null;

lib/_tls_wrap.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,17 @@ TLSSocket.prototype.setSession = function(session) {
660660
TLSSocket.prototype.getPeerCertificate = function(detailed) {
661661
if (this._handle) {
662662
return common.translatePeerCertificate(
663-
this._handle.getPeerCertificate(detailed));
663+
this._handle.getPeerCertificate(detailed)) || {};
664+
}
665+
666+
return null;
667+
};
668+
669+
TLSSocket.prototype.getCertificate = function() {
670+
if (this._handle) {
671+
// It's not a peer cert, but the formatting is identical.
672+
return common.translatePeerCertificate(
673+
this._handle.getCertificate()) || {};
664674
}
665675

666676
return null;

src/node_crypto.cc

+21-2
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,7 @@ void SSLWrap<Base>::AddMethods(Environment* env, Local<FunctionTemplate> t) {
13941394
HandleScope scope(env->isolate());
13951395

13961396
env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate);
1397+
env->SetProtoMethodNoSideEffect(t, "getCertificate", GetCertificate);
13971398
env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished);
13981399
env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished);
13991400
env->SetProtoMethodNoSideEffect(t, "getSession", GetSession);
@@ -1856,8 +1857,26 @@ void SSLWrap<Base>::GetPeerCertificate(
18561857
}
18571858

18581859
done:
1859-
if (result.IsEmpty())
1860-
result = Object::New(env->isolate());
1860+
args.GetReturnValue().Set(result);
1861+
}
1862+
1863+
1864+
template <class Base>
1865+
void SSLWrap<Base>::GetCertificate(
1866+
const FunctionCallbackInfo<Value>& args) {
1867+
Base* w;
1868+
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
1869+
Environment* env = w->ssl_env();
1870+
1871+
ClearErrorOnReturn clear_error_on_return;
1872+
1873+
Local<Object> result;
1874+
1875+
X509Pointer cert(SSL_get_certificate(w->ssl_.get()));
1876+
1877+
if (cert)
1878+
result = X509ToObject(env, cert.get());
1879+
18611880
args.GetReturnValue().Set(result);
18621881
}
18631882

src/node_crypto.h

+1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ class SSLWrap {
272272

273273
static void GetPeerCertificate(
274274
const v8::FunctionCallbackInfo<v8::Value>& args);
275+
static void GetCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
275276
static void GetFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
276277
static void GetPeerFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
277278
static void GetSession(const v8::FunctionCallbackInfo<v8::Value>& args);

test/parallel/test-tls-peer-certificate-multi-keys.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ if (!common.hasCrypto)
2626

2727
const assert = require('assert');
2828
const tls = require('tls');
29-
const util = require('util');
3029
const fixtures = require('../common/fixtures');
3130

3231
const options = {
@@ -37,13 +36,22 @@ const options = {
3736
const server = tls.createServer(options, function(cleartext) {
3837
cleartext.end('World');
3938
});
39+
40+
server.once('secureConnection', common.mustCall(function(socket) {
41+
const cert = socket.getCertificate();
42+
// The server's local cert is the client's peer cert.
43+
assert.deepStrictEqual(
44+
cert.subject.OU,
45+
['Information Technology', 'Engineering', 'Marketing']
46+
);
47+
}));
48+
4049
server.listen(0, common.mustCall(function() {
4150
const socket = tls.connect({
4251
port: this.address().port,
4352
rejectUnauthorized: false
4453
}, common.mustCall(function() {
4554
const peerCert = socket.getPeerCertificate();
46-
console.error(util.inspect(peerCert));
4755
assert.deepStrictEqual(
4856
peerCert.subject.OU,
4957
['Information Technology', 'Engineering', 'Marketing']

test/parallel/test-tls-peer-certificate.js

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ connect({
4343
}, function(err, pair, cleanup) {
4444
assert.ifError(err);
4545
const socket = pair.client.conn;
46+
const localCert = socket.getCertificate();
47+
assert.deepStrictEqual(localCert, {});
4648
let peerCert = socket.getPeerCertificate();
4749
assert.ok(!peerCert.issuerCertificate);
4850

test/parallel/test-tls-pfx-authorizationerror.js

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const server = tls
2222
rejectUnauthorized: false
2323
},
2424
common.mustCall(function(c) {
25+
assert.strictEqual(c.getPeerCertificate().serialNumber,
26+
'FAD50CC6A07F516C');
2527
assert.strictEqual(c.authorizationError, null);
2628
c.end();
2729
})
@@ -35,6 +37,8 @@ const server = tls
3537
rejectUnauthorized: false
3638
},
3739
function() {
40+
assert.strictEqual(client.getCertificate().serialNumber,
41+
'FAD50CC6A07F516C');
3842
client.end();
3943
server.close();
4044
}

0 commit comments

Comments
 (0)