aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@404: * Software engineering by Intevation GmbH aheinecke@404: * aheinecke@404: * This file is Free Software under the GNU GPL (v>=2) aheinecke@404: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@404: * See LICENSE.txt for details. aheinecke@404: */ rrenkert@367: #include "createcertlistdialog.h" aheinecke@454: #include "sslhelp.h" aheinecke@454: #include "administratorwindow.h" aheinecke@454: andre@1390: #include "pubkey.h" andre@1390: rrenkert@367: #include aheinecke@454: #include rrenkert@367: #include rrenkert@367: #include rrenkert@367: #include rrenkert@367: #include rrenkert@367: #include rrenkert@367: #include rrenkert@367: #include rrenkert@413: #include rrenkert@367: aheinecke@454: #include aheinecke@454: aheinecke@454: CreateCertListDialog::CreateCertListDialog(AdministratorWindow *parent) : aheinecke@454: QDialog(parent), aheinecke@454: mAdminWindow(parent), aheinecke@454: mPk(NULL) rrenkert@367: { rrenkert@413: setWindowTitle(tr("Save certificate list")); rrenkert@367: setupGUI(); rrenkert@413: resize(500, 200); aheinecke@465: mKeyFile->setText(mAdminWindow->settings()->value("LastKey", QString()).toString()); aheinecke@465: mSaveDir->setText(mAdminWindow->settings()->value("LastOutputDir", QString()).toString()); aheinecke@465: if (!mKeyFile->text().isEmpty()) { aheinecke@465: loadKeyFile(mKeyFile->text()); aheinecke@465: } rrenkert@367: } rrenkert@367: rrenkert@367: void CreateCertListDialog::setupGUI() rrenkert@367: { rrenkert@367: /* Top level layout / widgets */ rrenkert@367: QVBoxLayout *topLayout = new QVBoxLayout; rrenkert@413: QVBoxLayout *headerLayout = new QVBoxLayout; rrenkert@413: QHBoxLayout *headerSubLayout = new QHBoxLayout; rrenkert@428: QHBoxLayout *centerLayout = new QHBoxLayout; rrenkert@367: QHBoxLayout *bottomLayout = new QHBoxLayout; rrenkert@428: QVBoxLayout *labelLayout = new QVBoxLayout; rrenkert@428: QVBoxLayout *fieldLayout = new QVBoxLayout; rrenkert@428: QVBoxLayout *buttonLayout = new QVBoxLayout; rrenkert@367: rrenkert@413: QLabel *header = new QLabel("

" + tr("Save certificate list") + "

"); rrenkert@413: QLabel *description = new QLabel( rrenkert@426: tr("Save all managed root certificates in a new, signed certificate list.")); rrenkert@413: headerSubLayout->insertSpacing(0, 40); rrenkert@413: headerSubLayout->addWidget(description); rrenkert@413: QFrame *headerSeparator = new QFrame(); rrenkert@413: headerSeparator->setFrameShape(QFrame::HLine); rrenkert@413: headerSeparator->setFrameShadow(QFrame::Sunken); rrenkert@413: headerLayout->addWidget(header); rrenkert@413: headerLayout->addLayout(headerSubLayout); rrenkert@413: headerLayout->addWidget(headerSeparator); rrenkert@428: headerLayout->insertSpacing(3, 10); rrenkert@367: rrenkert@522: QLabel *certLabel = new QLabel(tr("Select signing key:")); rrenkert@522: QLabel *saveLabel = new QLabel(tr("Select output folder:")); rrenkert@428: labelLayout->addWidget(certLabel); rrenkert@428: labelLayout->addWidget(saveLabel); rrenkert@428: aheinecke@465: mKeyFile = new QLineEdit(); aheinecke@465: mSaveDir = new QLineEdit(); aheinecke@465: fieldLayout->addWidget(mKeyFile); aheinecke@465: fieldLayout->addWidget(mSaveDir); rrenkert@428: rrenkert@367: QPushButton *certSelect = new QPushButton("..."); rrenkert@367: certSelect->setFixedWidth(30); rrenkert@428: connect(certSelect, SIGNAL(clicked()), this, SLOT(openCertificateSelect())); rrenkert@367: QPushButton *saveSelect = new QPushButton("..."); rrenkert@367: connect(saveSelect, SIGNAL(clicked()), this, SLOT(openSaveLocation())); rrenkert@367: saveSelect->setFixedWidth(30); rrenkert@428: buttonLayout->addWidget(certSelect); rrenkert@428: buttonLayout->addWidget(saveSelect); rrenkert@413: rrenkert@426: QString footerText = tr("In addition, each certificate list will be saved " rrenkert@413: "automatically in the archive directory:\n"); rrenkert@426: footerText.append(QStandardPaths::writableLocation( rrenkert@426: QStandardPaths::DataLocation)); rrenkert@413: QLabel *footer = new QLabel(footerText); rrenkert@367: rrenkert@428: centerLayout->addLayout(labelLayout); rrenkert@428: centerLayout->addLayout(fieldLayout); rrenkert@428: centerLayout->addLayout(buttonLayout); rrenkert@367: emanuel@1199: QPushButton *create = new QPushButton(" " + tr("Save list")); rrenkert@367: connect(create, SIGNAL(clicked()), this, SLOT(createList())); emanuel@1199: QPushButton *cancel = new QPushButton(" " + tr("Cancel")); rrenkert@413: connect(cancel, SIGNAL(clicked()), this, SLOT(close())); rrenkert@367: bottomLayout->insertStretch(0, 10); rrenkert@367: bottomLayout->addWidget(create); rrenkert@413: bottomLayout->addWidget(cancel); rrenkert@413: rrenkert@413: QFrame *bottomSeparator = new QFrame(); rrenkert@413: bottomSeparator->setFrameShape(QFrame::HLine); rrenkert@413: bottomSeparator->setFrameShadow(QFrame::Sunken); rrenkert@367: rrenkert@367: topLayout->addLayout(headerLayout); rrenkert@367: topLayout->addLayout(centerLayout); rrenkert@367: topLayout->insertStretch(2, 10); rrenkert@413: topLayout->addWidget(footer); rrenkert@428: topLayout->insertSpacing(4, 10); rrenkert@413: topLayout->addWidget(bottomSeparator); rrenkert@367: topLayout->addLayout(bottomLayout); rrenkert@367: rrenkert@367: setLayout(topLayout); rrenkert@367: rrenkert@367: return; rrenkert@367: } rrenkert@367: aheinecke@454: void CreateCertListDialog::showErrorMessage(const QString &msg) aheinecke@454: { aheinecke@454: QMessageBox::warning(this, tr("Error!"), msg); aheinecke@454: } aheinecke@454: aheinecke@465: void CreateCertListDialog::loadKeyFile(const QString& fileName) rrenkert@367: { aheinecke@454: if (mPk != NULL) { aheinecke@454: pk_free(mPk); aheinecke@454: delete mPk; aheinecke@454: mPk = NULL; aheinecke@454: } aheinecke@454: aheinecke@454: mPk = new pk_context; aheinecke@454: pk_init(mPk); aheinecke@465: int ret = pk_parse_keyfile(mPk, mKeyFile->text().toLocal8Bit().constData(), ""); aheinecke@454: aheinecke@454: if (ret != 0) { aheinecke@454: showErrorMessage(tr("Failed to load certificate: %1") aheinecke@454: .arg(getPolarSSLErrorMsg(ret))); aheinecke@465: pk_free(mPk); aheinecke@465: delete mPk; aheinecke@465: mPk = NULL; aheinecke@454: return; aheinecke@454: } aheinecke@465: andre@1390: /* Check that it is a RSA key of the specified size */ andre@1390: if (!mPk->pk_info || pk_get_size(mPk) != TRUSTBRIDGE_RSA_KEY_SIZE || aheinecke@465: mPk->pk_info->type != POLARSSL_PK_RSA) { andre@1390: showErrorMessage(tr("Only %1 bit RSA keys are supported by the current format.").arg( andre@1390: TRUSTBRIDGE_RSA_KEY_SIZE)); aheinecke@465: pk_free(mPk); aheinecke@465: delete mPk; aheinecke@465: mPk = NULL; aheinecke@465: return; aheinecke@465: } aheinecke@465: } aheinecke@465: aheinecke@465: void CreateCertListDialog::openCertificateSelect() aheinecke@465: { aheinecke@465: QString keyFile = QFileDialog::getOpenFileName( aheinecke@465: this, tr("Select certificate"), mKeyFile->text().isEmpty() ? aheinecke@465: QDir::homePath() : mKeyFile->text(), "*.pem"); aheinecke@465: mKeyFile->setText(keyFile); aheinecke@465: aheinecke@465: mAdminWindow->settings()->setValue("LastKey", keyFile); aheinecke@465: loadKeyFile(keyFile); aheinecke@465: aheinecke@465: return; rrenkert@367: } rrenkert@367: rrenkert@367: void CreateCertListDialog::openSaveLocation() rrenkert@367: { aheinecke@465: QString saveDir = QFileDialog::getExistingDirectory( aheinecke@465: this, tr("Select target location"), aheinecke@465: mSaveDir->text().isEmpty() ? QDir::homePath() : mSaveDir->text()); aheinecke@465: mAdminWindow->settings()->setValue("LastOutputDir", saveDir); aheinecke@465: mSaveDir->setText(saveDir); aheinecke@465: } aheinecke@465: aheinecke@465: CreateCertListDialog::~CreateCertListDialog() aheinecke@465: { aheinecke@465: if (mPk) { aheinecke@465: pk_free(mPk); aheinecke@465: delete mPk; aheinecke@465: mPk = NULL; aheinecke@465: } rrenkert@367: } rrenkert@367: aheinecke@466: bool CreateCertListDialog::writeList(const QList& certs, aheinecke@466: const QString& filePath, aheinecke@466: const QDateTime& listDate, aheinecke@466: pk_context *pk) aheinecke@466: { aheinecke@466: /* Build up the list data */ aheinecke@473: QByteArray listData("F:1\r\nD:"); aheinecke@466: listData.append(listDate.toString(Qt::ISODate) + "\r\n"); aheinecke@466: aheinecke@466: foreach (const Certificate& cert, certs) { aheinecke@473: listData.append(cert.base64Line() + "\r\n"); aheinecke@466: } aheinecke@466: aheinecke@466: QByteArray signature = rsaSignSHA256Hash(sha256sum(listData), pk); andre@1390: if (signature.size() != TRUSTBRIDGE_RSA_KEY_SIZE / 8) { aheinecke@466: qDebug() << "Signature creation returned signature of invalid size."; aheinecke@466: return false; aheinecke@466: } aheinecke@466: listData.prepend("\r\n"); aheinecke@466: listData.prepend(signature.toBase64()); aheinecke@466: listData.prepend("S:"); aheinecke@466: aheinecke@466: QFile outputFile(filePath); aheinecke@466: aheinecke@466: if (!outputFile.open(QIODevice::WriteOnly)) { aheinecke@466: qDebug() << "Failed to open output file: " << filePath; aheinecke@466: return false; aheinecke@466: } aheinecke@466: aheinecke@466: if (outputFile.write(listData) != listData.size()) { aheinecke@466: qDebug() << "Failed to write list: " << filePath; aheinecke@466: outputFile.close(); aheinecke@466: return false; aheinecke@466: } aheinecke@466: outputFile.close(); aheinecke@466: return true; aheinecke@466: } aheinecke@466: rrenkert@367: void CreateCertListDialog::createList() rrenkert@367: { aheinecke@465: if (!mPk) { aheinecke@465: showErrorMessage(tr("Please select a valid rsa key.")); aheinecke@465: } aheinecke@465: if (mSaveDir->text().isEmpty()) { aheinecke@465: showErrorMessage(tr("Please select an output location first.")); aheinecke@465: } aheinecke@454: aheinecke@465: QDateTime currentDateTimeUtc = QDateTime::currentDateTimeUtc(); aheinecke@465: aheinecke@465: QString fileName = QString::fromLatin1("certificates-") aheinecke@465: .append(currentDateTimeUtc.toString(("yyyyMMddHHmmss"))) aheinecke@465: .append(".txt"); aheinecke@465: aheinecke@465: QString filePath = mSaveDir->text().append("/").append(fileName); aheinecke@465: aheinecke@466: if (!writeList(mAdminWindow->certificates(), filePath, aheinecke@466: currentDateTimeUtc, mPk)) { aheinecke@466: showErrorMessage(tr("Failed to write list to: %1").arg(filePath)); aheinecke@465: } aheinecke@465: aheinecke@466: QFile outputFile(filePath); aheinecke@465: aheinecke@465: /* Archive the list */ aheinecke@465: QDir archiveDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); aheinecke@465: if (!archiveDir.mkpath(archiveDir.path())) { aheinecke@465: showErrorMessage(tr("Failed to create archive location.")); aheinecke@465: return; aheinecke@465: } aheinecke@465: aheinecke@465: if (!outputFile.copy(archiveDir.filePath(fileName))) { aheinecke@465: showErrorMessage(tr("Failed Archive a copy.")); aheinecke@465: return; aheinecke@465: } aheinecke@465: aheinecke@515: QString curCerts = archiveDir.filePath("current_certificates.txt"); aheinecke@515: aheinecke@515: if (QFile::exists(curCerts)) { aheinecke@515: if (!QFile::remove(curCerts)) { aheinecke@515: showErrorMessage(tr("Failed to update current_certificates.txt")); aheinecke@515: return; aheinecke@515: } aheinecke@515: } aheinecke@515: aheinecke@515: if (!outputFile.copy(curCerts)) { aheinecke@465: showErrorMessage(tr("Failed to write current_certificates file.")); aheinecke@465: return; aheinecke@465: } aheinecke@465: andre@679: andre@679: QString keyFingerprint; andre@679: andre@679: { andre@679: /* Calculate sha256 sum of the der key */ andre@679: unsigned char output_buf[16000]; /* Buf size taken from examples */ andre@679: int ret; andre@679: ret = pk_write_key_der (mPk, output_buf, 16000); andre@679: if (ret <= 0) { andre@679: showErrorMessage(tr("Failed to calculate key hash.")); andre@679: return; andre@679: } andre@679: QByteArray derKey((const char*)output_buf, ret); andre@679: QByteArray fp = sha256sum(derKey); andre@679: andre@679: for (int i=0; i < fp.size(); i++) { andre@679: keyFingerprint += QString("%1").arg( andre@679: (unsigned char)(fp[i]), 0, 16).rightJustified(2, '0'); andre@679: if (i != fp.size() - 1) { andre@679: keyFingerprint += ":"; andre@679: } andre@679: } andre@679: keyFingerprint = keyFingerprint.toUpper(); andre@679: } andre@679: andre@679: mAdminWindow->logChanges(curCerts, keyFingerprint); andre@679: aheinecke@465: QMessageBox::information(this, "", tr("Saved certificate list:\n%1").arg(fileName)); andre@1317: emit creationSuccessful(); andre@1317: aheinecke@465: close(); rrenkert@367: }