Skip to content

Commit c973d50

Browse files
committed
tls: add ability to get cert/peer cert as X509Certificate object
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #37070 Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent 84e41d2 commit c973d50

File tree

9 files changed

+218
-74
lines changed

9 files changed

+218
-74
lines changed

doc/api/crypto.md

+10
Original file line numberDiff line numberDiff line change
@@ -1804,6 +1804,16 @@ added: v15.6.0
18041804

18051805
The issuer identification included in this certificate.
18061806

1807+
### `x509.issuerCertificate`
1808+
<!-- YAML
1809+
added: REPLACEME
1810+
-->
1811+
1812+
* Type: {X509Certificate}
1813+
1814+
The issuer certificate or `undefined` if the issuer certificate is not
1815+
available.
1816+
18071817
### `x509.keyUsage`
18081818
<!-- YAML
18091819
added: v15.6.0

doc/api/tls.md

+59-35
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,41 @@ added: v0.11.4
904904
Always returns `true`. This may be used to distinguish TLS sockets from regular
905905
`net.Socket` instances.
906906

907+
### `tlsSocket.exportKeyingMaterial(length, label[, context])`
908+
<!-- YAML
909+
added:
910+
- v13.10.0
911+
- v12.17.0
912+
-->
913+
914+
* `length` {number} number of bytes to retrieve from keying material
915+
* `label` {string} an application specific label, typically this will be a
916+
value from the
917+
[IANA Exporter Label Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#exporter-labels).
918+
* `context` {Buffer} Optionally provide a context.
919+
920+
* Returns: {Buffer} requested bytes of the keying material
921+
922+
Keying material is used for validations to prevent different kind of attacks in
923+
network protocols, for example in the specifications of IEEE 802.1X.
924+
925+
Example
926+
927+
```js
928+
const keyingMaterial = tlsSocket.exportKeyingMaterial(
929+
128,
930+
'client finished');
931+
932+
/**
933+
Example return value of keyingMaterial:
934+
<Buffer 76 26 af 99 c5 56 8e 42 09 91 ef 9f 93 cb ad 6c 7b 65 f8 53 f1 d8 d9
935+
12 5a 33 b8 b5 25 df 7b 37 9f e0 e2 4f b8 67 83 a3 2f cd 5d 41 42 4c 91
936+
74 ef 2c ... 78 more bytes>
937+
*/
938+
```
939+
See the OpenSSL [`SSL_export_keying_material`][] documentation for more
940+
information.
941+
907942
### `tlsSocket.getCertificate()`
908943
<!-- YAML
909944
added: v11.2.0
@@ -1113,6 +1148,18 @@ provided by SSL/TLS is not desired or is not enough.
11131148
Corresponds to the `SSL_get_peer_finished` routine in OpenSSL and may be used
11141149
to implement the `tls-unique` channel binding from [RFC 5929][].
11151150

1151+
### `tlsSocket.getPeerX509Certificate()`
1152+
<!-- YAML
1153+
added: REPLACEME
1154+
-->
1155+
1156+
* Returns: {X509Certificate}
1157+
1158+
Returns the peer certificate as an {X509Certificate} object.
1159+
1160+
If there is no peer certificate, or the socket has been destroyed,
1161+
`undefined` will be returned.
1162+
11161163
### `tlsSocket.getProtocol()`
11171164
<!-- YAML
11181165
added: v5.7.0
@@ -1164,41 +1211,6 @@ See
11641211
[SSL_get_shared_sigalgs](https://www.openssl.org/docs/man1.1.1/man3/SSL_get_shared_sigalgs.html)
11651212
for more information.
11661213

1167-
### `tlsSocket.exportKeyingMaterial(length, label[, context])`
1168-
<!-- YAML
1169-
added:
1170-
- v13.10.0
1171-
- v12.17.0
1172-
-->
1173-
1174-
* `length` {number} number of bytes to retrieve from keying material
1175-
* `label` {string} an application specific label, typically this will be a
1176-
value from the
1177-
[IANA Exporter Label Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#exporter-labels).
1178-
* `context` {Buffer} Optionally provide a context.
1179-
1180-
* Returns: {Buffer} requested bytes of the keying material
1181-
1182-
Keying material is used for validations to prevent different kind of attacks in
1183-
network protocols, for example in the specifications of IEEE 802.1X.
1184-
1185-
Example
1186-
1187-
```js
1188-
const keyingMaterial = tlsSocket.exportKeyingMaterial(
1189-
128,
1190-
'client finished');
1191-
1192-
/**
1193-
Example return value of keyingMaterial:
1194-
<Buffer 76 26 af 99 c5 56 8e 42 09 91 ef 9f 93 cb ad 6c 7b 65 f8 53 f1 d8 d9
1195-
12 5a 33 b8 b5 25 df 7b 37 9f e0 e2 4f b8 67 83 a3 2f cd 5d 41 42 4c 91
1196-
74 ef 2c ... 78 more bytes>
1197-
*/
1198-
```
1199-
See the OpenSSL [`SSL_export_keying_material`][] documentation for more
1200-
information.
1201-
12021214
### `tlsSocket.getTLSTicket()`
12031215
<!-- YAML
12041216
added: v0.11.4
@@ -1213,6 +1225,18 @@ It may be useful for debugging.
12131225

12141226
See [Session Resumption][] for more information.
12151227

1228+
### `tlsSocket.getX509Certificate()`
1229+
<!-- YAML
1230+
added: REPLACEME
1231+
-->
1232+
1233+
* Returns: {X509Certificate}
1234+
1235+
Returns the local certificate as an {X509Certificate} object.
1236+
1237+
If there is no local certificate, or the socket has been destroyed,
1238+
`undefined` will be returned.
1239+
12161240
### `tlsSocket.isSessionReused()`
12171241
<!-- YAML
12181242
added: v0.5.6

lib/_tls_wrap.js

+13
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ const {
9090
validateString,
9191
validateUint32
9292
} = require('internal/validators');
93+
const {
94+
InternalX509Certificate
95+
} = require('internal/crypto/x509');
9396
const traceTls = getOptionValue('--trace-tls');
9497
const tlsKeylog = getOptionValue('--tls-keylog');
9598
const { appendFile } = require('fs');
@@ -998,6 +1001,16 @@ TLSSocket.prototype.getCertificate = function() {
9981001
return null;
9991002
};
10001003

1004+
TLSSocket.prototype.getPeerX509Certificate = function(detailed) {
1005+
const cert = this._handle?.getPeerX509Certificate();
1006+
return cert ? new InternalX509Certificate(cert) : undefined;
1007+
};
1008+
1009+
TLSSocket.prototype.getX509Certificate = function() {
1010+
const cert = this._handle?.getX509Certificate();
1011+
return cert ? new InternalX509Certificate(cert) : undefined;
1012+
};
1013+
10011014
// Proxy TLSSocket handle methods
10021015
function makeSocketMethodProxy(name) {
10031016
return function socketMethodProxy(...args) {

lib/internal/crypto/x509.js

+20-9
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ function getFlags(options = {}) {
9090
return flags;
9191
}
9292

93+
class InternalX509Certificate extends JSTransferable {
94+
[kInternalState] = new SafeMap();
95+
96+
constructor(handle) {
97+
super();
98+
this[kHandle] = handle;
99+
}
100+
}
101+
93102
class X509Certificate extends JSTransferable {
94103
[kInternalState] = new SafeMap();
95104

@@ -168,6 +177,17 @@ class X509Certificate extends JSTransferable {
168177
return value;
169178
}
170179

180+
get issuerCertificate() {
181+
let value = this[kInternalState].get('issuerCertificate');
182+
if (value === undefined) {
183+
const cert = this[kHandle].getIssuerCert();
184+
if (cert)
185+
value = new InternalX509Certificate(this[kHandle].getIssuerCert());
186+
this[kInternalState].set('issuerCertificate', value);
187+
}
188+
return value;
189+
}
190+
171191
get infoAccess() {
172192
let value = this[kInternalState].get('infoAccess');
173193
if (value === undefined) {
@@ -313,15 +333,6 @@ class X509Certificate extends JSTransferable {
313333
}
314334
}
315335

316-
class InternalX509Certificate extends JSTransferable {
317-
[kInternalState] = new SafeMap();
318-
319-
constructor(handle) {
320-
super();
321-
this[kHandle] = handle;
322-
}
323-
}
324-
325336
InternalX509Certificate.prototype.constructor = X509Certificate;
326337
ObjectSetPrototypeOf(
327338
InternalX509Certificate.prototype,

src/crypto/crypto_tls.cc

+26
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,20 @@ void TLSWrap::GetPeerCertificate(const FunctionCallbackInfo<Value>& args) {
15911591
args.GetReturnValue().Set(ret);
15921592
}
15931593

1594+
void TLSWrap::GetPeerX509Certificate(const FunctionCallbackInfo<Value>& args) {
1595+
TLSWrap* w;
1596+
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
1597+
Environment* env = w->env();
1598+
1599+
X509Certificate::GetPeerCertificateFlag flag = w->is_server()
1600+
? X509Certificate::GetPeerCertificateFlag::SERVER
1601+
: X509Certificate::GetPeerCertificateFlag::NONE;
1602+
1603+
Local<Value> ret;
1604+
if (X509Certificate::GetPeerCert(env, w->ssl_, flag).ToLocal(&ret))
1605+
args.GetReturnValue().Set(ret);
1606+
}
1607+
15941608
void TLSWrap::GetCertificate(const FunctionCallbackInfo<Value>& args) {
15951609
TLSWrap* w;
15961610
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
@@ -1601,6 +1615,15 @@ void TLSWrap::GetCertificate(const FunctionCallbackInfo<Value>& args) {
16011615
args.GetReturnValue().Set(ret);
16021616
}
16031617

1618+
void TLSWrap::GetX509Certificate(const FunctionCallbackInfo<Value>& args) {
1619+
TLSWrap* w;
1620+
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
1621+
Environment* env = w->env();
1622+
Local<Value> ret;
1623+
if (X509Certificate::GetCert(env, w->ssl_).ToLocal(&ret))
1624+
args.GetReturnValue().Set(ret);
1625+
}
1626+
16041627
void TLSWrap::GetFinished(const FunctionCallbackInfo<Value>& args) {
16051628
Environment* env = Environment::GetCurrent(args);
16061629

@@ -2051,11 +2074,14 @@ void TLSWrap::Initialize(
20512074
env->SetProtoMethodNoSideEffect(t, "getALPNNegotiatedProtocol",
20522075
GetALPNNegotiatedProto);
20532076
env->SetProtoMethodNoSideEffect(t, "getCertificate", GetCertificate);
2077+
env->SetProtoMethodNoSideEffect(t, "getX509Certificate", GetX509Certificate);
20542078
env->SetProtoMethodNoSideEffect(t, "getCipher", GetCipher);
20552079
env->SetProtoMethodNoSideEffect(t, "getEphemeralKeyInfo",
20562080
GetEphemeralKeyInfo);
20572081
env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished);
20582082
env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate);
2083+
env->SetProtoMethodNoSideEffect(t, "getPeerX509Certificate",
2084+
GetPeerX509Certificate);
20592085
env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished);
20602086
env->SetProtoMethodNoSideEffect(t, "getProtocol", GetProtocol);
20612087
env->SetProtoMethodNoSideEffect(t, "getSession", GetSession);

src/crypto/crypto_tls.h

+4
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,16 @@ class TLSWrap : public AsyncWrap,
184184
static void GetALPNNegotiatedProto(
185185
const v8::FunctionCallbackInfo<v8::Value>& args);
186186
static void GetCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
187+
static void GetX509Certificate(
188+
const v8::FunctionCallbackInfo<v8::Value>& args);
187189
static void GetCipher(const v8::FunctionCallbackInfo<v8::Value>& args);
188190
static void GetEphemeralKeyInfo(
189191
const v8::FunctionCallbackInfo<v8::Value>& args);
190192
static void GetFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
191193
static void GetPeerCertificate(
192194
const v8::FunctionCallbackInfo<v8::Value>& args);
195+
static void GetPeerX509Certificate(
196+
const v8::FunctionCallbackInfo<v8::Value>& args);
193197
static void GetPeerFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
194198
static void GetProtocol(const v8::FunctionCallbackInfo<v8::Value>& args);
195199
static void GetServername(const v8::FunctionCallbackInfo<v8::Value>& args);

0 commit comments

Comments
 (0)