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: https://github.com/nodejs/node/issues/24095 PR-URL: https://github.com/nodejs/node/pull/24261 Fixes: https://github.com/nodejs/node/issues/24095 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
This commit is contained in:
parent
98278584ee
commit
db35fee1e1
@ -566,6 +566,22 @@ added: v0.11.4
|
|||||||
Always returns `true`. This may be used to distinguish TLS sockets from regular
|
Always returns `true`. This may be used to distinguish TLS sockets from regular
|
||||||
`net.Socket` instances.
|
`net.Socket` instances.
|
||||||
|
|
||||||
|
### tlsSocket.getCertificate()
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Returns: {Object}
|
||||||
|
|
||||||
|
Returns an object representing the local certificate. The returned object has
|
||||||
|
some properties corresponding to the fields of the certificate.
|
||||||
|
|
||||||
|
See [`tls.TLSSocket.getPeerCertificate()`][] for an example of the certificate
|
||||||
|
structure.
|
||||||
|
|
||||||
|
If there is no local certificate, an empty object will be returned. If the
|
||||||
|
socket has been destroyed, `null` will be returned.
|
||||||
|
|
||||||
### tlsSocket.getCipher()
|
### tlsSocket.getCipher()
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.11.4
|
added: v0.11.4
|
||||||
@ -658,6 +674,7 @@ certificate.
|
|||||||
```
|
```
|
||||||
|
|
||||||
If the peer does not provide a certificate, an empty object will be returned.
|
If the peer does not provide a certificate, an empty object will be returned.
|
||||||
|
If the socket has been destroyed, `null` will be returned.
|
||||||
|
|
||||||
### tlsSocket.getPeerFinished()
|
### tlsSocket.getPeerFinished()
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
@ -202,6 +202,9 @@ exports.createSecureContext = function createSecureContext(options) {
|
|||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Translate some fields from the handle's C-friendly format into more idiomatic
|
||||||
|
// javascript object representations before passing them back to the user. Can
|
||||||
|
// be used on any cert object, but changing the name would be semver-major.
|
||||||
exports.translatePeerCertificate = function translatePeerCertificate(c) {
|
exports.translatePeerCertificate = function translatePeerCertificate(c) {
|
||||||
if (!c)
|
if (!c)
|
||||||
return null;
|
return null;
|
||||||
|
@ -660,7 +660,17 @@ TLSSocket.prototype.setSession = function(session) {
|
|||||||
TLSSocket.prototype.getPeerCertificate = function(detailed) {
|
TLSSocket.prototype.getPeerCertificate = function(detailed) {
|
||||||
if (this._handle) {
|
if (this._handle) {
|
||||||
return common.translatePeerCertificate(
|
return common.translatePeerCertificate(
|
||||||
this._handle.getPeerCertificate(detailed));
|
this._handle.getPeerCertificate(detailed)) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
TLSSocket.prototype.getCertificate = function() {
|
||||||
|
if (this._handle) {
|
||||||
|
// It's not a peer cert, but the formatting is identical.
|
||||||
|
return common.translatePeerCertificate(
|
||||||
|
this._handle.getCertificate()) || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -1409,6 +1409,7 @@ void SSLWrap<Base>::AddMethods(Environment* env, Local<FunctionTemplate> t) {
|
|||||||
HandleScope scope(env->isolate());
|
HandleScope scope(env->isolate());
|
||||||
|
|
||||||
env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate);
|
env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate);
|
||||||
|
env->SetProtoMethodNoSideEffect(t, "getCertificate", GetCertificate);
|
||||||
env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished);
|
env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished);
|
||||||
env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished);
|
env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished);
|
||||||
env->SetProtoMethodNoSideEffect(t, "getSession", GetSession);
|
env->SetProtoMethodNoSideEffect(t, "getSession", GetSession);
|
||||||
@ -1871,8 +1872,26 @@ void SSLWrap<Base>::GetPeerCertificate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (result.IsEmpty())
|
args.GetReturnValue().Set(result);
|
||||||
result = Object::New(env->isolate());
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class Base>
|
||||||
|
void SSLWrap<Base>::GetCertificate(
|
||||||
|
const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Base* w;
|
||||||
|
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
|
||||||
|
Environment* env = w->ssl_env();
|
||||||
|
|
||||||
|
ClearErrorOnReturn clear_error_on_return;
|
||||||
|
|
||||||
|
Local<Object> result;
|
||||||
|
|
||||||
|
X509Pointer cert(SSL_get_certificate(w->ssl_.get()));
|
||||||
|
|
||||||
|
if (cert)
|
||||||
|
result = X509ToObject(env, cert.get());
|
||||||
|
|
||||||
args.GetReturnValue().Set(result);
|
args.GetReturnValue().Set(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,6 +272,7 @@ class SSLWrap {
|
|||||||
|
|
||||||
static void GetPeerCertificate(
|
static void GetPeerCertificate(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void GetCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetPeerFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetPeerFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetSession(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetSession(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
@ -26,7 +26,6 @@ if (!common.hasCrypto)
|
|||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const tls = require('tls');
|
const tls = require('tls');
|
||||||
const util = require('util');
|
|
||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
@ -37,13 +36,22 @@ const options = {
|
|||||||
const server = tls.createServer(options, function(cleartext) {
|
const server = tls.createServer(options, function(cleartext) {
|
||||||
cleartext.end('World');
|
cleartext.end('World');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.once('secureConnection', common.mustCall(function(socket) {
|
||||||
|
const cert = socket.getCertificate();
|
||||||
|
// The server's local cert is the client's peer cert.
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
cert.subject.OU,
|
||||||
|
['Information Technology', 'Engineering', 'Marketing']
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
server.listen(0, common.mustCall(function() {
|
server.listen(0, common.mustCall(function() {
|
||||||
const socket = tls.connect({
|
const socket = tls.connect({
|
||||||
port: this.address().port,
|
port: this.address().port,
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
}, common.mustCall(function() {
|
}, common.mustCall(function() {
|
||||||
const peerCert = socket.getPeerCertificate();
|
const peerCert = socket.getPeerCertificate();
|
||||||
console.error(util.inspect(peerCert));
|
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
peerCert.subject.OU,
|
peerCert.subject.OU,
|
||||||
['Information Technology', 'Engineering', 'Marketing']
|
['Information Technology', 'Engineering', 'Marketing']
|
||||||
|
@ -43,6 +43,8 @@ connect({
|
|||||||
}, function(err, pair, cleanup) {
|
}, function(err, pair, cleanup) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
const socket = pair.client.conn;
|
const socket = pair.client.conn;
|
||||||
|
const localCert = socket.getCertificate();
|
||||||
|
assert.deepStrictEqual(localCert, {});
|
||||||
let peerCert = socket.getPeerCertificate();
|
let peerCert = socket.getPeerCertificate();
|
||||||
assert.ok(!peerCert.issuerCertificate);
|
assert.ok(!peerCert.issuerCertificate);
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ const server = tls
|
|||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
},
|
},
|
||||||
common.mustCall(function(c) {
|
common.mustCall(function(c) {
|
||||||
|
assert.strictEqual(c.getPeerCertificate().serialNumber,
|
||||||
|
'FAD50CC6A07F516C');
|
||||||
assert.strictEqual(c.authorizationError, null);
|
assert.strictEqual(c.authorizationError, null);
|
||||||
c.end();
|
c.end();
|
||||||
})
|
})
|
||||||
@ -35,6 +37,8 @@ const server = tls
|
|||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
|
assert.strictEqual(client.getCertificate().serialNumber,
|
||||||
|
'FAD50CC6A07F516C');
|
||||||
client.end();
|
client.end();
|
||||||
server.close();
|
server.close();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user