tls: implement clientCertEngine option
Add an option 'clientCertEngine' to `tls.createSecureContext()` which gets wired up to OpenSSL function `SSL_CTX_set_client_cert_engine`. The option is passed through from `https.request()` as well. This allows using a custom OpenSSL engine to provide the client certificate.
This commit is contained in:
parent
f7436ba135
commit
6ee985f311
@ -653,6 +653,12 @@ Used when `Console` is instantiated without `stdout` stream or when `stdout` or
|
|||||||
|
|
||||||
Used when the native call from `process.cpuUsage` cannot be processed properly.
|
Used when the native call from `process.cpuUsage` cannot be processed properly.
|
||||||
|
|
||||||
|
<a id="ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED"></a>
|
||||||
|
### ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED
|
||||||
|
|
||||||
|
Used when a client certificate engine is requested that is not supported by the
|
||||||
|
version of OpenSSL being used.
|
||||||
|
|
||||||
<a id="ERR_CRYPTO_ECDH_INVALID_FORMAT"></a>
|
<a id="ERR_CRYPTO_ECDH_INVALID_FORMAT"></a>
|
||||||
### ERR_CRYPTO_ECDH_INVALID_FORMAT
|
### ERR_CRYPTO_ECDH_INVALID_FORMAT
|
||||||
|
|
||||||
|
@ -208,6 +208,18 @@ exports.createSecureContext = function createSecureContext(options, context) {
|
|||||||
c.context.setFreeListLength(0);
|
c.context.setFreeListLength(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof options.clientCertEngine === 'string') {
|
||||||
|
if (c.context.setClientCertEngine)
|
||||||
|
c.context.setClientCertEngine(options.clientCertEngine);
|
||||||
|
else
|
||||||
|
throw new errors.Error('ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED');
|
||||||
|
} else if (options.clientCertEngine != null) {
|
||||||
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
||||||
|
'options.clientCertEngine',
|
||||||
|
['string', 'null', 'undefined'],
|
||||||
|
options.clientCertEngine);
|
||||||
|
}
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -816,6 +816,7 @@ function tlsConnectionListener(rawSocket) {
|
|||||||
// - rejectUnauthorized. Boolean, default to true.
|
// - rejectUnauthorized. Boolean, default to true.
|
||||||
// - key. string.
|
// - key. string.
|
||||||
// - cert: string.
|
// - cert: string.
|
||||||
|
// - clientCertEngine: string.
|
||||||
// - ca: string or array of strings.
|
// - ca: string or array of strings.
|
||||||
// - sessionTimeout: integer.
|
// - sessionTimeout: integer.
|
||||||
//
|
//
|
||||||
@ -859,6 +860,7 @@ function Server(options, listener) {
|
|||||||
key: this.key,
|
key: this.key,
|
||||||
passphrase: this.passphrase,
|
passphrase: this.passphrase,
|
||||||
cert: this.cert,
|
cert: this.cert,
|
||||||
|
clientCertEngine: this.clientCertEngine,
|
||||||
ca: this.ca,
|
ca: this.ca,
|
||||||
ciphers: this.ciphers,
|
ciphers: this.ciphers,
|
||||||
ecdhCurve: this.ecdhCurve,
|
ecdhCurve: this.ecdhCurve,
|
||||||
@ -931,6 +933,8 @@ Server.prototype.setOptions = function(options) {
|
|||||||
if (options.key) this.key = options.key;
|
if (options.key) this.key = options.key;
|
||||||
if (options.passphrase) this.passphrase = options.passphrase;
|
if (options.passphrase) this.passphrase = options.passphrase;
|
||||||
if (options.cert) this.cert = options.cert;
|
if (options.cert) this.cert = options.cert;
|
||||||
|
if (options.clientCertEngine)
|
||||||
|
this.clientCertEngine = options.clientCertEngine;
|
||||||
if (options.ca) this.ca = options.ca;
|
if (options.ca) this.ca = options.ca;
|
||||||
if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
|
if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
|
||||||
if (options.crl) this.crl = options.crl;
|
if (options.crl) this.crl = options.crl;
|
||||||
|
@ -160,6 +160,10 @@ Agent.prototype.getName = function getName(options) {
|
|||||||
if (options.cert)
|
if (options.cert)
|
||||||
name += options.cert;
|
name += options.cert;
|
||||||
|
|
||||||
|
name += ':';
|
||||||
|
if (options.clientCertEngine)
|
||||||
|
name += options.clientCertEngine;
|
||||||
|
|
||||||
name += ':';
|
name += ':';
|
||||||
if (options.ciphers)
|
if (options.ciphers)
|
||||||
name += options.ciphers;
|
name += options.ciphers;
|
||||||
|
@ -232,6 +232,8 @@ E('ERR_CHILD_CLOSED_BEFORE_REPLY', 'Child closed before reply received');
|
|||||||
E('ERR_CONSOLE_WRITABLE_STREAM',
|
E('ERR_CONSOLE_WRITABLE_STREAM',
|
||||||
'Console expects a writable stream instance for %s');
|
'Console expects a writable stream instance for %s');
|
||||||
E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s');
|
E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s');
|
||||||
|
E('ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED',
|
||||||
|
'Custom engines not supported by this OpenSSL');
|
||||||
E('ERR_CRYPTO_ECDH_INVALID_FORMAT', 'Invalid ECDH format: %s');
|
E('ERR_CRYPTO_ECDH_INVALID_FORMAT', 'Invalid ECDH format: %s');
|
||||||
E('ERR_CRYPTO_ENGINE_UNKNOWN', 'Engine "%s" was not found');
|
E('ERR_CRYPTO_ENGINE_UNKNOWN', 'Engine "%s" was not found');
|
||||||
E('ERR_CRYPTO_FIPS_FORCED',
|
E('ERR_CRYPTO_FIPS_FORCED',
|
||||||
|
@ -354,6 +354,41 @@ static int PasswordCallback(char *buf, int size, int rwflag, void *u) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loads OpenSSL engine by engine id and returns it. The loaded engine
|
||||||
|
// gets a reference so remember the corresponding call to ENGINE_free.
|
||||||
|
// In case of error the appropriate js exception is scheduled
|
||||||
|
// and nullptr is returned.
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
static ENGINE* LoadEngineById(const char* engine_id, char (*errmsg)[1024]) {
|
||||||
|
MarkPopErrorOnReturn mark_pop_error_on_return;
|
||||||
|
|
||||||
|
ENGINE* engine = ENGINE_by_id(engine_id);
|
||||||
|
|
||||||
|
if (engine == nullptr) {
|
||||||
|
// Engine not found, try loading dynamically.
|
||||||
|
engine = ENGINE_by_id("dynamic");
|
||||||
|
if (engine != nullptr) {
|
||||||
|
if (!ENGINE_ctrl_cmd_string(engine, "SO_PATH", engine_id, 0) ||
|
||||||
|
!ENGINE_ctrl_cmd_string(engine, "LOAD", nullptr, 0)) {
|
||||||
|
ENGINE_free(engine);
|
||||||
|
engine = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine == nullptr) {
|
||||||
|
int err = ERR_get_error();
|
||||||
|
if (err != 0) {
|
||||||
|
ERR_error_string_n(err, *errmsg, sizeof(*errmsg));
|
||||||
|
} else {
|
||||||
|
snprintf(*errmsg, sizeof(*errmsg),
|
||||||
|
"Engine \"%s\" was not found", engine_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
#endif // !OPENSSL_NO_ENGINE
|
||||||
|
|
||||||
// This callback is used to avoid the default passphrase callback in OpenSSL
|
// This callback is used to avoid the default passphrase callback in OpenSSL
|
||||||
// which will typically prompt for the passphrase. The prompting is designed
|
// which will typically prompt for the passphrase. The prompting is designed
|
||||||
@ -498,6 +533,10 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
|
|||||||
SecureContext::SetSessionTimeout);
|
SecureContext::SetSessionTimeout);
|
||||||
env->SetProtoMethod(t, "close", SecureContext::Close);
|
env->SetProtoMethod(t, "close", SecureContext::Close);
|
||||||
env->SetProtoMethod(t, "loadPKCS12", SecureContext::LoadPKCS12);
|
env->SetProtoMethod(t, "loadPKCS12", SecureContext::LoadPKCS12);
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
env->SetProtoMethod(t, "setClientCertEngine",
|
||||||
|
SecureContext::SetClientCertEngine);
|
||||||
|
#endif // !OPENSSL_NO_ENGINE
|
||||||
env->SetProtoMethod(t, "getTicketKeys", SecureContext::GetTicketKeys);
|
env->SetProtoMethod(t, "getTicketKeys", SecureContext::GetTicketKeys);
|
||||||
env->SetProtoMethod(t, "setTicketKeys", SecureContext::SetTicketKeys);
|
env->SetProtoMethod(t, "setTicketKeys", SecureContext::SetTicketKeys);
|
||||||
env->SetProtoMethod(t, "setFreeListLength", SecureContext::SetFreeListLength);
|
env->SetProtoMethod(t, "setFreeListLength", SecureContext::SetFreeListLength);
|
||||||
@ -1295,6 +1334,46 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
void SecureContext::SetClientCertEngine(
|
||||||
|
const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
CHECK_EQ(args.Length(), 1);
|
||||||
|
CHECK(args[0]->IsString());
|
||||||
|
|
||||||
|
SecureContext* sc = Unwrap<SecureContext>(args.This());
|
||||||
|
|
||||||
|
MarkPopErrorOnReturn mark_pop_error_on_return;
|
||||||
|
|
||||||
|
// SSL_CTX_set_client_cert_engine does not itself support multiple
|
||||||
|
// calls by cleaning up before overwriting the client_cert_engine
|
||||||
|
// internal context variable.
|
||||||
|
// Instead of trying to fix up this problem we in turn also do not
|
||||||
|
// support multiple calls to SetClientCertEngine.
|
||||||
|
if (sc->client_cert_engine_provided_) {
|
||||||
|
return env->ThrowError(
|
||||||
|
"Multiple calls to SetClientCertEngine are not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
const node::Utf8Value engine_id(env->isolate(), args[0]);
|
||||||
|
char errmsg[1024];
|
||||||
|
ENGINE* engine = LoadEngineById(*engine_id, &errmsg);
|
||||||
|
|
||||||
|
if (engine == nullptr) {
|
||||||
|
return env->ThrowError(errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = SSL_CTX_set_client_cert_engine(sc->ctx_, engine);
|
||||||
|
// Free reference (SSL_CTX_set_client_cert_engine took it via ENGINE_init).
|
||||||
|
ENGINE_free(engine);
|
||||||
|
if (r == 0) {
|
||||||
|
return ThrowCryptoError(env, ERR_get_error());
|
||||||
|
}
|
||||||
|
sc->client_cert_engine_provided_ = true;
|
||||||
|
}
|
||||||
|
#endif // !OPENSSL_NO_ENGINE
|
||||||
|
|
||||||
|
|
||||||
void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
|
void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
|
||||||
#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
|
#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
|
||||||
|
|
||||||
@ -6093,20 +6172,10 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) {
|
|||||||
|
|
||||||
ClearErrorOnReturn clear_error_on_return;
|
ClearErrorOnReturn clear_error_on_return;
|
||||||
|
|
||||||
|
// Load engine.
|
||||||
const node::Utf8Value engine_id(env->isolate(), args[0]);
|
const node::Utf8Value engine_id(env->isolate(), args[0]);
|
||||||
ENGINE* engine = ENGINE_by_id(*engine_id);
|
char errmsg[1024];
|
||||||
|
ENGINE* engine = LoadEngineById(*engine_id, &errmsg);
|
||||||
// Engine not found, try loading dynamically
|
|
||||||
if (engine == nullptr) {
|
|
||||||
engine = ENGINE_by_id("dynamic");
|
|
||||||
if (engine != nullptr) {
|
|
||||||
if (!ENGINE_ctrl_cmd_string(engine, "SO_PATH", *engine_id, 0) ||
|
|
||||||
!ENGINE_ctrl_cmd_string(engine, "LOAD", nullptr, 0)) {
|
|
||||||
ENGINE_free(engine);
|
|
||||||
engine = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engine == nullptr) {
|
if (engine == nullptr) {
|
||||||
int err = ERR_get_error();
|
int err = ERR_get_error();
|
||||||
|
@ -93,6 +93,9 @@ class SecureContext : public BaseObject {
|
|||||||
SSL_CTX* ctx_;
|
SSL_CTX* ctx_;
|
||||||
X509* cert_;
|
X509* cert_;
|
||||||
X509* issuer_;
|
X509* issuer_;
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
bool client_cert_engine_provided_ = false;
|
||||||
|
#endif // !OPENSSL_NO_ENGINE
|
||||||
|
|
||||||
static const int kMaxSessionSize = 10 * 1024;
|
static const int kMaxSessionSize = 10 * 1024;
|
||||||
|
|
||||||
@ -135,6 +138,10 @@ class SecureContext : public BaseObject {
|
|||||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void LoadPKCS12(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void LoadPKCS12(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
static void SetClientCertEngine(
|
||||||
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
#endif // !OPENSSL_NO_ENGINE
|
||||||
static void GetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void SetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void SetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void SetFreeListLength(
|
static void SetFreeListLength(
|
||||||
|
24
test/addons/openssl-client-cert-engine/binding.gyp
Normal file
24
test/addons/openssl-client-cert-engine/binding.gyp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'testengine',
|
||||||
|
'type': 'none',
|
||||||
|
'conditions': [
|
||||||
|
['OS=="mac" and '
|
||||||
|
'node_use_openssl=="true" and '
|
||||||
|
'node_shared=="false" and '
|
||||||
|
'node_shared_openssl=="false"', {
|
||||||
|
'type': 'shared_library',
|
||||||
|
'sources': [ 'testengine.cc' ],
|
||||||
|
'product_extension': 'engine',
|
||||||
|
'include_dirs': ['../../../deps/openssl/openssl/include'],
|
||||||
|
'link_settings': {
|
||||||
|
'libraries': [
|
||||||
|
'../../../../out/<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
62
test/addons/openssl-client-cert-engine/test.js
Normal file
62
test/addons/openssl-client-cert-engine/test.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../../common');
|
||||||
|
const fixture = require('../../common/fixtures');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const engine = path.join(__dirname,
|
||||||
|
`/build/${common.buildType}/testengine.engine`);
|
||||||
|
|
||||||
|
if (!fs.existsSync(engine))
|
||||||
|
common.skip('no client cert engine');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
|
const agentKey = fs.readFileSync(fixture.path('/keys/agent1-key.pem'));
|
||||||
|
const agentCert = fs.readFileSync(fixture.path('/keys/agent1-cert.pem'));
|
||||||
|
const agentCa = fs.readFileSync(fixture.path('/keys/ca1-cert.pem'));
|
||||||
|
|
||||||
|
const port = common.PORT;
|
||||||
|
|
||||||
|
const serverOptions = {
|
||||||
|
key: agentKey,
|
||||||
|
cert: agentCert,
|
||||||
|
ca: agentCa,
|
||||||
|
requestCert: true,
|
||||||
|
rejectUnauthorized: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const server = https.createServer(serverOptions, (req, res) => {
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('hello world');
|
||||||
|
}).listen(port, common.localhostIPv4, () => {
|
||||||
|
const clientOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
host: common.localhostIPv4,
|
||||||
|
port: port,
|
||||||
|
path: '/test',
|
||||||
|
clientCertEngine: engine, // engine will provide key+cert
|
||||||
|
rejectUnauthorized: false, // prevent failing on self-signed certificates
|
||||||
|
headers: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = https.request(clientOptions, common.mustCall(function(response) {
|
||||||
|
let body = '';
|
||||||
|
response.setEncoding('utf8');
|
||||||
|
response.on('data', function(chunk) {
|
||||||
|
body += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
response.on('end', common.mustCall(function() {
|
||||||
|
assert.strictEqual(body, 'hello world');
|
||||||
|
server.close();
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
req.end();
|
||||||
|
});
|
100
test/addons/openssl-client-cert-engine/testengine.cc
Normal file
100
test/addons/openssl-client-cert-engine/testengine.cc
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <openssl/engine.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifndef ENGINE_CMD_BASE
|
||||||
|
# error did not get engine.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TEST_ENGINE_ID "testengine"
|
||||||
|
#define TEST_ENGINE_NAME "dummy test engine"
|
||||||
|
|
||||||
|
#define AGENT_KEY "test/fixtures/keys/agent1-key.pem"
|
||||||
|
#define AGENT_CERT "test/fixtures/keys/agent1-cert.pem"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
int EngineInit(ENGINE* engine) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EngineFinish(ENGINE* engine) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EngineDestroy(ENGINE* engine) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LoadFile(const char* filename) {
|
||||||
|
std::ifstream file(filename);
|
||||||
|
return std::string(std::istreambuf_iterator<char>(file),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int EngineLoadSSLClientCert(ENGINE* engine,
|
||||||
|
SSL* ssl,
|
||||||
|
STACK_OF(X509_NAME)* ca_dn,
|
||||||
|
X509** ppcert,
|
||||||
|
EVP_PKEY** ppkey,
|
||||||
|
STACK_OF(X509)** pother,
|
||||||
|
UI_METHOD* ui_method,
|
||||||
|
void* callback_data) {
|
||||||
|
if (ppcert != nullptr) {
|
||||||
|
std::string cert = LoadFile(AGENT_CERT);
|
||||||
|
if (cert.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO* bio = BIO_new_mem_buf(cert.data(), cert.size());
|
||||||
|
*ppcert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
|
||||||
|
BIO_vfree(bio);
|
||||||
|
if (*ppcert == nullptr) {
|
||||||
|
printf("Could not read certificate\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ppkey != nullptr) {
|
||||||
|
std::string key = LoadFile(AGENT_KEY);
|
||||||
|
if (key.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO* bio = BIO_new_mem_buf(key.data(), key.size());
|
||||||
|
*ppkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
|
||||||
|
BIO_vfree(bio);
|
||||||
|
if (*ppkey == nullptr) {
|
||||||
|
printf("Could not read private key\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bind_fn(ENGINE* engine, const char* id) {
|
||||||
|
ENGINE_set_id(engine, TEST_ENGINE_ID);
|
||||||
|
ENGINE_set_name(engine, TEST_ENGINE_NAME);
|
||||||
|
ENGINE_set_init_function(engine, EngineInit);
|
||||||
|
ENGINE_set_finish_function(engine, EngineFinish);
|
||||||
|
ENGINE_set_destroy_function(engine, EngineDestroy);
|
||||||
|
ENGINE_set_load_ssl_client_cert_function(engine, EngineLoadSSLClientCert);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
IMPLEMENT_DYNAMIC_CHECK_FN();
|
||||||
|
IMPLEMENT_DYNAMIC_BIND_FN(bind_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
@ -12,7 +12,7 @@ const agent = new https.Agent();
|
|||||||
// empty options
|
// empty options
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
agent.getName({}),
|
agent.getName({}),
|
||||||
'localhost::::::::::'
|
'localhost:::::::::::'
|
||||||
);
|
);
|
||||||
|
|
||||||
// pass all options arguments
|
// pass all options arguments
|
||||||
@ -31,5 +31,5 @@ const options = {
|
|||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
agent.getName(options),
|
agent.getName(options),
|
||||||
'0.0.0.0:443:192.168.1.1:ca:cert:ciphers:key:pfx:false:localhost:'
|
'0.0.0.0:443:192.168.1.1:ca:cert::ciphers:key:pfx:false:localhost:'
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user