From 45de747461cfc64841cb86cae868ecf2e65a254a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Hohnst=C3=A4dt?= Date: Sat, 20 Feb 2021 08:40:17 +0100 Subject: [PATCH] Add support for SSH2 ED25519 private key export Encrypting the SSH2 private keys is not supported, yet. --- lib/db_key.cpp | 38 ++++++++++++++++++++++++++------------ lib/openssl_compat.h | 2 ++ lib/pki_key.cpp | 36 +++++++++++++++++++++++++++++++++--- lib/pki_key.h | 3 ++- widgets/ExportDialog.h | 3 ++- 5 files changed, 65 insertions(+), 17 deletions(-) diff --git a/lib/db_key.cpp b/lib/db_key.cpp index 92b8d0d7..e40fbae3 100644 --- a/lib/db_key.cpp +++ b/lib/db_key.cpp @@ -216,12 +216,15 @@ exportType::etype db_key::clipboardFormat(QModelIndexList indexes) const if (ssh2compatible) types << exportType(exportType::SSH2_public, "pub", tr("SSH2 public")); - if (allPriv) + if (allPriv) { types << exportType(exportType::PEM_private, "pem", tr("PEM private")) << exportType(exportType::PKCS8, "pk8", "PKCS#8"); - + if (ssh2compatible) + types << exportType(exportType::SSH2_private, + "", tr("SSH2 private")); + } ExportDialog *dlg = new ExportDialog(NULL, tr("Export keys to Clipboard"), QString(), NULL, QPixmap(":keyImg"), types); @@ -241,7 +244,7 @@ void db_key::store(QModelIndex index) const EVP_CIPHER *algo = NULL, *encrypt = EVP_aes_256_cbc(); QString title = tr("Export public key [%1]"); QList types; - bool pvk = false; + bool pvk = false, ed25519 = false; pki_key *key = fromIndex(index); pki_evp *privkey = dynamic_cast(key); @@ -253,6 +256,10 @@ void db_key::store(QModelIndex index) int keytype = key->getKeyType(); if (keytype == EVP_PKEY_RSA || keytype == EVP_PKEY_DSA) pvk = true; +#ifdef EVP_PKEY_ED25519 + if (keytype == EVP_PKEY_ED25519) + ed25519 = true; +#endif #endif types << @@ -264,12 +271,13 @@ void db_key::store(QModelIndex index) "pub", tr("SSH2 public")); if (!key->isPubKey() && !key->isToken()) { QList usual; + if (!ed25519) + types << exportType(exportType::PEM_private_encrypt, + "pem", tr("PEM encryped")); types << - exportType(exportType::DER_private, "der", - tr("DER private")) << - exportType(exportType::PEM_private_encrypt, "pem", - tr("PEM encryped")) << - exportType(exportType::PKCS8, "pk8", "PKCS#8"); + exportType(exportType::DER_private, "der", + tr("DER private")) << + exportType(exportType::PKCS8, "pk8", "PKCS#8"); if (pvk) { types << @@ -278,11 +286,14 @@ void db_key::store(QModelIndex index) exportType(exportType::PVK_encrypt, "pvk", tr("PVK encrypted")); } - usual << - exportType(exportType::PEM_private, "pem", - tr("PEM private")) << - exportType(exportType::PKCS8_encrypt, "pk8", + if (!ed25519) + usual << exportType(exportType::PEM_private, "pem", + tr("PEM private")); + usual << exportType(exportType::PKCS8_encrypt, "pk8", tr("PKCS#8 encrypted")); + if (key->SSH2_compatible()) + usual << exportType(exportType::SSH2_private, "", + tr("SSH2 private")); title = tr("Export private key [%1]"); types = usual << exportType() << types; } @@ -338,6 +349,9 @@ void db_key::store(QModelIndex index) case exportType::SSH2_public: key->writeSSH2public(file); break; + case exportType::SSH2_private: + key->writeSSH2private(file, PwDialog::pwCallback); + break; case exportType::PVK_private: privkey->writePVKprivate(file, NULL); break; diff --git a/lib/openssl_compat.h b/lib/openssl_compat.h index ee78b951..8939b761 100644 --- a/lib/openssl_compat.h +++ b/lib/openssl_compat.h @@ -105,6 +105,7 @@ static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) OPENSSL_free(ctx); } +#if 0 static inline EVP_CIPHER_CTX *EVP_CIPHER_CTX_new() { return (EVP_CIPHER_CTX*)OPENSSL_zalloc(sizeof(EVP_CIPHER_CTX)); @@ -115,6 +116,7 @@ static inline void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx) EVP_CIPHER_CTX_cleanup(ctx); OPENSSL_free(ctx); } +#endif #endif diff --git a/lib/pki_key.cpp b/lib/pki_key.cpp index 37a85479..eb6514a5 100644 --- a/lib/pki_key.cpp +++ b/lib/pki_key.cpp @@ -92,9 +92,10 @@ QByteArray pki_key::i2d() const return i2d_bytearray(I2D_VOID(i2d_PUBKEY), key); } -void pki_key::write_SSH2_ed25519_private(BioByteArray &b, +void pki_key::write_SSH2_ed25519_private(BIO *b, const EVP_PKEY *pkey, const EVP_CIPHER *enc) const { +#ifndef OPENSSL_NO_EC static const char data0001[] = { 0, 0, 0, 1}; char buf_nonce[8]; QByteArray data, priv, pubfull; @@ -118,6 +119,7 @@ void pki_key::write_SSH2_ed25519_private(BioByteArray &b, PEM_write_bio(b, PEM_STRING_OPENSSH_KEY, (char*)"", (unsigned char*)(data.data()), data.size()); pki_openssl_error(); +#endif } bool pki_key::pem(BioByteArray &b, int format) @@ -131,6 +133,7 @@ bool pki_key::pem(BioByteArray &b, int format) b += SSH2publicQByteArray(); break; case exportType::PEM_private: + case exportType::SSH2_private: pkey = decryptKey(); keytype = EVP_PKEY_id(pkey); switch (keytype) { @@ -152,6 +155,8 @@ bool pki_key::pem(BioByteArray &b, int format) break; #ifdef EVP_PKEY_ED25519 case EVP_PKEY_ED25519: + if (format == exportType::PEM_private) + return false; write_SSH2_ed25519_private(b, pkey, NULL); break; #endif @@ -173,6 +178,19 @@ bool pki_key::pem(BioByteArray &b, int format) return true; } +void pki_key::writeSSH2private(XFile &file, pem_password_cb *cb) const +{ + (void)cb; +// pass_info p(XCA_TITLE, tr("Please enter the password protecting the SSH2 private key '%1'").arg(getIntName())); + + EVP_PKEY *pkey = decryptKey(); + if (!pkey) { + pki_openssl_error(); + return; + } + write_SSH2_ed25519_private(file.bio(), pkey, NULL); +} + QString pki_key::length() const { bool dsa_unset = false; @@ -355,18 +373,17 @@ QString pki_key::ecPubKey() const return pub; } +#ifdef EVP_PKEY_ED25519 static QByteArray ed25519Key(int(*EVP_PKEY_get_raw) (const EVP_PKEY*, unsigned char *, size_t *), const EVP_PKEY *pkey) { -#ifdef EVP_PKEY_ED25519 unsigned char k[ED25519_KEYLEN]; size_t len = sizeof k; if (EVP_PKEY_id(pkey) == EVP_PKEY_ED25519 && EVP_PKEY_get_raw(pkey, k, &len)) return QByteArray((char*)k, len); -#endif return QByteArray(); } @@ -379,6 +396,19 @@ QByteArray pki_key::ed25519PrivKey(const EVP_PKEY *pkey) const { return ed25519Key(EVP_PKEY_get_raw_private_key, pkey); } +#else + +QByteArray pki_key::ed25519PubKey() const +{ + return QByteArray(); +} + +QByteArray pki_key::ed25519PrivKey(const EVP_PKEY *) const +{ + return QByteArray(); +} + +#endif #endif QList pki_key::possibleHashNids() diff --git a/lib/pki_key.h b/lib/pki_key.h index f465b28e..4a1dda4a 100644 --- a/lib/pki_key.h +++ b/lib/pki_key.h @@ -236,9 +236,10 @@ class pki_key: public pki_base QByteArray i2d() const; EVP_PKEY *load_ssh2_key(XFile &file); void writeSSH2public(XFile &file) const; + void writeSSH2private(XFile &file, pem_password_cb *cb) const; QString fingerprint(const QString &format) const; bool SSH2_compatible() const; - void write_SSH2_ed25519_private(BioByteArray &b, + void write_SSH2_ed25519_private(BIO *b, const EVP_PKEY *pkey, const EVP_CIPHER *enc) const; void print(BioByteArray &b, enum print_opt opt) const; void resetUcount() diff --git a/widgets/ExportDialog.h b/widgets/ExportDialog.h index 3732d967..603c1582 100644 --- a/widgets/ExportDialog.h +++ b/widgets/ExportDialog.h @@ -21,7 +21,7 @@ class exportType { PEM_key, PEM_private, PEM_private_encrypt, DER_private, DER_key, PKCS8, PKCS8_encrypt, SSH2_public, PEM_selected, PKCS7_selected, Index, vcalendar, vcalendar_ca, - PVK_private, PVK_encrypt, ETYPE_max }; + PVK_private, PVK_encrypt, SSH2_private, ETYPE_max }; enum etype type; QString desc; QString extension; @@ -41,6 +41,7 @@ class exportType { case PEM_private: case PEM_private_encrypt: case PEM_selected: + case SSH2_private: return true; default: return false;