refactor: generate Certs using secureUtils method
This commit is contained in:
parent
5a71d63923
commit
2058519e57
5 changed files with 82 additions and 185 deletions
|
|
@ -45,20 +45,6 @@ macro(configure_libs)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(OpenSSL ${REQUIRED_OPENSSL_VERSION} REQUIRED COMPONENTS SSL Crypto)
|
find_package(OpenSSL ${REQUIRED_OPENSSL_VERSION} REQUIRED COMPONENTS SSL Crypto)
|
||||||
if(WIN32) #Used for dev in TLS and WIX
|
|
||||||
cmake_path(SET OPENSSL_ROOT_DIR NORMALIZE "${OPENSSL_INCLUDE_DIR}/..")
|
|
||||||
message(VERBOSE "Set OPENSSL_ROOT_DIR: ${OPENSSL_ROOT_DIR}")
|
|
||||||
set(OPENSSL_EXE_DIR "${OPENSSL_ROOT_DIR}/tools/openssl")
|
|
||||||
add_definitions(-DOPENSSL_EXE_DIR="${OPENSSL_EXE_DIR}")
|
|
||||||
# HACK Install a copy of openssl on windows
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
${OPENSSL_EXE_DIR}/openssl.exe
|
|
||||||
${OPENSSL_EXE_DIR}/openssl.cnf
|
|
||||||
DESTINATION .
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
option(ENABLE_COVERAGE "Enable test coverage" OFF)
|
option(ENABLE_COVERAGE "Enable test coverage" OFF)
|
||||||
if(ENABLE_COVERAGE)
|
if(ENABLE_COVERAGE)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||||
* SPDX-FileCopyrightText: (C) 2015 Symless Ltd.
|
* SPDX-FileCopyrightText: (C) 2015 Symless Ltd.
|
||||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
*/
|
*/
|
||||||
|
|
@ -17,119 +18,11 @@
|
||||||
|
|
||||||
static const char *const kCertificateKeyLength = "rsa:";
|
static const char *const kCertificateKeyLength = "rsa:";
|
||||||
static const char *const kCertificateHashAlgorithm = "-sha256";
|
static const char *const kCertificateHashAlgorithm = "-sha256";
|
||||||
static const char *const kCertificateLifetime = "365";
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
static const char *const kWinOpenSslBinary = "openssl.exe";
|
|
||||||
static const char *const kConfigFile = "openssl.cnf";
|
|
||||||
#elif defined(Q_OS_UNIX)
|
|
||||||
static const char *const kUnixOpenSslCommand = "openssl";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
|
|
||||||
namespace deskflow::gui {
|
|
||||||
|
|
||||||
QString openSslWindowsDir()
|
|
||||||
{
|
|
||||||
|
|
||||||
auto openSslDir = QDir(QCoreApplication::applicationDirPath());
|
|
||||||
auto openSslBin = QFile(QStringLiteral("%1/%2").arg(openSslDir.absolutePath(), kWinOpenSslBinary));
|
|
||||||
|
|
||||||
// in production, openssl is deployed with the app.
|
|
||||||
// in development, we can use the openssl path available at compile-time.
|
|
||||||
if (!openSslBin.exists()) {
|
|
||||||
openSslDir = QDir(OPENSSL_EXE_DIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the path still isn't found, something is seriously wrong.
|
|
||||||
const auto path = openSslDir.absolutePath();
|
|
||||||
if (openSslDir.exists()) {
|
|
||||||
qDebug("openssl dir: %s", qUtf8Printable(path));
|
|
||||||
} else {
|
|
||||||
qFatal("openssl dir not found: %s", qUtf8Printable(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
return QDir::cleanPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString openSslWindowsBinary()
|
|
||||||
{
|
|
||||||
auto dir = QDir(openSslWindowsDir());
|
|
||||||
auto path = dir.filePath(kWinOpenSslBinary);
|
|
||||||
|
|
||||||
// when installed, there is no openssl bin dir; it's installed at the base.
|
|
||||||
// in development, we use the standard dir structure for openssl (bin dir).
|
|
||||||
if (!QFile::exists(path)) {
|
|
||||||
auto binDir = QDir(dir.filePath("bin"));
|
|
||||||
path = binDir.filePath(kWinOpenSslBinary);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the path still isn't found, something is seriously wrong.
|
|
||||||
if (!QFile::exists(path)) {
|
|
||||||
qFatal() << "openssl binary not found: " << path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace deskflow::gui
|
|
||||||
|
|
||||||
using namespace deskflow::gui;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent)
|
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TlsCertificate::runTool(const QStringList &args)
|
|
||||||
{
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
const auto program = openSslWindowsBinary();
|
|
||||||
#else
|
|
||||||
const auto program = kUnixOpenSslCommand;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QStringList environment;
|
|
||||||
|
|
||||||
// Windows is special! :)
|
|
||||||
// For OpenSSL, it's very common to bundle the openssl.exe and openssl.cnf files
|
|
||||||
// with the application. This is made a little more complex in the Windows dev
|
|
||||||
// env, because vcpkg can't find the openssl.cnf file by default, so we need to
|
|
||||||
// give it a bit of guidance by setting the `OPENSSL_CONF` env var.
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
const auto openSslDir = QDir(openSslWindowsDir());
|
|
||||||
const auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile));
|
|
||||||
environment << QString("OPENSSL_CONF=%1").arg(config);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QProcess process;
|
|
||||||
process.setEnvironment(environment);
|
|
||||||
for (const auto &envVar : std::as_const(environment)) {
|
|
||||||
qDebug("set env var: %s", qUtf8Printable(envVar));
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug("running: %s %s", qUtf8Printable(program), qUtf8Printable(args.join(" ")));
|
|
||||||
process.start(program, args);
|
|
||||||
bool success = process.waitForStarted();
|
|
||||||
|
|
||||||
QString toolStderr;
|
|
||||||
if (success && process.waitForFinished()) {
|
|
||||||
m_toolStdout = process.readAllStandardOutput().trimmed();
|
|
||||||
toolStderr = process.readAllStandardError().trimmed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (int code = process.exitCode(); !success || code != 0) {
|
|
||||||
qDebug("openssl failed with code %d: %s", code, qUtf8Printable(toolStderr));
|
|
||||||
|
|
||||||
qCritical("failed to generate tls certificate:\n\n%s", qUtf8Printable(toolStderr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TlsCertificate::generateCertificate(const QString &path, int keyLength)
|
bool TlsCertificate::generateCertificate(const QString &path, int keyLength)
|
||||||
{
|
{
|
||||||
qDebug("generating tls certificate: %s", qUtf8Printable(path));
|
qDebug("generating tls certificate: %s", qUtf8Printable(path));
|
||||||
|
|
@ -143,43 +36,14 @@ bool TlsCertificate::generateCertificate(const QString &path, int keyLength)
|
||||||
|
|
||||||
QString keySize = kCertificateKeyLength + QString::number(keyLength);
|
QString keySize = kCertificateKeyLength + QString::number(keyLength);
|
||||||
|
|
||||||
QStringList arguments;
|
try {
|
||||||
|
deskflow::generatePemSelfSignedCert(path.toStdString(), keyLength);
|
||||||
// self signed certificate
|
} catch (const std::exception &e) {
|
||||||
arguments.append("req");
|
qCritical() << "failed to generate self-signed pem cert: " << e.what();
|
||||||
arguments.append("-x509");
|
|
||||||
arguments.append("-nodes");
|
|
||||||
|
|
||||||
// valid duration
|
|
||||||
arguments.append("-days");
|
|
||||||
arguments.append(kCertificateLifetime);
|
|
||||||
|
|
||||||
// subject information
|
|
||||||
arguments.append("-subj");
|
|
||||||
|
|
||||||
QString subInfo("/CN=%1");
|
|
||||||
arguments.append(subInfo.arg(kAppName));
|
|
||||||
|
|
||||||
// private key
|
|
||||||
arguments.append("-newkey");
|
|
||||||
arguments.append(keySize);
|
|
||||||
|
|
||||||
// key output filename
|
|
||||||
arguments.append("-keyout");
|
|
||||||
arguments.append(path);
|
|
||||||
|
|
||||||
// certificate output filename
|
|
||||||
arguments.append("-out");
|
|
||||||
arguments.append(path);
|
|
||||||
|
|
||||||
if (runTool(arguments)) {
|
|
||||||
qDebug("tls certificate generated");
|
|
||||||
|
|
||||||
return generateFingerprint(path);
|
|
||||||
} else {
|
|
||||||
qCritical("failed to generate tls certificate");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
qDebug("tls certificate generated");
|
||||||
|
return generateFingerprint(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TlsCertificate::generateFingerprint(const QString &certificateFilename)
|
bool TlsCertificate::generateFingerprint(const QString &certificateFilename)
|
||||||
|
|
@ -199,28 +63,5 @@ bool TlsCertificate::generateFingerprint(const QString &certificateFilename)
|
||||||
|
|
||||||
int TlsCertificate::getCertKeyLength(const QString &path)
|
int TlsCertificate::getCertKeyLength(const QString &path)
|
||||||
{
|
{
|
||||||
|
return deskflow::getCertLength(path.toStdString());
|
||||||
QStringList arguments;
|
|
||||||
arguments.append("rsa");
|
|
||||||
arguments.append("-in");
|
|
||||||
arguments.append(path);
|
|
||||||
arguments.append("-text");
|
|
||||||
arguments.append("-noout");
|
|
||||||
|
|
||||||
if (!runTool(arguments)) {
|
|
||||||
qFatal("failed to get key length from certificate");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString searchStart("Private-Key: (");
|
|
||||||
const QString searchEnd(" bit");
|
|
||||||
|
|
||||||
// Get the line that contains the key length from the output
|
|
||||||
const auto indexStart = m_toolStdout.indexOf(searchStart);
|
|
||||||
const auto indexEnd = m_toolStdout.indexOf(searchEnd, indexStart);
|
|
||||||
const auto start = indexStart + searchStart.length();
|
|
||||||
const auto end = indexEnd - (indexStart + searchStart.length());
|
|
||||||
auto keyLength = m_toolStdout.mid(start, end);
|
|
||||||
|
|
||||||
return keyLength.toInt();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||||
* SPDX-FileCopyrightText: (C) 2015 Symless Ltd.
|
* SPDX-FileCopyrightText: (C) 2015 Symless Ltd.
|
||||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
*/
|
*/
|
||||||
|
|
@ -19,9 +20,5 @@ public:
|
||||||
int getCertKeyLength(const QString &path);
|
int getCertKeyLength(const QString &path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool runTool(const QStringList &args);
|
|
||||||
bool generateFingerprint(const QString &certificateFilename);
|
bool generateFingerprint(const QString &certificateFilename);
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_toolStdout;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "base/finally.h"
|
#include "base/finally.h"
|
||||||
#include "io/filesystem.h"
|
#include "io/filesystem.h"
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/x509v3.h>
|
#include <openssl/x509v3.h>
|
||||||
|
|
@ -84,4 +85,72 @@ std::vector<std::uint8_t> pemFileCertFingerprint(const std::string &path, Finger
|
||||||
|
|
||||||
return SSLCertFingerprint(cert, type);
|
return SSLCertFingerprint(cert, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generatePemSelfSignedCert(const std::string &path, int keyLength)
|
||||||
|
{
|
||||||
|
auto expirationDays = 365;
|
||||||
|
|
||||||
|
auto *privateKey = EVP_PKEY_new();
|
||||||
|
if (!privateKey) {
|
||||||
|
throw std::runtime_error("could not allocate private key for certificate");
|
||||||
|
}
|
||||||
|
auto privateKeyFree = finally([privateKey]() { EVP_PKEY_free(privateKey); });
|
||||||
|
|
||||||
|
privateKey = EVP_RSA_gen(keyLength);
|
||||||
|
|
||||||
|
auto *cert = X509_new();
|
||||||
|
if (!cert) {
|
||||||
|
throw std::runtime_error("could not allocate certificate");
|
||||||
|
}
|
||||||
|
auto certFree = 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), expirationDays * 24 * 3600);
|
||||||
|
X509_set_pubkey(cert, privateKey);
|
||||||
|
|
||||||
|
auto *name = X509_get_subject_name(cert);
|
||||||
|
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast<const unsigned char *>("Deskflow"), -1, -1, 0);
|
||||||
|
X509_set_issuer_name(cert, name);
|
||||||
|
|
||||||
|
X509_sign(cert, privateKey, EVP_sha256());
|
||||||
|
|
||||||
|
auto fp = fopenUtf8Path(path.c_str(), "w");
|
||||||
|
if (!fp) {
|
||||||
|
throw std::runtime_error("could not open certificate output path");
|
||||||
|
}
|
||||||
|
auto fileClose = finally([fp]() { std::fclose(fp); });
|
||||||
|
|
||||||
|
PEM_write_PrivateKey(fp, privateKey, nullptr, nullptr, 0, nullptr, nullptr);
|
||||||
|
PEM_write_X509(fp, cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCertLength(const std::string &path)
|
||||||
|
{
|
||||||
|
auto fp = fopenUtf8Path(path.c_str(), "r");
|
||||||
|
if (!fp) {
|
||||||
|
throw std::runtime_error("could not open certificate output path");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_PKEY *privateKey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!privateKey) {
|
||||||
|
throw std::runtime_error("could not open certificate");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_PKEY_base_id(privateKey) != EVP_PKEY_RSA) {
|
||||||
|
throw std::runtime_error("not an RSA key");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int size = EVP_PKEY_get_bits(privateKey);
|
||||||
|
|
||||||
|
EVP_PKEY_free(privateKey);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace deskflow
|
} // namespace deskflow
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,8 @@ std::string formatSSLFingerprint(const std::vector<uint8_t> &fingerprint, bool e
|
||||||
std::vector<std::uint8_t> SSLCertFingerprint(X509 *cert, FingerprintType type);
|
std::vector<std::uint8_t> SSLCertFingerprint(X509 *cert, FingerprintType type);
|
||||||
|
|
||||||
std::vector<std::uint8_t> pemFileCertFingerprint(const std::string &path, FingerprintType type);
|
std::vector<std::uint8_t> pemFileCertFingerprint(const std::string &path, FingerprintType type);
|
||||||
|
|
||||||
|
void generatePemSelfSignedCert(const std::string &path, int keyLength = 2048);
|
||||||
|
|
||||||
|
int getCertLength(const std::string &path);
|
||||||
} // namespace deskflow
|
} // namespace deskflow
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue