Use openssl library instead of CLI to generate certificates
This commit is contained in:
parent
dbf56a9375
commit
aa3afa9062
|
@ -0,0 +1 @@
|
||||||
|
Barrier no longer uses openssl CLI tool for any operations and hooks into the openssl library directly.
|
|
@ -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
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue