tls: add ability to get cert/peer cert as X509Certificate object
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37070 Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
This commit is contained in:
parent
84e41d27da
commit
c973d503e0
@ -1804,6 +1804,16 @@ added: v15.6.0
|
||||
|
||||
The issuer identification included in this certificate.
|
||||
|
||||
### `x509.issuerCertificate`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Type: {X509Certificate}
|
||||
|
||||
The issuer certificate or `undefined` if the issuer certificate is not
|
||||
available.
|
||||
|
||||
### `x509.keyUsage`
|
||||
<!-- YAML
|
||||
added: v15.6.0
|
||||
|
@ -904,6 +904,41 @@ added: v0.11.4
|
||||
Always returns `true`. This may be used to distinguish TLS sockets from regular
|
||||
`net.Socket` instances.
|
||||
|
||||
### `tlsSocket.exportKeyingMaterial(length, label[, context])`
|
||||
<!-- YAML
|
||||
added:
|
||||
- v13.10.0
|
||||
- v12.17.0
|
||||
-->
|
||||
|
||||
* `length` {number} number of bytes to retrieve from keying material
|
||||
* `label` {string} an application specific label, typically this will be a
|
||||
value from the
|
||||
[IANA Exporter Label Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#exporter-labels).
|
||||
* `context` {Buffer} Optionally provide a context.
|
||||
|
||||
* Returns: {Buffer} requested bytes of the keying material
|
||||
|
||||
Keying material is used for validations to prevent different kind of attacks in
|
||||
network protocols, for example in the specifications of IEEE 802.1X.
|
||||
|
||||
Example
|
||||
|
||||
```js
|
||||
const keyingMaterial = tlsSocket.exportKeyingMaterial(
|
||||
128,
|
||||
'client finished');
|
||||
|
||||
/**
|
||||
Example return value of keyingMaterial:
|
||||
<Buffer 76 26 af 99 c5 56 8e 42 09 91 ef 9f 93 cb ad 6c 7b 65 f8 53 f1 d8 d9
|
||||
12 5a 33 b8 b5 25 df 7b 37 9f e0 e2 4f b8 67 83 a3 2f cd 5d 41 42 4c 91
|
||||
74 ef 2c ... 78 more bytes>
|
||||
*/
|
||||
```
|
||||
See the OpenSSL [`SSL_export_keying_material`][] documentation for more
|
||||
information.
|
||||
|
||||
### `tlsSocket.getCertificate()`
|
||||
<!-- YAML
|
||||
added: v11.2.0
|
||||
@ -1113,6 +1148,18 @@ provided by SSL/TLS is not desired or is not enough.
|
||||
Corresponds to the `SSL_get_peer_finished` routine in OpenSSL and may be used
|
||||
to implement the `tls-unique` channel binding from [RFC 5929][].
|
||||
|
||||
### `tlsSocket.getPeerX509Certificate()`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Returns: {X509Certificate}
|
||||
|
||||
Returns the peer certificate as an {X509Certificate} object.
|
||||
|
||||
If there is no peer certificate, or the socket has been destroyed,
|
||||
`undefined` will be returned.
|
||||
|
||||
### `tlsSocket.getProtocol()`
|
||||
<!-- YAML
|
||||
added: v5.7.0
|
||||
@ -1164,41 +1211,6 @@ See
|
||||
[SSL_get_shared_sigalgs](https://www.openssl.org/docs/man1.1.1/man3/SSL_get_shared_sigalgs.html)
|
||||
for more information.
|
||||
|
||||
### `tlsSocket.exportKeyingMaterial(length, label[, context])`
|
||||
<!-- YAML
|
||||
added:
|
||||
- v13.10.0
|
||||
- v12.17.0
|
||||
-->
|
||||
|
||||
* `length` {number} number of bytes to retrieve from keying material
|
||||
* `label` {string} an application specific label, typically this will be a
|
||||
value from the
|
||||
[IANA Exporter Label Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#exporter-labels).
|
||||
* `context` {Buffer} Optionally provide a context.
|
||||
|
||||
* Returns: {Buffer} requested bytes of the keying material
|
||||
|
||||
Keying material is used for validations to prevent different kind of attacks in
|
||||
network protocols, for example in the specifications of IEEE 802.1X.
|
||||
|
||||
Example
|
||||
|
||||
```js
|
||||
const keyingMaterial = tlsSocket.exportKeyingMaterial(
|
||||
128,
|
||||
'client finished');
|
||||
|
||||
/**
|
||||
Example return value of keyingMaterial:
|
||||
<Buffer 76 26 af 99 c5 56 8e 42 09 91 ef 9f 93 cb ad 6c 7b 65 f8 53 f1 d8 d9
|
||||
12 5a 33 b8 b5 25 df 7b 37 9f e0 e2 4f b8 67 83 a3 2f cd 5d 41 42 4c 91
|
||||
74 ef 2c ... 78 more bytes>
|
||||
*/
|
||||
```
|
||||
See the OpenSSL [`SSL_export_keying_material`][] documentation for more
|
||||
information.
|
||||
|
||||
### `tlsSocket.getTLSTicket()`
|
||||
<!-- YAML
|
||||
added: v0.11.4
|
||||
@ -1213,6 +1225,18 @@ It may be useful for debugging.
|
||||
|
||||
See [Session Resumption][] for more information.
|
||||
|
||||
### `tlsSocket.getX509Certificate()`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Returns: {X509Certificate}
|
||||
|
||||
Returns the local certificate as an {X509Certificate} object.
|
||||
|
||||
If there is no local certificate, or the socket has been destroyed,
|
||||
`undefined` will be returned.
|
||||
|
||||
### `tlsSocket.isSessionReused()`
|
||||
<!-- YAML
|
||||
added: v0.5.6
|
||||
|
@ -90,6 +90,9 @@ const {
|
||||
validateString,
|
||||
validateUint32
|
||||
} = require('internal/validators');
|
||||
const {
|
||||
InternalX509Certificate
|
||||
} = require('internal/crypto/x509');
|
||||
const traceTls = getOptionValue('--trace-tls');
|
||||
const tlsKeylog = getOptionValue('--tls-keylog');
|
||||
const { appendFile } = require('fs');
|
||||
@ -998,6 +1001,16 @@ TLSSocket.prototype.getCertificate = function() {
|
||||
return null;
|
||||
};
|
||||
|
||||
TLSSocket.prototype.getPeerX509Certificate = function(detailed) {
|
||||
const cert = this._handle?.getPeerX509Certificate();
|
||||
return cert ? new InternalX509Certificate(cert) : undefined;
|
||||
};
|
||||
|
||||
TLSSocket.prototype.getX509Certificate = function() {
|
||||
const cert = this._handle?.getX509Certificate();
|
||||
return cert ? new InternalX509Certificate(cert) : undefined;
|
||||
};
|
||||
|
||||
// Proxy TLSSocket handle methods
|
||||
function makeSocketMethodProxy(name) {
|
||||
return function socketMethodProxy(...args) {
|
||||
|
@ -90,6 +90,15 @@ function getFlags(options = {}) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
class InternalX509Certificate extends JSTransferable {
|
||||
[kInternalState] = new SafeMap();
|
||||
|
||||
constructor(handle) {
|
||||
super();
|
||||
this[kHandle] = handle;
|
||||
}
|
||||
}
|
||||
|
||||
class X509Certificate extends JSTransferable {
|
||||
[kInternalState] = new SafeMap();
|
||||
|
||||
@ -168,6 +177,17 @@ class X509Certificate extends JSTransferable {
|
||||
return value;
|
||||
}
|
||||
|
||||
get issuerCertificate() {
|
||||
let value = this[kInternalState].get('issuerCertificate');
|
||||
if (value === undefined) {
|
||||
const cert = this[kHandle].getIssuerCert();
|
||||
if (cert)
|
||||
value = new InternalX509Certificate(this[kHandle].getIssuerCert());
|
||||
this[kInternalState].set('issuerCertificate', value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
get infoAccess() {
|
||||
let value = this[kInternalState].get('infoAccess');
|
||||
if (value === undefined) {
|
||||
@ -313,15 +333,6 @@ class X509Certificate extends JSTransferable {
|
||||
}
|
||||
}
|
||||
|
||||
class InternalX509Certificate extends JSTransferable {
|
||||
[kInternalState] = new SafeMap();
|
||||
|
||||
constructor(handle) {
|
||||
super();
|
||||
this[kHandle] = handle;
|
||||
}
|
||||
}
|
||||
|
||||
InternalX509Certificate.prototype.constructor = X509Certificate;
|
||||
ObjectSetPrototypeOf(
|
||||
InternalX509Certificate.prototype,
|
||||
|
@ -1591,6 +1591,20 @@ void TLSWrap::GetPeerCertificate(const FunctionCallbackInfo<Value>& args) {
|
||||
args.GetReturnValue().Set(ret);
|
||||
}
|
||||
|
||||
void TLSWrap::GetPeerX509Certificate(const FunctionCallbackInfo<Value>& args) {
|
||||
TLSWrap* w;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
|
||||
Environment* env = w->env();
|
||||
|
||||
X509Certificate::GetPeerCertificateFlag flag = w->is_server()
|
||||
? X509Certificate::GetPeerCertificateFlag::SERVER
|
||||
: X509Certificate::GetPeerCertificateFlag::NONE;
|
||||
|
||||
Local<Value> ret;
|
||||
if (X509Certificate::GetPeerCert(env, w->ssl_, flag).ToLocal(&ret))
|
||||
args.GetReturnValue().Set(ret);
|
||||
}
|
||||
|
||||
void TLSWrap::GetCertificate(const FunctionCallbackInfo<Value>& args) {
|
||||
TLSWrap* w;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
|
||||
@ -1601,6 +1615,15 @@ void TLSWrap::GetCertificate(const FunctionCallbackInfo<Value>& args) {
|
||||
args.GetReturnValue().Set(ret);
|
||||
}
|
||||
|
||||
void TLSWrap::GetX509Certificate(const FunctionCallbackInfo<Value>& args) {
|
||||
TLSWrap* w;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
|
||||
Environment* env = w->env();
|
||||
Local<Value> ret;
|
||||
if (X509Certificate::GetCert(env, w->ssl_).ToLocal(&ret))
|
||||
args.GetReturnValue().Set(ret);
|
||||
}
|
||||
|
||||
void TLSWrap::GetFinished(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
@ -2051,11 +2074,14 @@ void TLSWrap::Initialize(
|
||||
env->SetProtoMethodNoSideEffect(t, "getALPNNegotiatedProtocol",
|
||||
GetALPNNegotiatedProto);
|
||||
env->SetProtoMethodNoSideEffect(t, "getCertificate", GetCertificate);
|
||||
env->SetProtoMethodNoSideEffect(t, "getX509Certificate", GetX509Certificate);
|
||||
env->SetProtoMethodNoSideEffect(t, "getCipher", GetCipher);
|
||||
env->SetProtoMethodNoSideEffect(t, "getEphemeralKeyInfo",
|
||||
GetEphemeralKeyInfo);
|
||||
env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished);
|
||||
env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate);
|
||||
env->SetProtoMethodNoSideEffect(t, "getPeerX509Certificate",
|
||||
GetPeerX509Certificate);
|
||||
env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished);
|
||||
env->SetProtoMethodNoSideEffect(t, "getProtocol", GetProtocol);
|
||||
env->SetProtoMethodNoSideEffect(t, "getSession", GetSession);
|
||||
|
@ -184,12 +184,16 @@ class TLSWrap : public AsyncWrap,
|
||||
static void GetALPNNegotiatedProto(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetX509Certificate(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetCipher(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetEphemeralKeyInfo(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetPeerCertificate(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetPeerX509Certificate(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetPeerFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetProtocol(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetServername(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
namespace node {
|
||||
|
||||
using v8::Array;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
@ -82,6 +81,7 @@ Local<FunctionTemplate> X509Certificate::GetConstructorTemplate(
|
||||
env->SetProtoMethod(tmpl, "checkPrivateKey", CheckPrivateKey);
|
||||
env->SetProtoMethod(tmpl, "verify", Verify);
|
||||
env->SetProtoMethod(tmpl, "toLegacy", ToLegacy);
|
||||
env->SetProtoMethod(tmpl, "getIssuerCert", GetIssuerCert);
|
||||
env->set_x509_constructor_template(tmpl);
|
||||
}
|
||||
return tmpl;
|
||||
@ -93,14 +93,16 @@ bool X509Certificate::HasInstance(Environment* env, Local<Object> object) {
|
||||
|
||||
MaybeLocal<Object> X509Certificate::New(
|
||||
Environment* env,
|
||||
X509Pointer cert) {
|
||||
X509Pointer cert,
|
||||
STACK_OF(X509)* issuer_chain) {
|
||||
std::shared_ptr<ManagedX509> mcert(new ManagedX509(std::move(cert)));
|
||||
return New(env, std::move(mcert));
|
||||
return New(env, std::move(mcert), issuer_chain);
|
||||
}
|
||||
|
||||
MaybeLocal<Object> X509Certificate::New(
|
||||
Environment* env,
|
||||
std::shared_ptr<ManagedX509> cert) {
|
||||
std::shared_ptr<ManagedX509> cert,
|
||||
STACK_OF(X509)* issuer_chain) {
|
||||
EscapableHandleScope scope(env->isolate());
|
||||
Local<Function> ctor;
|
||||
if (!GetConstructorTemplate(env)->GetFunction(env->context()).ToLocal(&ctor))
|
||||
@ -110,7 +112,7 @@ MaybeLocal<Object> X509Certificate::New(
|
||||
if (!ctor->NewInstance(env->context()).ToLocal(&obj))
|
||||
return MaybeLocal<Object>();
|
||||
|
||||
new X509Certificate(env, obj, std::move(cert));
|
||||
new X509Certificate(env, obj, std::move(cert), issuer_chain);
|
||||
return scope.Escape(obj);
|
||||
}
|
||||
|
||||
@ -122,7 +124,7 @@ MaybeLocal<Object> X509Certificate::GetCert(
|
||||
if (cert == nullptr)
|
||||
return MaybeLocal<Object>();
|
||||
|
||||
X509Pointer ptr(cert);
|
||||
X509Pointer ptr(X509_dup(cert));
|
||||
return New(env, std::move(ptr));
|
||||
}
|
||||
|
||||
@ -130,16 +132,12 @@ MaybeLocal<Object> X509Certificate::GetPeerCert(
|
||||
Environment* env,
|
||||
const SSLPointer& ssl,
|
||||
GetPeerCertificateFlag flag) {
|
||||
EscapableHandleScope scope(env->isolate());
|
||||
ClearErrorOnReturn clear_error_on_return;
|
||||
Local<Object> obj;
|
||||
MaybeLocal<Object> maybe_cert;
|
||||
|
||||
bool is_server =
|
||||
static_cast<int>(flag) & static_cast<int>(GetPeerCertificateFlag::SERVER);
|
||||
bool abbreviated =
|
||||
static_cast<int>(flag)
|
||||
& static_cast<int>(GetPeerCertificateFlag::ABBREVIATED);
|
||||
|
||||
X509Pointer cert(is_server ? SSL_get_peer_certificate(ssl.get()) : nullptr);
|
||||
STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl.get());
|
||||
@ -148,23 +146,14 @@ MaybeLocal<Object> X509Certificate::GetPeerCert(
|
||||
|
||||
std::vector<Local<Value>> certs;
|
||||
|
||||
if (!cert) cert.reset(sk_X509_value(ssl_certs, 0));
|
||||
if (!X509Certificate::New(env, std::move(cert)).ToLocal(&obj))
|
||||
return MaybeLocal<Object>();
|
||||
|
||||
certs.push_back(obj);
|
||||
|
||||
int count = sk_X509_num(ssl_certs);
|
||||
if (!abbreviated) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
cert.reset(X509_dup(sk_X509_value(ssl_certs, i)));
|
||||
if (!cert || !X509Certificate::New(env, std::move(cert)).ToLocal(&obj))
|
||||
return MaybeLocal<Object>();
|
||||
certs.push_back(obj);
|
||||
}
|
||||
if (!cert) {
|
||||
cert.reset(sk_X509_value(ssl_certs, 0));
|
||||
sk_X509_delete(ssl_certs, 0);
|
||||
}
|
||||
|
||||
return scope.Escape(Array::New(env->isolate(), certs.data(), certs.size()));
|
||||
return sk_X509_num(ssl_certs)
|
||||
? New(env, std::move(cert), ssl_certs)
|
||||
: New(env, std::move(cert));
|
||||
}
|
||||
|
||||
void X509Certificate::Parse(const FunctionCallbackInfo<Value>& args) {
|
||||
@ -475,13 +464,32 @@ void X509Certificate::ToLegacy(const FunctionCallbackInfo<Value>& args) {
|
||||
args.GetReturnValue().Set(ret);
|
||||
}
|
||||
|
||||
void X509Certificate::GetIssuerCert(const FunctionCallbackInfo<Value>& args) {
|
||||
X509Certificate* cert;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder());
|
||||
if (cert->issuer_cert_)
|
||||
args.GetReturnValue().Set(cert->issuer_cert_->object());
|
||||
}
|
||||
|
||||
X509Certificate::X509Certificate(
|
||||
Environment* env,
|
||||
Local<Object> object,
|
||||
std::shared_ptr<ManagedX509> cert)
|
||||
std::shared_ptr<ManagedX509> cert,
|
||||
STACK_OF(X509)* issuer_chain)
|
||||
: BaseObject(env, object),
|
||||
cert_(std::move(cert)) {
|
||||
MakeWeak();
|
||||
|
||||
if (issuer_chain != nullptr && sk_X509_num(issuer_chain)) {
|
||||
X509Pointer cert(X509_dup(sk_X509_value(issuer_chain, 0)));
|
||||
sk_X509_delete(issuer_chain, 0);
|
||||
Local<Object> obj = sk_X509_num(issuer_chain)
|
||||
? X509Certificate::New(env, std::move(cert), issuer_chain)
|
||||
.ToLocalChecked()
|
||||
: X509Certificate::New(env, std::move(cert))
|
||||
.ToLocalChecked();
|
||||
issuer_cert_.reset(Unwrap<X509Certificate>(obj));
|
||||
}
|
||||
}
|
||||
|
||||
void X509Certificate::MemoryInfo(MemoryTracker* tracker) const {
|
||||
|
@ -38,7 +38,7 @@ class ManagedX509 : public MemoryRetainer {
|
||||
class X509Certificate : public BaseObject {
|
||||
public:
|
||||
enum class GetPeerCertificateFlag {
|
||||
ABBREVIATED,
|
||||
NONE,
|
||||
SERVER
|
||||
};
|
||||
|
||||
@ -49,11 +49,13 @@ class X509Certificate : public BaseObject {
|
||||
|
||||
static v8::MaybeLocal<v8::Object> New(
|
||||
Environment* env,
|
||||
X509Pointer cert);
|
||||
X509Pointer cert,
|
||||
STACK_OF(X509)* issuer_chain = nullptr);
|
||||
|
||||
static v8::MaybeLocal<v8::Object> New(
|
||||
Environment* env,
|
||||
std::shared_ptr<ManagedX509> cert);
|
||||
std::shared_ptr<ManagedX509> cert,
|
||||
STACK_OF(X509)* issuer_chain = nullptr);
|
||||
|
||||
static v8::MaybeLocal<v8::Object> GetCert(
|
||||
Environment* env,
|
||||
@ -91,6 +93,7 @@ class X509Certificate : public BaseObject {
|
||||
static void CheckPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Verify(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void ToLegacy(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetIssuerCert(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
X509* get() { return cert_->get(); }
|
||||
|
||||
@ -124,9 +127,11 @@ class X509Certificate : public BaseObject {
|
||||
X509Certificate(
|
||||
Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
std::shared_ptr<ManagedX509> cert);
|
||||
std::shared_ptr<ManagedX509> cert,
|
||||
STACK_OF(X509)* issuer_chain = nullptr);
|
||||
|
||||
std::shared_ptr<ManagedX509> cert_;
|
||||
BaseObjectPtr<X509Certificate> issuer_cert_;
|
||||
};
|
||||
|
||||
} // namespace crypto
|
||||
|
43
test/parallel/test-tls-getcertificate-x509.js
Normal file
43
test/parallel/test-tls-getcertificate-x509.js
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const { X509Certificate } = require('crypto');
|
||||
|
||||
const options = {
|
||||
key: fixtures.readKey('agent6-key.pem'),
|
||||
cert: fixtures.readKey('agent6-cert.pem')
|
||||
};
|
||||
|
||||
const server = tls.createServer(options, function(cleartext) {
|
||||
cleartext.end('World');
|
||||
});
|
||||
|
||||
server.once('secureConnection', common.mustCall(function(socket) {
|
||||
const cert = socket.getX509Certificate();
|
||||
assert(cert instanceof X509Certificate);
|
||||
assert.strictEqual(
|
||||
cert.serialNumber,
|
||||
'D0082F458B6EFBE8');
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(function() {
|
||||
const socket = tls.connect({
|
||||
port: this.address().port,
|
||||
rejectUnauthorized: false
|
||||
}, common.mustCall(function() {
|
||||
const peerCert = socket.getPeerX509Certificate();
|
||||
assert(peerCert.issuerCertificate instanceof X509Certificate);
|
||||
assert.strictEqual(peerCert.issuerCertificate.issuerCertificate, undefined);
|
||||
assert.strictEqual(
|
||||
peerCert.issuerCertificate.serialNumber,
|
||||
'ECC9B856270DA9A7'
|
||||
);
|
||||
server.close();
|
||||
}));
|
||||
socket.end('Hello');
|
||||
}));
|
Loading…
x
Reference in New Issue
Block a user