* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "openssl_wrapper.hpp"
#include "actor/buslog.hpp"
#include "utils/os_utils.hpp"
#include "openssl/err.h"
#include "openssl/rand.h"
#include "openssl/ssl.h"
#include "openssl/x509v3.h"
#include <cstdio>
#include <pthread.h>
#include <securec.h>
#include <map>
#include <set>
#include <mutex>
#include <sstream>
#include <string>
using litebus::os::ENV_VAR_MAX_LENGTH;
using std::map;
using std::string;
namespace litebus {
SSL_ENVS::~SSL_ENVS()
{
ca = nullptr;
cert = nullptr;
pkey = nullptr;
}
SSL_ENVS::SSL_ENVS()
: sslEnabled(false),
verifyCert(false),
requireCert(false),
isLoadFromFile(true),
dptType(WITHOUT_DECRYPT),
fetchFromEnv(true)
{
}
void SSL_ENVS::Init()
{
sslEnabled = false;
verifyCert = false;
requireCert = false;
isLoadFromFile = true;
caDir.clear();
caFile.clear();
certFile.clear();
keyFile.clear();
dptDir.clear();
dptRootFile.clear();
dptComFile.clear();
dptKFile.clear();
dptType = WITHOUT_DECRYPT;
fetchFromEnv = true;
}
constexpr auto PASSWDLEN = 512;
char g_passwdForDecryptingPrivateKey[PASSWDLEN + 1] = { 0 };
std::map<std::string, char*> g_multiPasswdForDecryptingPrivateKey;
constexpr int WITHOUT_DECRYPT_INDEX = 0;
constexpr int OSS_DECRYPT_INDEX = 1;
constexpr int HARES_DECRYPT_INDEX = 2;
constexpr int DEEP = 4;
constexpr int LITEBUS_ERROR_SIZE_TOO_LONG = -2;
constexpr off_t MAX_FILE_SIZE = 100 * 1024 * 1024;
const std::string DEFAULT_SSL = "litebus";
std::mutex g_sslEnvMutex;
static SSL_ENVS *g_sslEnvs = new (std::nothrow) SSL_ENVS();
std::map<std::string, SSL_ENVS> g_multiSSLEnvs;
static const std::map<int, DecryptType>::value_type G_DECRYPTVALUE[] = {
std::map<int, DecryptType>::value_type(WITHOUT_DECRYPT_INDEX, WITHOUT_DECRYPT),
std::map<int, DecryptType>::value_type(OSS_DECRYPT_INDEX, OSS_DECRYPT),
std::map<int, DecryptType>::value_type(HARES_DECRYPT_INDEX, HARES_DECRYPT)
};
std::map<int, DecryptType> g_toDecryptType(G_DECRYPTVALUE,
G_DECRYPTVALUE + sizeof(G_DECRYPTVALUE) / sizeof(G_DECRYPTVALUE[0]));
bool DecodeBoolString(const string &boolString)
{
return (boolString == "true" || boolString == "1");
}
DecryptType DecodeDecryptTypeString(const string &decryptTypeString)
{
int decryptValue;
try {
decryptValue = std::stoi(decryptTypeString);
} catch (const std::exception &e) {
BUSLOG_ERROR("Decode decrypt type failed, error: {}", e.what());
return UNKNOWN_DECRYPT;
}
auto decryptItem = g_toDecryptType.find(decryptValue);
if (decryptItem == g_toDecryptType.end()) {
return UNKNOWN_DECRYPT;
}
return g_toDecryptType[decryptValue];
}
const char *GetSslEnv(const string &key)
{
const char *env = getenv(key.c_str());
if (env != nullptr && strlen(env) <= ENV_VAR_MAX_LENGTH) {
return env;
}
return nullptr;
}
void FetchSSLConfigFromEnvCA()
{
if (g_sslEnvs == nullptr) {
return;
}
const char *sslEnabledString = GetSslEnv("LITEBUS_SSL_ENABLED");
if (sslEnabledString != nullptr) {
g_sslEnvs->sslEnabled = DecodeBoolString(sslEnabledString);
BUSLOG_INFO("sslEnabled is {}", g_sslEnvs->sslEnabled);
}
const char *sslLoadFromFileString = GetSslEnv("LITEBUS_SSL_LOAD_FROM_FILE");
if (sslLoadFromFileString != nullptr) {
g_sslEnvs->isLoadFromFile = DecodeBoolString(sslLoadFromFileString);
BUSLOG_INFO("sslLoadFromFile is {}", g_sslEnvs->isLoadFromFile);
}
const char *verifyCertString = GetSslEnv("LITEBUS_SSL_VERIFY_CERT");
if (verifyCertString != nullptr) {
g_sslEnvs->verifyCert = DecodeBoolString(verifyCertString);
BUSLOG_INFO("verifyCert is {}", g_sslEnvs->verifyCert);
}
const char *requireCertString = GetSslEnv("LITEBUS_SSL_REQUIRE_CERT");
if (requireCertString != nullptr) {
g_sslEnvs->requireCert = DecodeBoolString(requireCertString);
BUSLOG_INFO("requireCert is {}", g_sslEnvs->requireCert);
}
const char *caDirString = GetSslEnv("LITEBUS_SSL_CA_DIR");
if (caDirString != nullptr) {
g_sslEnvs->caDir = caDirString;
}
const char *caFileString = GetSslEnv("LITEBUS_SSL_CA_FILE");
if (caFileString != nullptr) {
g_sslEnvs->caFile = caFileString;
}
const char *caCertFileString = GetSslEnv("LITEBUS_SSL_CERT_FILE");
if (caCertFileString != nullptr) {
g_sslEnvs->certFile = caCertFileString;
}
const char *keyFileString = GetSslEnv("LITEBUS_SSL_KEY_FILE");
if (keyFileString != nullptr) {
g_sslEnvs->keyFile = keyFileString;
}
}
void FetchSSLConfigFromEnvDecrypt()
{
if (g_sslEnvs == nullptr) {
return;
}
const char *decryptDirString = GetSslEnv("LITEBUS_SSL_DECRYPT_DIR");
if (decryptDirString != nullptr) {
g_sslEnvs->dptDir = decryptDirString;
}
const char *decryptRootFileString = GetSslEnv("LITEBUS_SSL_DECRYPT_ROOT_FILE");
if (decryptRootFileString != nullptr) {
g_sslEnvs->dptRootFile = decryptRootFileString;
}
const char *decryptCommonFileString = GetSslEnv("LITEBUS_SSL_DECRYPT_COMMON_FILE");
if (decryptCommonFileString != nullptr) {
g_sslEnvs->dptComFile = decryptCommonFileString;
}
const char *decryptKeyFileString = GetSslEnv("LITEBUS_SSL_DECRYPT_KEY_FILE");
if (decryptKeyFileString != nullptr) {
g_sslEnvs->dptKFile = decryptKeyFileString;
}
const char *decryptTypeString = GetSslEnv("LITEBUS_SSL_DECRYPT_TYPE");
if (decryptTypeString != nullptr) {
g_sslEnvs->dptType = DecodeDecryptTypeString(decryptTypeString);
BUSLOG_INFO("dptType is {}", g_sslEnvs->dptType);
}
}
void FetchSSLConfigFromEnv()
{
FetchSSLConfigFromEnvCA();
FetchSSLConfigFromEnvDecrypt();
return;
}
void SetSSLEnvsCA(SSL_ENVS *sslEnvs, const std::string &key, const std::string &value)
{
if (sslEnvs == nullptr) {
return;
}
if (key == "LITEBUS_SSL_ENABLED") {
sslEnvs->sslEnabled = DecodeBoolString(value);
BUSLOG_INFO("sslEnabled is {}", sslEnvs->sslEnabled);
} else if (key == "LITEBUS_SSL_VERIFY_CERT") {
sslEnvs->verifyCert = DecodeBoolString(value);
BUSLOG_INFO("verifyCert is {}", sslEnvs->verifyCert);
} else if (key == "LITEBUS_SSL_REQUIRE_CERT") {
sslEnvs->requireCert = DecodeBoolString(value);
BUSLOG_INFO("requireCert is {}", sslEnvs->requireCert);
} else if (key == "LITEBUS_SSL_CA_DIR") {
sslEnvs->caDir = value;
} else if (key == "LITEBUS_SSL_CA_FILE") {
sslEnvs->caFile = value;
} else if (key == "LITEBUS_SSL_CERT_FILE") {
sslEnvs->certFile = value;
} else if (key == "LITEBUS_SSL_KEY_FILE") {
sslEnvs->keyFile = value;
} else if (key == "LITEBUS_SSL_LOAD_FROM_FILE") {
sslEnvs->isLoadFromFile = DecodeBoolString(value);
}
}
void SetSSLEnvsCA(const std::string &key, const std::string &value)
{
SetSSLEnvsCA(g_sslEnvs, key, value);
}
void SetSSLEnvsDecrypt(SSL_ENVS *sslEnvs, const std::string &key, const std::string &value)
{
if (sslEnvs == nullptr) {
return;
}
if (key == "LITEBUS_SSL_DECRYPT_DIR") {
sslEnvs->dptDir = value;
} else if (key == "LITEBUS_SSL_DECRYPT_ROOT_FILE") {
sslEnvs->dptRootFile = value;
} else if (key == "LITEBUS_SSL_DECRYPT_COMMON_FILE") {
sslEnvs->dptComFile = value;
} else if (key == "LITEBUS_SSL_DECRYPT_KEY_FILE") {
sslEnvs->dptKFile = value;
} else if (key == "LITEBUS_SSL_DECRYPT_TYPE") {
sslEnvs->dptType = DecodeDecryptTypeString(value);
BUSLOG_INFO("dptType is {}", sslEnvs->dptType);
} else if (key == "LITEBUS_SSL_FETCH_FROM_ENV") {
sslEnvs->fetchFromEnv = DecodeBoolString(value);
BUSLOG_INFO("fetchFromEnv is {}", sslEnvs->fetchFromEnv);
}
}
void SetSSLEnvsDecrypt(const std::string &key, const std::string &value)
{
SetSSLEnvsDecrypt(g_sslEnvs, key, value);
}
void SetSSLEnvs(const std::string &key, const std::string &value)
{
std::lock_guard<std::mutex> lock(g_sslEnvMutex);
SetSSLEnvsCA(key, value);
SetSSLEnvsDecrypt(key, value);
return;
}
void SetSSLPemKeyEnvs(SSL_ENVS *sslEnvs, EVP_PKEY *pkey)
{
std::lock_guard<std::mutex> lock(g_sslEnvMutex);
if (sslEnvs == nullptr) {
return;
}
sslEnvs->pkey = pkey;
}
void SetSSLPemCertEnvs(SSL_ENVS *sslEnvs, X509 *cert)
{
std::lock_guard<std::mutex> lock(g_sslEnvMutex);
if (sslEnvs == nullptr) {
return;
}
sslEnvs->cert = cert;
}
void SetSSLPemCAEnvs(SSL_ENVS *sslEnvs, STACK_OF(X509) *ca)
{
std::lock_guard<std::mutex> lock(g_sslEnvMutex);
if (sslEnvs == nullptr) {
return;
}
sslEnvs->ca = ca;
}
void ClearSslPemCerts(SSL_ENVS *sslEnvs)
{
if (sslEnvs->pkey) {
EVP_PKEY_free(sslEnvs->pkey);
}
if (sslEnvs->cert) {
X509_free(sslEnvs->cert);
}
if (sslEnvs->ca) {
sk_X509_pop_free(sslEnvs->ca, X509_free);
}
}
void FetchSSLConfigFromMap(std::map<std::string, std::string> &environment)
{
for (const auto &envItem : environment) {
SetSSLEnvs(envItem.first, envItem.second);
}
return;
}
* The first parameter:passwd contains at most 255 characters(255 not contain terminator '\0').
* Parameter passwd_len is [0, 512]
*/
void SetPasswd(const char *passwdKey, size_t passwdLen, char *dest)
{
if ((passwdKey == nullptr) || (passwdLen > PASSWDLEN) || (dest == nullptr)) {
BUSLOG_ERROR("invalid parameters");
return;
}
errno_t rc = strncpy_s(dest, PASSWDLEN + 1, passwdKey, passwdLen);
if (rc == EOK) {
return;
}
BUSLOG_ERROR("strncpy_s failed, errno = {}", rc);
}
void SetPasswdForDecryptingPrivateKey(const char *passwdKey, size_t passwdLen)
{
SetPasswd(passwdKey, passwdLen, g_passwdForDecryptingPrivateKey);
}
FILE *OpenDecryptKeyFile(const string &filePath)
{
char pathCheck[PATH_MAX] = { 0 };
if (realpath(filePath.c_str(), pathCheck) == nullptr) {
BUSLOG_ERROR("realpath checking is error");
return nullptr;
}
struct stat buf = { 0 };
int rtn = stat(pathCheck, &buf);
if (rtn != 0) {
BUSLOG_ERROR("file stat failed");
return nullptr;
}
if (buf.st_size > MAX_FILE_SIZE) {
BUSLOG_ERROR("file size is too big");
return nullptr;
}
return fopen(pathCheck, "r");
}
int GetMultiSslEnv(const std::string &sslKey, SSL_ENVS &sslEnv)
{
std::lock_guard<std::mutex> lock(g_sslEnvMutex);
if (sslKey == DEFAULT_SSL) {
if (g_sslEnvs == nullptr) {
BUSLOG_ERROR("SSL envs hasn't been initialized yet.");
return -1;
}
sslEnv = *g_sslEnvs;
return 0;
}
if (g_multiSSLEnvs.find(sslKey) == g_multiSSLEnvs.end()) {
BUSLOG_ERROR("SSL {} envs hasn't been initialized yet.", sslKey.c_str());
return -1;
}
sslEnv = g_multiSSLEnvs[sslKey];
return 0;
}
int GetPasswd(const std::string &sslKey, char *passwdKey, size_t passwdLen)
{
BUSLOG_INFO("get ssl parameters");
if ((passwdKey == nullptr) || (passwdLen <= PASSWDLEN)) {
BUSLOG_ERROR("invalid parameters {}", sslKey);
return -1;
}
SSL_ENVS sslEnvs;
auto ret = GetMultiSslEnv(sslKey, sslEnvs);
if (ret != 0) {
return ret;
}
switch (sslEnvs.dptType) {
case WITHOUT_DECRYPT:
case UNKNOWN_DECRYPT: {
auto pwd = (sslKey == DEFAULT_SSL) ? g_passwdForDecryptingPrivateKey
: g_multiPasswdForDecryptingPrivateKey[sslKey];
if (pwd == nullptr) {
BUSLOG_WARN("no passwd for private key for ssl {}", sslKey);
return 0;
}
errno_t rc = strncpy_s(passwdKey, PASSWDLEN + 1, pwd, PASSWDLEN);
if (rc != EOK) {
BUSLOG_ERROR("strncpy error, rtn ={}", rc);
(void)memset_s(passwdKey, PASSWDLEN + 1, 0, PASSWDLEN + 1);
return -1;
}
return 0;
}
default:
return -1;
}
}
int GetPasswdForDecryptingPrivateKey(char *passwdKey, size_t passwdLen)
{
return GetPasswd(DEFAULT_SSL, passwdKey, passwdLen);
}
int ClearPasswd(char *passwdForDecryptingPrivateKey)
{
errno_t rc = memset_s(passwdForDecryptingPrivateKey, PASSWDLEN + 1, 0, PASSWDLEN + 1);
if (rc == EOK) {
return rc;
}
BUSLOG_ERROR("memset_s failed, errno ={}", rc);
return rc;
}
int ClearPasswdForDecryptingPrivateKey()
{
return ClearPasswd(g_passwdForDecryptingPrivateKey);
}
void ClearMultiPasswdForDecryptingPrivateKey()
{
for (auto iter : g_multiPasswdForDecryptingPrivateKey) {
(void)ClearPasswd(iter.second);
delete[] iter.second;
}
g_multiPasswdForDecryptingPrivateKey.clear();
}
}
extern "C" {
void LitebusSetPasswdForDecryptingPrivateKeyC(const char *passwdKey, size_t passwdLen)
{
litebus::SetPasswd(passwdKey, passwdLen, litebus::g_passwdForDecryptingPrivateKey);
return;
}
void LitebusSetMultiPasswdForDecryptingPrivateKeyC(char *sslKey, const char *passwdKey, size_t passwdLen)
{
std::string sslKeyS = sslKey;
if (litebus::g_multiPasswdForDecryptingPrivateKey.find(sslKeyS) !=
litebus::g_multiPasswdForDecryptingPrivateKey.end()) {
return;
}
char *passwdForDecryptingPrivateKey = new (std::nothrow) char[litebus::PASSWDLEN + 1];
BUS_OOM_EXIT(passwdForDecryptingPrivateKey);
litebus::g_multiPasswdForDecryptingPrivateKey[sslKeyS] = passwdForDecryptingPrivateKey;
litebus::SetPasswd(passwdKey, passwdLen, passwdForDecryptingPrivateKey);
return;
}
int LitebusSetSSLEnvsC(const char *key, const char *value)
{
if ((key == nullptr) || (value == nullptr)) {
return -1;
}
if (strlen(value) > ENV_VAR_MAX_LENGTH) {
return litebus::LITEBUS_ERROR_SIZE_TOO_LONG;
}
litebus::SetSSLEnvs(string(key), string(value));
return 0;
}
int LitebusSetSSLPemKeyEnvsC(EVP_PKEY *pkey)
{
if (pkey == nullptr) {
return -1;
}
litebus::SetSSLPemKeyEnvs(litebus::g_sslEnvs, pkey);
return 0;
}
int LitebusSetSSLPemCertEnvsC(X509 *cert)
{
if (cert == nullptr) {
return -1;
}
litebus::SetSSLPemCertEnvs(litebus::g_sslEnvs, cert);
return 0;
}
int LitebusSetSSLPemCAEnvsC(STACK_OF(X509) *ca)
{
if (ca == nullptr) {
return -1;
}
litebus::SetSSLPemCAEnvs(litebus::g_sslEnvs, ca);
return 0;
}
int LitebusSetMultiSSLEnvsC(const char *sslKey, const char *key, const char *value)
{
if ((key == nullptr) || (value == nullptr) || (sslKey == nullptr)) {
return -1;
}
if (strlen(value) > ENV_VAR_MAX_LENGTH) {
return litebus::LITEBUS_ERROR_SIZE_TOO_LONG;
}
std::lock_guard<std::mutex> lock(litebus::g_sslEnvMutex);
if (litebus::g_multiSSLEnvs.find(string(sslKey)) == litebus::g_multiSSLEnvs.end()) {
litebus::g_multiSSLEnvs[string(sslKey)] = litebus::SSL_ENVS();
}
litebus::SetSSLEnvsCA(&litebus::g_multiSSLEnvs[string(sslKey)], string(key), string(value));
litebus::SetSSLEnvsDecrypt(&litebus::g_multiSSLEnvs[string(sslKey)], string(key), string(value));
return 0;
}
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
struct CRYPTO_dynlock_value {
std::mutex mutex;
};
#endif
namespace litebus {
namespace openssl {
static SSL_CTX *g_sslServerCtx = nullptr;
static SSL_CTX *g_sslClientCtx = nullptr;
std::map<std::string, SSL_CTX*> g_sslCtx;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static std::mutex *g_mutexes = nullptr;
#endif
int VerifyCallback(int ret, X509_STORE_CTX *store)
{
if (ret != 1) {
std::ostringstream message;
int error = X509_STORE_CTX_get_error(store);
const char *errorPtr = X509_verify_cert_error_string(error);
if (errorPtr != nullptr) {
message << "Error code is :" << std::to_string(error) << ", with message :" << errorPtr;
BUSLOG_ERROR("verify err msg is {}", message.str());
} else {
BUSLOG_ERROR("verify err msg is can not get the error message");
}
}
return ret;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
void LockFun(int mode, int n, const char *file, int line)
{
auto cryptoMode = static_cast<unsigned int>(mode);
if (cryptoMode & CRYPTO_LOCK) {
g_mutexes[n].lock();
} else {
g_mutexes[n].unlock();
}
}
unsigned long IdFun()
{
return ((unsigned long)pthread_self());
}
CRYPTO_dynlock_value *DynCreateFun(const char *file, int line)
{
auto *value = new (std::nothrow) CRYPTO_dynlock_value();
if (value == nullptr) {
return nullptr;
}
return value;
}
void DynLockFun(int mode, CRYPTO_dynlock_value *value, const char *file, int line)
{
auto cryptoMode = static_cast<unsigned int>(mode);
if (cryptoMode & CRYPTO_LOCK) {
value->mutex.lock();
} else {
value->mutex.unlock();
}
}
void DynKillLockFun(CRYPTO_dynlock_value *value, const char *file, int line)
{
delete value;
}
#endif
* Clean SSL context. It is visible only to Tests with explicitly declaring this function as
* we don't want applications changing the global ssl context on running time.
*/
void SslClean()
{
if (g_sslClientCtx != nullptr) {
SSL_CTX_free(g_sslClientCtx);
g_sslClientCtx = nullptr;
}
if (g_sslServerCtx != nullptr) {
SSL_CTX_free(g_sslServerCtx);
g_sslServerCtx = nullptr;
}
for (auto iter : g_sslCtx) {
if (iter.second == nullptr) {
continue;
}
SSL_CTX_free(iter.second);
iter.second = nullptr;
}
g_sslCtx.clear();
}
int SslDecryptPrivateKey()
{
char out[PASSWDLEN + 1] = { 0 };
if (GetPasswdForDecryptingPrivateKey(out, PASSWDLEN + 1) < 0) {
return -1;
}
SetPasswdForDecryptingPrivateKey(out, PASSWDLEN);
(void)memset_s(out, PASSWDLEN + 1, 0, PASSWDLEN + 1);
return 0;
}
int MultiSslDecryptPrivateKey(const std::string &key)
{
if (key != DEFAULT_SSL &&
g_multiPasswdForDecryptingPrivateKey.find(key) == g_multiPasswdForDecryptingPrivateKey.end()) {
char *passwdForDecryptingPrivateKey = new (std::nothrow) char[PASSWDLEN + 1]{ 0 };
BUS_OOM_EXIT(passwdForDecryptingPrivateKey);
g_multiPasswdForDecryptingPrivateKey[key] = passwdForDecryptingPrivateKey;
}
char out[PASSWDLEN + 1] = { 0 };
if (GetPasswd(key, out, PASSWDLEN + 1) < 0) {
return -1;
}
auto passwdForDecryptingPrivateKey = g_multiPasswdForDecryptingPrivateKey[key];
SetPasswd(out, PASSWDLEN, passwdForDecryptingPrivateKey);
(void)memset_s(out, PASSWDLEN + 1, 0, PASSWDLEN + 1);
return 0;
}
void SslMutualAuth(SSL_ENVS *sslEnvs, SSL_CTX *sslCtx)
{
if (sslEnvs->verifyCert) {
unsigned int mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
SSL_CTX_set_verify(sslCtx, mode, &VerifyCallback);
SSL_CTX_set_verify_depth(sslCtx, DEEP);
return;
}
SSL_CTX_set_verify(sslCtx, SSL_VERIFY_NONE, nullptr);
}
bool SslVerifyKey(SSL_ENVS *sslEnvs, SSL_CTX *sslCtx)
{
SslMutualAuth(sslEnvs, sslCtx);
const X509* cert = SSL_CTX_get0_certificate(sslCtx);
if (!cert) {
BUSLOG_ERROR("Failed to get certificate");
return false;
}
if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0) {
BUSLOG_ERROR("The certificate has not yet taken effect");
return false;
}
if (X509_cmp_current_time(X509_get_notAfter(cert)) < 0) {
BUSLOG_ERROR("The certificate has expired");
return false;
}
if (SSL_CTX_check_private_key(sslCtx) != 1) {
BUSLOG_ERROR("Private key doesn't match the certificate");
return false;
}
const char *modernCiphers =
"ECDHE-ECDSA-AES128-GCM-SHA256:"
"ECDHE-RSA-AES128-GCM-SHA256:"
"ECDHE-ECDSA-CHACHA20-POLY1305:"
"ECDHE-RSA-CHACHA20-POLY1305:"
"DHE-RSA-AES128-GCM-SHA256:"
"!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!3DES";
if (SSL_CTX_set_cipher_list(sslCtx, modernCiphers) == 0) {
BUSLOG_ERROR("set cipher fail");
return false;
}
if (SSL_CTX_set_ciphersuites(sslCtx, "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256") == 0) {
BUSLOG_ERROR("set cipher fail");
return false;
}
return true;
}
bool SetVerifyContextFromPem(SSL_ENVS *sslEnvs, SSL_CTX *sslCtx)
{
if (sslEnvs->cert == nullptr || sslEnvs->pkey == nullptr || sslEnvs->ca == nullptr) {
BUSLOG_ERROR("CA, cert or key from pem is empty");
return false;
}
if (SSL_CTX_use_certificate(sslCtx, sslEnvs->cert) != 1) {
BUSLOG_ERROR("Couldn't load cert from pem");
return false;
}
if (SSL_CTX_use_PrivateKey(sslCtx, sslEnvs->pkey) != 1) {
BUSLOG_ERROR("Couldn't load key from pem");
return false;
}
X509_STORE *caStore = SSL_CTX_get_cert_store(sslCtx);
if (caStore == nullptr) {
BUSLOG_ERROR("Couldn't load CA from pem");
return false;
}
for (int i = 0; i < sk_X509_num(sslEnvs->ca); i++) {
X509 *caCert = sk_X509_value(sslEnvs->ca, i);
if (!X509_STORE_add_cert(caStore, caCert)) {
BUSLOG_ERROR("Couldn't load CA Cert from pem");
return false;
}
}
return SslVerifyKey(sslEnvs, sslCtx);
}
bool SetVerifyContextFromFile(SSL_ENVS *sslEnvs, SSL_CTX *sslCtx)
{
if (sslEnvs->requireCert && !sslEnvs->verifyCert) {
sslEnvs->verifyCert = true;
}
if (sslEnvs->verifyCert) {
if (sslEnvs->caFile.empty() || sslEnvs->caDir.empty()) {
BUSLOG_ERROR("Couldn't load CA file and/or directory");
return false;
}
if (SSL_CTX_load_verify_locations(sslCtx, sslEnvs->caFile.c_str(), sslEnvs->caDir.c_str()) != 1) {
BUSLOG_ERROR("Couldn't load CA file and/or directory");
return false;
}
}
if (SSL_CTX_use_certificate_chain_file(sslCtx, sslEnvs->certFile.c_str()) != 1) {
BUSLOG_ERROR("Couldn't load cert file");
return false;
}
if (SSL_CTX_use_PrivateKey_file(sslCtx, sslEnvs->keyFile.c_str(), SSL_FILETYPE_PEM) != 1) {
BUSLOG_ERROR("Couldn't load key file");
return false;
}
return SslVerifyKey(sslEnvs, sslCtx);
}
bool SslVerify(SSL_ENVS *sslEnvs, SSL_CTX *sslCtx, char *passwdForDecryptingPrivateKey)
{
unsigned long sslOptions = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
#ifdef SSL_OP_NO_RENEGOTIATION
sslOptions |= SSL_OP_NO_RENEGOTIATION;
#endif
#endif
(void)SSL_CTX_set_options(sslCtx, sslOptions);
if (SSL_CTX_set_min_proto_version(sslCtx, TLS1_2_VERSION) != 1) {
BUSLOG_ERROR("Set TLS 1.2 version failed.");
return false;
}
SSL_CTX_set_default_passwd_cb_userdata(sslCtx, passwdForDecryptingPrivateKey);
int groupList[] = {NID_X25519};
if (SSL_CTX_set1_groups(sslCtx, groupList, static_cast<int>(sizeof(groupList) / sizeof(int))) != 1) {
BUSLOG_ERROR("Create curve (x25519) fail");
return false;
}
BUSLOG_INFO("Create curve (x25519) success");
if (!sslEnvs->isLoadFromFile) {
return SetVerifyContextFromPem(sslEnvs, sslCtx);
}
return SetVerifyContextFromFile(sslEnvs, sslCtx);
}
bool SslMultiInit(const std::map<std::string, SSL_ENVS> &multiSSLEnv)
{
for (auto iter : multiSSLEnv) {
auto key = iter.first;
auto sslEnv = iter.second;
if (!sslEnv.sslEnabled) {
continue;
}
g_sslCtx[key] = SSL_CTX_new(TLS_client_method());
if (g_sslCtx[key] == nullptr) {
BUSLOG_ERROR("Couldn't create SSL context {}", key);
return false;
}
if (MultiSslDecryptPrivateKey(key) < 0) {
return false;
}
(void)SSL_CTX_set_mode(g_sslCtx[key], SSL_MODE_RELEASE_BUFFERS);
(void)SslVerify(&sslEnv, g_sslCtx[key], g_multiPasswdForDecryptingPrivateKey[key]);
continue;
}
return true;
}
bool IsSslEnable()
{
auto litebusInternalEnable = (g_sslEnvs != nullptr) && (g_sslEnvs->sslEnabled);
for (auto sslEnv : g_multiSSLEnvs) {
litebusInternalEnable = litebusInternalEnable || sslEnv.second.sslEnabled;
}
return litebusInternalEnable;
}
bool SslParamCheck(const SSL_ENVS &sslEnvs)
{
if (!sslEnvs.sslEnabled) {
return true;
}
if (!sslEnvs.isLoadFromFile) {
if (sslEnvs.cert == nullptr || sslEnvs.pkey == nullptr || sslEnvs.ca == nullptr) {
BUSLOG_ERROR("SSL is load from pem! Plese set path with P12 certificate");
return false;
}
return true;
}
if (sslEnvs.keyFile.empty()) {
BUSLOG_ERROR("SSL requires key! Plese set path with LITEBUS_SSL_KEY_FILE");
return false;
}
if (sslEnvs.certFile.empty()) {
BUSLOG_ERROR("SSL requires certificate! Plese set path with LITEBUS_SSL_CERT_FILE");
return false;
}
if (sslEnvs.dptType == UNKNOWN_DECRYPT) {
BUSLOG_ERROR("SSL requires decrypt type! Plese set path with LITEBUS_SSL_DECRYPT_TYPE");
return false;
}
return true;
}
* Re-configure SSL context. It is visible only to Tests with explicitly declaring this function as
* we don't want applications changing the global ssl context on running time.
*/
bool SslInitInternal()
{
litebus::g_sslEnvMutex.lock();
if (g_sslEnvs == nullptr) {
BUSLOG_ERROR("SSL envs hasn't been initialized yet.");
litebus::g_sslEnvMutex.unlock();
return false;
}
SSL_ENVS sslEnvs = *g_sslEnvs;
std::map<std::string, SSL_ENVS> multiSSLEnv = g_multiSSLEnvs;
litebus::g_sslEnvMutex.unlock();
SslClean();
(void)SSL_library_init();
(void)SSL_load_error_strings();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
if (CRYPTO_num_locks() <= 0) {
return false;
} else {
g_mutexes = new (std::nothrow) std::mutex[CRYPTO_num_locks()];
if (g_mutexes == nullptr) {
BUSLOG_ERROR("Couldn't allocate memory for mutex.");
return false;
}
}
CRYPTO_set_id_callback(&IdFun);
CRYPTO_set_locking_callback(&LockFun);
CRYPTO_set_dynlock_create_callback(&DynCreateFun);
CRYPTO_set_dynlock_lock_callback(&DynLockFun);
CRYPTO_set_dynlock_destroy_callback(&DynKillLockFun);
#endif
if (!sslEnvs.sslEnabled) {
return SslMultiInit(multiSSLEnv);
}
g_sslServerCtx = SSL_CTX_new(TLS_server_method());
g_sslClientCtx = SSL_CTX_new(TLS_client_method());
if (g_sslServerCtx == nullptr || g_sslClientCtx == nullptr) {
BUSLOG_ERROR("Couldn't create SSL context");
return false;
}
if (SslDecryptPrivateKey() < 0) {
return false;
}
(void)SSL_CTX_set_mode(g_sslServerCtx, SSL_MODE_RELEASE_BUFFERS);
(void)SSL_CTX_set_mode(g_sslClientCtx, SSL_MODE_RELEASE_BUFFERS);
if (!SslVerify(&sslEnvs, g_sslServerCtx, g_passwdForDecryptingPrivateKey) ||
!SslVerify(&sslEnvs, g_sslClientCtx, g_passwdForDecryptingPrivateKey)) {
return false;
}
return SslMultiInit(multiSSLEnv);
}
void SslFinalize()
{
std::lock_guard<std::mutex> lock(litebus::g_sslEnvMutex);
if (g_sslEnvs != nullptr) {
g_sslEnvs->Init();
}
for (auto iter : g_multiSSLEnvs) {
iter.second.Init();
}
g_multiSSLEnvs.clear();
}
* it should be initialized only once in litebus::initialize as it is not threadsafe
*/
bool SslInit()
{
if (g_sslServerCtx != nullptr || g_sslClientCtx != nullptr) {
BUSLOG_WARN("ssl Ctx is already initialized");
return true;
}
{
std::lock_guard<std::mutex> lock(litebus::g_sslEnvMutex);
if (g_sslEnvs == nullptr) {
BUSLOG_ERROR("g_sslEnvs initialized failed");
return false;
}
if (g_sslEnvs->fetchFromEnv) {
FetchSSLConfigFromEnv();
}
if (!IsSslEnable()) {
return true;
}
if (!SslParamCheck(*g_sslEnvs)) {
return false;
}
}
bool initialized = SslInitInternal();
(void)ClearPasswdForDecryptingPrivateKey();
ClearMultiPasswdForDecryptingPrivateKey();
if (!g_sslEnvs->isLoadFromFile) {
ClearSslPemCerts(g_sslEnvs);
}
if (!initialized) {
BUSLOG_ERROR("SSL initialize failed");
SslClean();
return false;
}
BUSLOG_INFO("SSL initialized successfully");
return true;
}
SSL_CTX *SslCtx(const bool &client, const std::string &sslKey)
{
if (!sslKey.empty() && sslKey != DEFAULT_SSL) {
return g_sslCtx[sslKey];
}
if (client) {
return g_sslClientCtx;
} else {
return g_sslServerCtx;
}
}
bool IsSslEnabled()
{
std::lock_guard<std::mutex> lock(litebus::g_sslEnvMutex);
if (g_sslEnvs == nullptr) {
return false;
}
bool isEnabled = g_sslEnvs->sslEnabled;
return isEnabled;
}
}
}