Use EC keys instead of RSA
Use smaller and safer EC keys, replacing 2048 bit RSA. NID_X9_62_prime256v1 is roughly as secure as a 3072 bit RSA key, but way shorter. Since we have to embed the key in the identity packet that is sent over UDP and some stacks aren't happy with large UDP messages (notably: macos), I switched to EC instead of to a longer RSA key. This seems to be compatible with other clients even on older systems like Android 5.0. I did stick with NID_X9_62_prime256v1 because stronger EC like NID_secp384r1 failed the handshake (I didn't investigate why). We now store the kind of key in the config, so we can know which kind of key we are loading.
This commit is contained in:
parent
e408e4e3bd
commit
9a39eaa237
5 changed files with 84 additions and 13 deletions
|
@ -527,14 +527,7 @@ void LanLinkProvider::configureSslSocket(QSslSocket *socket, const QString &devi
|
|||
// Configure for ssl
|
||||
QSslConfiguration sslConfig;
|
||||
sslConfig.setLocalCertificate(KdeConnectConfig::instance().certificate());
|
||||
|
||||
QFile privateKeyFile(KdeConnectConfig::instance().privateKeyPath());
|
||||
QSslKey privateKey;
|
||||
if (privateKeyFile.open(QIODevice::ReadOnly)) {
|
||||
privateKey = QSslKey(privateKeyFile.readAll(), QSsl::Rsa);
|
||||
}
|
||||
privateKeyFile.close();
|
||||
sslConfig.setPrivateKey(privateKey);
|
||||
sslConfig.setPrivateKey(KdeConnectConfig::instance().privateKey());
|
||||
|
||||
if (isDeviceTrusted) {
|
||||
QSslCertificate certificate = KdeConnectConfig::instance().getTrustedDeviceCertificate(deviceId);
|
||||
|
|
|
@ -117,6 +117,11 @@ QSslCertificate KdeConnectConfig::certificate()
|
|||
return d->m_certificate;
|
||||
}
|
||||
|
||||
QSslKey KdeConnectConfig::privateKey()
|
||||
{
|
||||
return d->m_privateKey;
|
||||
}
|
||||
|
||||
DeviceInfo KdeConnectConfig::deviceInfo()
|
||||
{
|
||||
const auto incoming = PluginLoader::instance()->incomingCapabilities();
|
||||
|
@ -130,6 +135,16 @@ DeviceInfo KdeConnectConfig::deviceInfo()
|
|||
QSet(outgoing.begin(), outgoing.end()));
|
||||
}
|
||||
|
||||
QSsl::KeyAlgorithm KdeConnectConfig::privateKeyAlgorithm()
|
||||
{
|
||||
QString value = d->m_config->value(QStringLiteral("keyAlgorithm"), QStringLiteral("RSA")).toString();
|
||||
if (value == QLatin1String("EC")) {
|
||||
return QSsl::KeyAlgorithm::Ec;
|
||||
} else {
|
||||
return QSsl::KeyAlgorithm::Rsa;
|
||||
}
|
||||
}
|
||||
|
||||
QDir KdeConnectConfig::baseConfigDir()
|
||||
{
|
||||
QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
|
||||
|
@ -250,7 +265,7 @@ bool KdeConnectConfig::loadPrivateKey(const QString &keyPath)
|
|||
{
|
||||
QFile privKey(keyPath);
|
||||
if (privKey.exists() && privKey.open(QIODevice::ReadOnly)) {
|
||||
d->m_privateKey = QSslKey(privKey.readAll(), QSsl::KeyAlgorithm::Rsa);
|
||||
d->m_privateKey = QSslKey(privKey.readAll(), privateKeyAlgorithm());
|
||||
if (d->m_privateKey.isNull()) {
|
||||
qCWarning(KDECONNECT_CORE) << "Private key from" << keyPath << "is not valid!";
|
||||
}
|
||||
|
@ -295,7 +310,7 @@ void KdeConnectConfig::generatePrivateKey(const QString &keyPath)
|
|||
{
|
||||
qCDebug(KDECONNECT_CORE) << "Generating private key";
|
||||
|
||||
d->m_privateKey = SslHelper::generateRsaPrivateKey();
|
||||
d->m_privateKey = SslHelper::generateEcPrivateKey();
|
||||
if (d->m_privateKey.isNull()) {
|
||||
qCritical() << "Could not generate the private key";
|
||||
Daemon::instance()->reportError(i18n("KDE Connect failed to start"), i18n("Could not generate the private key."));
|
||||
|
@ -313,6 +328,9 @@ void KdeConnectConfig::generatePrivateKey(const QString &keyPath)
|
|||
}
|
||||
}
|
||||
|
||||
d->m_config->setValue(QStringLiteral("keyAlgorithm"), QStringLiteral("EC"));
|
||||
d->m_config->sync();
|
||||
|
||||
if (error) {
|
||||
Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store private key file: %1", keyPath));
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define KDECONNECTCONFIG_H
|
||||
|
||||
#include <QDir>
|
||||
#include <QSslKey>
|
||||
|
||||
#include "deviceinfo.h"
|
||||
#include "kdeconnectcore_export.h"
|
||||
|
@ -26,8 +27,10 @@ public:
|
|||
QString deviceId();
|
||||
QString name();
|
||||
DeviceType deviceType();
|
||||
QSslKey privateKey();
|
||||
QSslCertificate certificate();
|
||||
DeviceInfo deviceInfo();
|
||||
QSsl::KeyAlgorithm privateKeyAlgorithm();
|
||||
QString privateKeyPath();
|
||||
QString certificatePath();
|
||||
|
||||
|
|
|
@ -26,6 +26,61 @@ QString getSslError()
|
|||
return QString::fromLatin1(buf);
|
||||
}
|
||||
|
||||
QSslKey generateEcPrivateKey()
|
||||
{
|
||||
// Initialize context.
|
||||
auto pctxRaw = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
|
||||
auto pctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(pctxRaw, ::EVP_PKEY_CTX_free);
|
||||
if (!pctx) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed to allocate context " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
if (EVP_PKEY_keygen_init(pctx.get()) <= 0) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed to initialize context " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
// Set the curve.
|
||||
if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx.get(), NID_X9_62_prime256v1) <= 0) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed to set curve " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
// Generate private key.
|
||||
auto pkey = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>(EVP_PKEY_new(), ::EVP_PKEY_free);
|
||||
if (!pkey) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed to allocate private key " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
auto pkey_raw = pkey.get();
|
||||
if (EVP_PKEY_keygen(pctx.get(), &pkey_raw) <= 0) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed to generate private key " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
// Convert private key format to PEM as required by QSslKey.
|
||||
auto bio = std::unique_ptr<BIO, decltype(&::BIO_free_all)>(BIO_new(BIO_s_mem()), ::BIO_free_all);
|
||||
if (!bio) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed to allocate I/O abstraction " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
if (!PEM_write_bio_PrivateKey(bio.get(), pkey_raw, nullptr, nullptr, 0, nullptr, nullptr)) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed write PEM format private key to BIO " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
BUF_MEM *mem = nullptr;
|
||||
if (!BIO_get_mem_ptr(bio.get(), &mem)) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate EC Private Key failed get PEM format address " << getSslError();
|
||||
return QSslKey();
|
||||
}
|
||||
|
||||
return QSslKey(QByteArray(mem->data, mem->length), QSsl::KeyAlgorithm::Ec);
|
||||
}
|
||||
|
||||
QSslKey generateRsaPrivateKey()
|
||||
{
|
||||
// Initialize context.
|
||||
|
@ -86,11 +141,12 @@ QSslCertificate generateSelfSignedCertificate(const QSslKey &qtPrivateKey, const
|
|||
// Create certificate.
|
||||
auto x509 = std::unique_ptr<X509, decltype(&::X509_free)>(X509_new(), ::X509_free);
|
||||
if (!x509) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to allocate certifcate " << getSslError();
|
||||
qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to allocate certificate " << getSslError();
|
||||
return QSslCertificate();
|
||||
}
|
||||
|
||||
if (!X509_set_version(x509.get(), 2)) {
|
||||
constexpr int x509version = 3 - 1; // version is 0-indexed, so we need the -1
|
||||
if (!X509_set_version(x509.get(), x509version)) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to set version " << getSslError();
|
||||
return QSslCertificate();
|
||||
}
|
||||
|
@ -169,7 +225,7 @@ QSslCertificate generateSelfSignedCertificate(const QSslKey &qtPrivateKey, const
|
|||
}
|
||||
|
||||
// Sign the certificate with private key.
|
||||
if (!X509_sign(x509.get(), pkey.get(), EVP_sha256())) {
|
||||
if (!X509_sign(x509.get(), pkey.get(), EVP_sha512())) {
|
||||
qCWarning(KDECONNECT_CORE) << "Generate Self Signed Certificate failed to sign certificate " << getSslError();
|
||||
return QSslCertificate();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
namespace SslHelper
|
||||
{
|
||||
QSslKey generateEcPrivateKey();
|
||||
QSslKey generateRsaPrivateKey();
|
||||
QSslCertificate generateSelfSignedCertificate(const QSslKey &privateKey, const QString &commonName);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue