File indexing completed on 2024-05-05 16:10:20

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 2001 George Staikos <staikos@kde.org>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "ksslkeygen.h"
0022 #include "ksslkeygen_p.h"
0023 #include "ui_keygenwizard.h"
0024 
0025 #include <klocalizedstring.h>
0026 #include <kmessagebox.h>
0027 #include <kopenssl.h>
0028 #include <kwallet.h>
0029 
0030 #include <QProgressDialog>
0031 #include <QStandardPaths>
0032 #include <qplatformdefs.h>
0033 #include <qtemporaryfile.h>
0034 
0035 #include <assert.h>
0036 
0037 KSSLKeyGenWizardPage2::KSSLKeyGenWizardPage2(QWidget *parent)
0038     : QWizardPage(parent)
0039 {
0040     ui2 = new Ui_KGWizardPage2;
0041     ui2->setupUi(this);
0042     connect(ui2->_password1, SIGNAL(textChanged(QString)), this, SLOT(slotPassChanged()));
0043     connect(ui2->_password2, SIGNAL(textChanged(QString)), this, SLOT(slotPassChanged()));
0044 }
0045 
0046 bool KSSLKeyGenWizardPage2::isComplete() const
0047 {
0048     return ui2->_password1->text() == ui2->_password2->text() && ui2->_password1->text().length() >= 4;
0049 }
0050 
0051 void KSSLKeyGenWizardPage2::slotPassChanged()
0052 {
0053     emit completeChanged(); // well maybe it hasn't changed, but it might have; QWizard calls isComplete() to find out
0054 }
0055 
0056 QString KSSLKeyGenWizardPage2::password() const
0057 {
0058     Q_ASSERT(isComplete());
0059     return ui2->_password1->text();
0060 }
0061 
0062 ////
0063 
0064 class KSSLKeyGenPrivate
0065 {
0066 public:
0067     KSSLKeyGenPrivate()
0068         : idx(-1)
0069     {
0070     }
0071     int idx;
0072     Ui_KGWizardPage1 *ui1;
0073     KSSLKeyGenWizardPage2 *page2;
0074 };
0075 
0076 KSSLKeyGen::KSSLKeyGen(QWidget *parent)
0077     : QWizard(parent), d(new KSSLKeyGenPrivate)
0078 {
0079 #if KSSL_HAVE_SSL
0080 
0081     QWizardPage *page1 = new QWizardPage(this);
0082     page1->setTitle(i18n("KDE Certificate Request"));
0083     d->ui1 = new Ui_KGWizardPage1;
0084     d->ui1->setupUi(page1);
0085     addPage(page1);
0086     //setHelpEnabled(page1, false);
0087 
0088     d->page2 = new KSSLKeyGenWizardPage2(this);
0089     d->page2->setTitle(i18n("KDE Certificate Request - Password"));
0090     addPage(d->page2);
0091 #else
0092     // tell him he doesn't have SSL
0093 #endif
0094 }
0095 
0096 KSSLKeyGen::~KSSLKeyGen()
0097 {
0098     delete d->ui1;
0099     delete d;
0100 }
0101 
0102 bool KSSLKeyGen::validateCurrentPage()
0103 {
0104     if (currentPage() != d->page2) {
0105         return true;
0106     }
0107 
0108     assert(d->idx >= 0 && d->idx <= 3);   // for now
0109 
0110     // Generate the CSR
0111     int bits;
0112     switch (d->idx) {
0113     case 0:
0114         bits = 2048;
0115         break;
0116     case 1:
0117         bits = 1024;
0118         break;
0119     case 2:
0120         bits = 768;
0121         break;
0122     case 3:
0123         bits = 512;
0124         break;
0125     default:
0126         KMessageBox::error(this, i18n("Unsupported key size."), i18n("KDE SSL Information"));
0127         return false;
0128     }
0129 
0130     QProgressDialog *kpd = new QProgressDialog(this);
0131     kpd->setObjectName("progress dialog");
0132     kpd->setWindowTitle(i18n("KDE"));
0133     kpd->setLabelText(i18n("Please wait while the encryption keys are generated..."));
0134     kpd->setRange(0, 100);
0135     kpd->setValue(0);
0136     kpd->show();
0137     // FIXME - progress dialog won't show this way
0138 
0139     int rc = generateCSR("This CSR" /*FIXME */, d->page2->password(), bits, 0x10001 /* This is the traditional exponent used */);
0140     if (rc != 0) { // error
0141         return false;
0142     }
0143 
0144     kpd->setValue(100);
0145 
0146 #if 0 // TODO: implement
0147     if (rc == 0 && KWallet::Wallet::isEnabled()) {
0148         rc = KMessageBox::questionTwoActions(this, i18n("Do you wish to store the passphrase in your wallet file?"), QString(), KGuiItem(i18n("Store")), KGuiItem(i18n("Do Not Store")));
0149         if (rc == KMessageBox::PrimaryAction) {
0150             KWallet::Wallet *w = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), effectiveWinId());
0151             if (w) {
0152                 // FIXME: store passphrase in wallet
0153                 delete w;
0154             }
0155         }
0156     }
0157 #endif
0158 
0159     kpd->deleteLater();
0160     return true;
0161 }
0162 
0163 int KSSLKeyGen::generateCSR(const QString &name, const QString &pass, int bits, int e)
0164 {
0165 #if KSSL_HAVE_SSL
0166     KOSSL *kossl = KOSSL::self();
0167     int rc;
0168 
0169     X509_REQ *req = kossl->X509_REQ_new();
0170     if (!req) {
0171         return -2;
0172     }
0173 
0174     EVP_PKEY *pkey = kossl->EVP_PKEY_new();
0175     if (!pkey) {
0176         kossl->X509_REQ_free(req);
0177         return -4;
0178     }
0179 
0180     RSA *rsakey = kossl->RSA_generate_key(bits, e, nullptr, nullptr);
0181     if (!rsakey) {
0182         kossl->X509_REQ_free(req);
0183         kossl->EVP_PKEY_free(pkey);
0184         return -3;
0185     }
0186 
0187     rc = kossl->EVP_PKEY_assign(pkey, EVP_PKEY_RSA, (char *)rsakey);
0188 
0189     rc = kossl->X509_REQ_set_pubkey(req, pkey);
0190 
0191     // Set the subject
0192     X509_NAME *n = kossl->X509_NAME_new();
0193 
0194     kossl->X509_NAME_add_entry_by_txt(n, (char *)LN_countryName, MBSTRING_UTF8, (unsigned char *)name.toLocal8Bit().data(), -1, -1, 0);
0195     kossl->X509_NAME_add_entry_by_txt(n, (char *)LN_organizationName, MBSTRING_UTF8, (unsigned char *)name.toLocal8Bit().data(), -1, -1, 0);
0196     kossl->X509_NAME_add_entry_by_txt(n, (char *)LN_organizationalUnitName, MBSTRING_UTF8, (unsigned char *)name.toLocal8Bit().data(), -1, -1, 0);
0197     kossl->X509_NAME_add_entry_by_txt(n, (char *)LN_localityName, MBSTRING_UTF8, (unsigned char *)name.toLocal8Bit().data(), -1, -1, 0);
0198     kossl->X509_NAME_add_entry_by_txt(n, (char *)LN_stateOrProvinceName, MBSTRING_UTF8, (unsigned char *)name.toLocal8Bit().data(), -1, -1, 0);
0199     kossl->X509_NAME_add_entry_by_txt(n, (char *)LN_commonName, MBSTRING_UTF8, (unsigned char *)name.toLocal8Bit().data(), -1, -1, 0);
0200     kossl->X509_NAME_add_entry_by_txt(n, (char *)LN_pkcs9_emailAddress, MBSTRING_UTF8, (unsigned char *)name.toLocal8Bit().data(), -1, -1, 0);
0201 
0202     rc = kossl->X509_REQ_set_subject_name(req, n);
0203 
0204     rc = kossl->X509_REQ_sign(req, pkey, kossl->EVP_md5());
0205 
0206     // We write it to the database and then the caller can obtain it
0207     // back from there.  Yes it's inefficient, but it doesn't happen
0208     // often and this way things are uniform.
0209 
0210     const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kssl";
0211     QTemporaryFile csrFile(path + "csr_XXXXXX.der");
0212     csrFile.setAutoRemove(false);
0213 
0214     if (!csrFile.open()) {
0215         kossl->X509_REQ_free(req);
0216         kossl->EVP_PKEY_free(pkey);
0217         return -5;
0218     }
0219 
0220     QTemporaryFile p8File(path + "pkey_XXXXXX.p8");
0221     p8File.setAutoRemove(false);
0222 
0223     if (!p8File.open()) {
0224         kossl->X509_REQ_free(req);
0225         kossl->EVP_PKEY_free(pkey);
0226         return -5;
0227     }
0228 
0229     FILE *csr_fs = QT_FOPEN(QFile::encodeName(csrFile.fileName()), "r+");
0230     FILE *p8_fs = QT_FOPEN(QFile::encodeName(p8File.fileName()), "r+");
0231 
0232     kossl->i2d_X509_REQ_fp(csr_fs, req);
0233 
0234     kossl->i2d_PKCS8PrivateKey_fp(p8_fs, pkey,
0235                                   kossl->EVP_bf_cbc(), pass.toLocal8Bit().data(),
0236                                   pass.length(), nullptr, nullptr);
0237 
0238     // FIXME Write kconfig entry to store the filenames under the md5 hash
0239 
0240     kossl->X509_REQ_free(req);
0241     kossl->EVP_PKEY_free(pkey);
0242 
0243     fclose(csr_fs);
0244     fclose(p8_fs);
0245 
0246     return 0;
0247 #else
0248     return -1;
0249 #endif
0250 }
0251 
0252 QStringList KSSLKeyGen::supportedKeySizes()
0253 {
0254     QStringList x;
0255 
0256 #if KSSL_HAVE_SSL
0257     x   << i18n("2048 (High Grade)")
0258         << i18n("1024 (Medium Grade)")
0259         << i18n("768  (Low Grade)")
0260         << i18n("512  (Low Grade)");
0261 #else
0262     x   << i18n("No SSL support.");
0263 #endif
0264 
0265     return x;
0266 }
0267 
0268 void KSSLKeyGen::setKeySize(int idx)
0269 {
0270     d->idx = idx;
0271 }
0272 
0273 #include "moc_ksslkeygen.cpp"
0274 
0275 #include "moc_ksslkeygen_p.cpp"