/* * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. * * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). * * Use of this source code is governed by MIT license that can be found in the * LICENSE file in the root of the source tree. All contributing project authors * may be found in the AUTHORS file in the root of the source tree. */ #include "SSLUtil.h" #include "onceToken.h" #include "logger.h" #if defined(ENABLE_OPENSSL) #include #include #include #include #include #include #include #include #endif //defined(ENABLE_OPENSSL) using namespace std; namespace toolkit { std::string SSLUtil::getLastError() { #if defined(ENABLE_OPENSSL) unsigned long errCode = ERR_get_error(); if (errCode != 0) { char buffer[256]; ERR_error_string_n(errCode, buffer, sizeof(buffer)); return buffer; } else #endif //defined(ENABLE_OPENSSL) { return "No error"; } } #if defined(ENABLE_OPENSSL) static int getCerType(BIO *bio, const char *passwd, X509 **x509, int type) { //尝试pem格式 if (type == 1 || type == 0) { if (type == 0) { BIO_reset(bio); } // 尝试PEM格式 *x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); if (*x509) { return 1; } } if (type == 2 || type == 0) { if (type == 0) { BIO_reset(bio); } //尝试DER格式 *x509 = d2i_X509_bio(bio, nullptr); if (*x509) { return 2; } } if (type == 3 || type == 0) { if (type == 0) { BIO_reset(bio); } //尝试p12格式 PKCS12 *p12 = d2i_PKCS12_bio(bio, nullptr); if (p12) { EVP_PKEY *pkey = nullptr; PKCS12_parse(p12, passwd, &pkey, x509, nullptr); PKCS12_free(p12); if (pkey) { EVP_PKEY_free(pkey); } if (*x509) { return 3; } } } return 0; } #endif //defined(ENABLE_OPENSSL) vector > SSLUtil::loadPublicKey(const string &file_path_or_data, const string &passwd, bool isFile) { vector > ret; #if defined(ENABLE_OPENSSL) BIO *bio = isFile ? BIO_new_file((char *) file_path_or_data.data(), "r") : BIO_new_mem_buf((char *) file_path_or_data.data(), file_path_or_data.size()); if (!bio) { WarnL << (isFile ? "BIO_new_file" : "BIO_new_mem_buf") << " failed: " << getLastError(); return ret; } onceToken token0(nullptr, [&]() { BIO_free(bio); }); int cer_type = 0; X509 *x509 = nullptr; do { cer_type = getCerType(bio, passwd.data(), &x509, cer_type); if (cer_type) { ret.push_back(shared_ptr(x509, [](X509 *ptr) { X509_free(ptr); })); } } while (cer_type != 0); return ret; #else return ret; #endif //defined(ENABLE_OPENSSL) } shared_ptr SSLUtil::loadPrivateKey(const string &file_path_or_data, const string &passwd, bool isFile) { #if defined(ENABLE_OPENSSL) BIO *bio = isFile ? BIO_new_file((char *) file_path_or_data.data(), "r") : BIO_new_mem_buf((char *) file_path_or_data.data(), file_path_or_data.size()); if (!bio) { WarnL << (isFile ? "BIO_new_file" : "BIO_new_mem_buf") << " failed: " << getLastError(); return nullptr; } pem_password_cb *cb = [](char *buf, int size, int rwflag, void *userdata) -> int { const string *passwd = (const string *) userdata; size = size < (int) passwd->size() ? size : (int) passwd->size(); memcpy(buf, passwd->data(), size); return size; }; onceToken token0(nullptr, [&]() { BIO_free(bio); }); //尝试pem格式 EVP_PKEY *evp_key = PEM_read_bio_PrivateKey(bio, nullptr, cb, (void *) &passwd); if (!evp_key) { //尝试p12格式 BIO_reset(bio); PKCS12 *p12 = d2i_PKCS12_bio(bio, nullptr); if (!p12) { return nullptr; } X509 *x509 = nullptr; PKCS12_parse(p12, passwd.data(), &evp_key, &x509, nullptr); PKCS12_free(p12); if (x509) { X509_free(x509); } if (!evp_key) { return nullptr; } } return shared_ptr(evp_key, [](EVP_PKEY *ptr) { EVP_PKEY_free(ptr); }); #else return nullptr; #endif //defined(ENABLE_OPENSSL) } shared_ptr SSLUtil::makeSSLContext(const vector > &cers, const shared_ptr &key, bool serverMode, bool checkKey) { #if defined(ENABLE_OPENSSL) SSL_CTX *ctx = SSL_CTX_new(serverMode ? SSLv23_server_method() : SSLv23_client_method()); if (!ctx) { WarnL << "SSL_CTX_new " << (serverMode ? "SSLv23_server_method" : "SSLv23_client_method") << " failed: " << getLastError(); return nullptr; } int i = 0; for (auto &cer : cers) { //加载公钥 if (i++ == 0) { //SSL_CTX_use_certificate内部会调用X509_up_ref,所以这里不用X509_dup SSL_CTX_use_certificate(ctx, cer.get()); } else { //需要先拷贝X509对象,否则指针会失效 SSL_CTX_add_extra_chain_cert(ctx, X509_dup(cer.get())); } } if (key) { //提供了私钥 if (SSL_CTX_use_PrivateKey(ctx, key.get()) != 1) { WarnL << "SSL_CTX_use_PrivateKey failed: " << getLastError(); SSL_CTX_free(ctx); return nullptr; } } if (key || checkKey) { //加载私钥成功 if (SSL_CTX_check_private_key(ctx) != 1) { WarnL << "SSL_CTX_check_private_key failed: " << getLastError(); SSL_CTX_free(ctx); return nullptr; } } //公钥私钥匹配或者没有公私钥 return shared_ptr(ctx, [](SSL_CTX *ptr) { SSL_CTX_free(ptr); }); #else return nullptr; #endif //defined(ENABLE_OPENSSL) } shared_ptr SSLUtil::makeSSL(SSL_CTX *ctx) { #if defined(ENABLE_OPENSSL) auto *ssl = SSL_new(ctx); if (!ssl) { return nullptr; } return shared_ptr(ssl, [](SSL *ptr) { SSL_free(ptr); }); #else return nullptr; #endif //defined(ENABLE_OPENSSL) } bool SSLUtil::loadDefaultCAs(SSL_CTX *ctx) { #if defined(ENABLE_OPENSSL) if (!ctx) { return false; } if (SSL_CTX_set_default_verify_paths(ctx) != 1) { WarnL << "SSL_CTX_set_default_verify_paths failed: " << getLastError(); return false; } return true; #else return false; #endif //defined(ENABLE_OPENSSL) } bool SSLUtil::trustCertificate(SSL_CTX *ctx, X509 *cer) { #if defined(ENABLE_OPENSSL) X509_STORE *store = SSL_CTX_get_cert_store(ctx); if (store && cer) { if (X509_STORE_add_cert(store, cer) != 1) { WarnL << "X509_STORE_add_cert failed: " << getLastError(); return false; } return true; } #endif //defined(ENABLE_OPENSSL) return false; } bool SSLUtil::verifyX509(X509 *cer, ...) { #if defined(ENABLE_OPENSSL) va_list args; va_start(args, cer); X509_STORE *store = X509_STORE_new(); do { X509 *ca; if ((ca = va_arg(args, X509*)) == nullptr) { break; } X509_STORE_add_cert(store, ca); } while (true); va_end(args); X509_STORE_CTX *store_ctx = X509_STORE_CTX_new(); X509_STORE_CTX_init(store_ctx, store, cer, nullptr); auto ret = X509_verify_cert(store_ctx); if (ret != 1) { int depth = X509_STORE_CTX_get_error_depth(store_ctx); int err = X509_STORE_CTX_get_error(store_ctx); WarnL << "X509_verify_cert failed, depth: " << depth << ", err: " << X509_verify_cert_error_string(err); } X509_STORE_CTX_free(store_ctx); X509_STORE_free(store); return ret == 1; #else WarnL << "ENABLE_OPENSSL disabled, you can not use any features based on openssl"; return false; #endif //defined(ENABLE_OPENSSL) } #ifdef ENABLE_OPENSSL #ifndef X509_F_X509_PUBKEY_GET0 EVP_PKEY *X509_get0_pubkey(X509 *x){ EVP_PKEY *ret = X509_get_pubkey(x); if(ret){ EVP_PKEY_free(ret); } return ret; } #endif //X509_F_X509_PUBKEY_GET0 #ifndef EVP_F_EVP_PKEY_GET0_RSA RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey){ RSA *ret = EVP_PKEY_get1_RSA(pkey); if(ret){ RSA_free(ret); } return ret; } #endif //EVP_F_EVP_PKEY_GET0_RSA #endif //ENABLE_OPENSSL string SSLUtil::cryptWithRsaPublicKey(X509 *cer, const string &in_str, bool enc_or_dec) { #if defined(ENABLE_OPENSSL) EVP_PKEY *public_key = X509_get0_pubkey(cer); if (!public_key) { return ""; } auto rsa = EVP_PKEY_get1_RSA(public_key); if (!rsa) { return ""; } string out_str(RSA_size(rsa), '\0'); int ret = 0; if (enc_or_dec) { ret = RSA_public_encrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, RSA_PKCS1_PADDING); } else { ret = RSA_public_decrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, RSA_PKCS1_PADDING); } if (ret > 0) { out_str.resize(ret); return out_str; } WarnL << (enc_or_dec ? "RSA_public_encrypt" : "RSA_public_decrypt") << " failed: " << getLastError(); return ""; #else WarnL << "ENABLE_OPENSSL disabled, you can not use any features based on openssl"; return ""; #endif //defined(ENABLE_OPENSSL) } string SSLUtil::cryptWithRsaPrivateKey(EVP_PKEY *private_key, const string &in_str, bool enc_or_dec) { #if defined(ENABLE_OPENSSL) auto rsa = EVP_PKEY_get1_RSA(private_key); if (!rsa) { return ""; } string out_str(RSA_size(rsa), '\0'); int ret = 0; if (enc_or_dec) { ret = RSA_private_encrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, RSA_PKCS1_PADDING); } else { ret = RSA_private_decrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, RSA_PKCS1_PADDING); } if (ret > 0) { out_str.resize(ret); return out_str; } WarnL << getLastError(); return ""; #else WarnL << "ENABLE_OPENSSL disabled, you can not use any features based on openssl"; return ""; #endif //defined(ENABLE_OPENSSL) } string SSLUtil::getServerName(X509 *cer) { #if defined(ENABLE_OPENSSL) && defined(SSL_CTRL_SET_TLSEXT_HOSTNAME) if (!cer) { return ""; } //获取证书里的域名 X509_NAME *name = X509_get_subject_name(cer); char ret[256] = {0}; X509_NAME_get_text_by_NID(name, NID_commonName, ret, sizeof(ret)); return ret; #else return ""; #endif } }//namespace toolkit