Use openssl library instead of CLI to generate certificates

This commit is contained in:
Povilas Kanapickas 2021-11-01 02:52:41 +02:00
parent dbf56a9375
commit aa3afa9062
5 changed files with 52 additions and 149 deletions

View File

@ -0,0 +1 @@
Barrier no longer uses openssl CLI tool for any operations and hooks into the openssl library directly.

View File

@ -1,65 +0,0 @@
#
# Barrier OpenSSL configuration file.
# Used for generation of certificate requests.
#
dir = .
[ca]
default_ca = CA_default
[CA_default]
serial = $dir/serial
database = $dir/certindex.txt
new_certs_dir = $dir/certs
certificate = $dir/cacert.pem
private_key = $dir/private/cakey.pem
default_days = 365
default_md = md5
preserve = no
email_in_dn = no
nameopt = default_ca
certopt = default_ca
policy = policy_match
[policy_match]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[req]
default_bits = 2048 # Size of keys
default_keyfile = key.pem # name of generated keys
default_md = md5 # message digest algorithm
string_mask = nombstr # permitted characters
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
0.organizationName = Organization Name (company)
organizationalUnitName = Organizational Unit Name (department, division)
emailAddress = Email Address
emailAddress_max = 40
localityName = Locality Name (city, district)
stateOrProvinceName = State or Province Name (full name)
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
commonName = Common Name (hostname, IP, or your name)
commonName_max = 64
0.organizationName_default = My Company
localityName_default = My Town
stateOrProvinceName_default = State or Providence
countryName_default = US
[v3_ca]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
[v3_req]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash

View File

@ -30,16 +30,8 @@
#include <openssl/pem.h> #include <openssl/pem.h>
#include <openssl/x509.h> #include <openssl/x509.h>
static const char kCertificateLifetime[] = "365";
static const char kCertificateSubjectInfo[] = "/CN=Barrier";
static const char kCertificateFilename[] = "Barrier.pem"; static const char kCertificateFilename[] = "Barrier.pem";
static const char kSslDir[] = "SSL"; static const char kSslDir[] = "SSL";
static const char kUnixOpenSslCommand[] = "openssl";
#if defined(Q_OS_WIN)
static const char kWinOpenSslBinary[] = "openssl.exe";
static const char kConfigFile[] = "barrier.conf";
#endif
SslCertificate::SslCertificate(QObject *parent) : SslCertificate::SslCertificate(QObject *parent) :
QObject(parent) QObject(parent)
@ -50,93 +42,21 @@ SslCertificate::SslCertificate(QObject *parent) :
} }
} }
std::pair<bool, std::string> SslCertificate::runTool(const QStringList& args)
{
QString program;
#if defined(Q_OS_WIN)
program = QCoreApplication::applicationDirPath();
program.append("\\").append(kWinOpenSslBinary);
#else
program = kUnixOpenSslCommand;
#endif
QStringList environment;
#if defined(Q_OS_WIN)
environment << QString("OPENSSL_CONF=%1\\%2")
.arg(QCoreApplication::applicationDirPath())
.arg(kConfigFile);
#endif
QProcess process;
process.setEnvironment(environment);
process.start(program, args);
bool success = process.waitForStarted();
std::string output;
QString standardError;
if (success && process.waitForFinished())
{
output = process.readAllStandardOutput().trimmed().toStdString();
standardError = process.readAllStandardError().trimmed();
}
int code = process.exitCode();
if (!success || code != 0)
{
emit error(
QString("SSL tool failed: %1\n\nCode: %2\nError: %3")
.arg(program)
.arg(process.exitCode())
.arg(standardError.isEmpty() ? "Unknown" : standardError));
return {false, output};
}
return {true, output};
}
void SslCertificate::generateCertificate() void SslCertificate::generateCertificate()
{ {
auto filename = QString::fromStdString(getCertificatePath()); auto filename = QString::fromStdString(getCertificatePath());
QFile file(filename); QFile file(filename);
if (!file.exists() || !isCertificateValid(filename)) { if (!file.exists() || !isCertificateValid(filename)) {
QStringList arguments;
// self signed certificate
arguments.append("req");
arguments.append("-x509");
arguments.append("-nodes");
// valid duration
arguments.append("-days");
arguments.append(kCertificateLifetime);
// subject information
arguments.append("-subj");
QString subInfo(kCertificateSubjectInfo);
arguments.append(subInfo);
// private key
arguments.append("-newkey");
arguments.append("rsa:2048");
QDir sslDir(QString::fromStdString(getCertificateDirectory())); QDir sslDir(QString::fromStdString(getCertificateDirectory()));
if (!sslDir.exists()) { if (!sslDir.exists()) {
sslDir.mkpath("."); sslDir.mkpath(".");
} }
// key output filename try {
arguments.append("-keyout"); barrier::generate_pem_self_signed_cert(filename.toStdString());
arguments.append(filename); } catch (const std::exception& e) {
emit error(QString("SSL tool failed: %1").arg(e.what()));
// certificate output filename
arguments.append("-out");
arguments.append(filename);
if (!runTool(arguments).first) {
return; return;
} }

View File

@ -20,6 +20,7 @@
#include "base/finally.h" #include "base/finally.h"
#include "io/fstream.h" #include "io/fstream.h"
#include <openssl/evp.h>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/x509v3.h> #include <openssl/x509v3.h>
#include <openssl/pem.h> #include <openssl/pem.h>
@ -97,4 +98,48 @@ std::vector<std::uint8_t> get_pem_file_cert_fingerprint(const std::string& path,
return get_ssl_cert_fingerprint(cert, type); return get_ssl_cert_fingerprint(cert, type);
} }
void generate_pem_self_signed_cert(const std::string& path)
{
auto expiration_days = 365;
auto* private_key = EVP_PKEY_new();
if (!private_key) {
throw std::runtime_error("Could not allocate private key for certificate");
}
auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); });
auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
if (!rsa) {
throw std::runtime_error("Failed to generate RSA key");
}
EVP_PKEY_assign_RSA(private_key, rsa);
auto* cert = X509_new();
if (!cert) {
throw std::runtime_error("Could not allocate certificate");
}
auto cert_free = finally([cert]() { X509_free(cert); });
ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get_notAfter(cert), expiration_days * 24 * 3600);
X509_set_pubkey(cert, private_key);
auto* name = X509_get_subject_name(cert);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
reinterpret_cast<const unsigned char *>("Barrier"), -1, -1, 0);
X509_set_issuer_name(cert, name);
X509_sign(cert, private_key, EVP_sha256());
auto fp = fopen_utf8_path(path.c_str(), "r");
if (!fp) {
throw std::runtime_error("Could not open certificate output path");
}
auto file_close = finally([fp]() { std::fclose(fp); });
PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr);
PEM_write_X509(fp, cert);
}
} // namespace barrier } // namespace barrier

View File

@ -38,6 +38,8 @@ std::vector<std::uint8_t> get_ssl_cert_fingerprint(X509* cert, FingerprintType t
std::vector<std::uint8_t> get_pem_file_cert_fingerprint(const std::string& path, std::vector<std::uint8_t> get_pem_file_cert_fingerprint(const std::string& path,
FingerprintType type); FingerprintType type);
void generate_pem_self_signed_cert(const std::string& path);
} // namespace barrier } // namespace barrier
#endif // BARRIER_LIB_NET_SECUREUTILS_H #endif // BARRIER_LIB_NET_SECUREUTILS_H