From 768e9102729491781f0315f0dc3b84a0fc1bac13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=A4ckerlin?= Date: Thu, 8 Oct 2015 14:38:57 +0000 Subject: [PATCH] first version to show an save certificates from files or urls --- COPYING | 1 + ChangeLog | 4 ++ INSTALL | 1 + src/certificate.hxx | 133 ++++++++++++++++++++++++++++++++++++++++++++ src/certificate.ui | 59 ++++++++++++++++++++ src/certman.hxx | 79 +++++++++++++++++++++++++- src/certman.ui | 109 +++++++++++++++++++++++++++++++++++- src/certman_de.ts | 4 ++ src/certman_en.ts | 4 ++ src/certman_fr.ts | 4 ++ src/certman_it.ts | 4 ++ src/makefile.am | 4 +- src/openfromurl.hxx | 73 ++++++++++++++++++++++++ src/openfromurl.ui | 101 +++++++++++++++++++++++++++++++++ 14 files changed, 574 insertions(+), 6 deletions(-) create mode 120000 COPYING create mode 100644 ChangeLog create mode 120000 INSTALL create mode 100644 src/certificate.hxx create mode 100644 src/certificate.ui create mode 100644 src/certman_de.ts create mode 100644 src/certman_en.ts create mode 100644 src/certman_fr.ts create mode 100644 src/certman_it.ts create mode 100644 src/openfromurl.hxx create mode 100644 src/openfromurl.ui diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..caeca07 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +/usr/share/automake-1.14/COPYING \ No newline at end of file diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..7830462 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4 @@ +2015-10-08 08:50 marc + + * .: structure + diff --git a/INSTALL b/INSTALL new file mode 120000 index 0000000..f812f5a --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +/usr/share/automake-1.14/INSTALL \ No newline at end of file diff --git a/src/certificate.hxx b/src/certificate.hxx new file mode 100644 index 0000000..467e967 --- /dev/null +++ b/src/certificate.hxx @@ -0,0 +1,133 @@ +/*! @file + + @id $Id$ +*/ +// 1 2 3 4 5 6 7 8 +// 45678901234567890123456789012345678901234567890123456789012345678901234567890 + +#ifndef CERTIFICATE_HXX +#define CERTIFICATE_HXX + +#include +#include +#include +#include +#include +#include + +#include + +#if QT_VERSION >= 0x050000 +namespace QSsl { + typedef AlternativeNameEntryType AlternateNameEntryType; +} +#endif + +class Certificate: public QWidget, protected Ui::Certificate { + Q_OBJECT; + public: + Certificate(QWidget * parent = 0): QWidget(parent) { + setupUi(this); + } + Certificate& certificate(const QSslCertificate& cert) { + _certificate = cert; + _cert->clear(); +#if QT_VERSION < 0x050000 + if (!cert.isValid()) return *this; +#endif + _cert->addTopLevelItem + ((new QTreeWidgetItem + (QStringList()<addTopLevelItem + ((new QTreeWidgetItem + (QStringList()<addTopLevelItem + ((new QTreeWidgetItem + (QStringList()<addTopLevelItem + ((new QTreeWidgetItem + (QStringList()<addTopLevelItem + // ((new QTreeWidgetItem + // (QStringList()<addTopLevelItem + ((it = new QTreeWidgetItem(QStringList()<=QSslCertificate::Organization; + si=(QSslCertificate::SubjectInfo)((int)si-1)) + if (!cert.subjectInfo(si).isEmpty()) + it->addChild + ((new QTreeWidgetItem + (QStringList()<addChild + ((it2 = new QTreeWidgetItem + (QStringList()< +#if QT_VERSION <0x050000 + asns(cert.alternateSubjectNames()) +#else + asns(cert.subjectAlternativeNames()) +#endif + ; + for (QMultiMap::iterator + asn(asns.begin()); asn!=asns.end(); ++asn) + it2->addChild + ((new QTreeWidgetItem + (QStringList()<addTopLevelItem + ((it = new QTreeWidgetItem(QStringList()<=QSslCertificate::Organization; + si=(QSslCertificate::SubjectInfo)((int)si-1)) + if (!cert.issuerInfo(si).isEmpty()) + it->addChild + ((new QTreeWidgetItem + (QStringList()<expandAll(); + _cert->resizeColumnToContents(0); + _cert->resizeColumnToContents(1); + return *this; + } + QSslCertificate certificate() { + return _certificate; + } + QString alternateName(QSsl::AlternateNameEntryType an) { + switch (an) { + case QSsl::EmailEntry: return tr("E-Mail"); + case QSsl::DnsEntry: return tr("URL"); + } + return tr("error", "unknown certificate subject alternate name"); + } + QString subjectInfo(QSslCertificate::SubjectInfo si) { + switch (si) { + case QSslCertificate::Organization: + return tr("Organization"); + case QSslCertificate::CommonName: + return tr("Common Name"); + case QSslCertificate::LocalityName: + return tr("Locality"); + case QSslCertificate::OrganizationalUnitName: + return tr("Organizational Unit"); + case QSslCertificate::CountryName: + return tr("Country"); + case QSslCertificate::StateOrProvinceName: + return tr("State or Province"); + } + return tr("error", "unknown certificate subject info"); + } + protected: + QSslCertificate _certificate; +}; + +#endif diff --git a/src/certificate.ui b/src/certificate.ui new file mode 100644 index 0000000..9b0a434 --- /dev/null +++ b/src/certificate.ui @@ -0,0 +1,59 @@ + + + Certificate + + + + 0 + 0 + 400 + 320 + + + + Form + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + true + + + false + + + true + + + false + + + true + + + false + + + + Attribute + + + + + Value + + + + + + + + + diff --git a/src/certman.hxx b/src/certman.hxx index 29e78bc..e4306c6 100644 --- a/src/certman.hxx +++ b/src/certman.hxx @@ -11,18 +11,93 @@ #ifndef CERTMAN_HXX #define CERTMAN_HXX -#include +#include +#include #include +#include +#include +#include +#include + +#include /// Main Window /** Main window for certman */ class CertMan: public QMainWindow, protected Ui::CertMan { Q_OBJECT; public: - explicit CertMan(QWidget *parent = 0): QMainWindow(parent) { + explicit CertMan(QWidget *parent = 0): + QMainWindow(parent), + _openFromURL(this) { setupUi(this); + connect(&_openFromURL, SIGNAL(certificate(QUrl, QSslCertificate)), + SLOT(certificate(QUrl, QSslCertificate))); } virtual ~CertMan() {} + protected slots: + void on__actionOpenCertificate_triggered() { + QString fileName = QFileDialog::getOpenFileName(this); + if (fileName.isEmpty()) return; + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QSslCertificate c(&file); + file.close(); + if (c.isNull()) { + qDebug()<addSubWindow(win)->setWindowTitle(QFileInfo(fileName).fileName()); + win->show(); + win->certificate(c); + } + void on__actionOpenFromURL_triggered() { + if (_openFromURL.exec()==QDialog::Accepted) { + _openFromURL.read(); + } + } + void certificate(QUrl url, QSslCertificate cert) { + qDebug()<<"got certificate: "<addSubWindow(win)->setWindowTitle + (url.host()+" - "+ + cert.subjectInfo(QSslCertificate::CommonName).join("/")); + win->show(); + win->certificate(cert); + } + void on__actionSaveAs_triggered() { + QMdiSubWindow* win(_mdi->currentSubWindow()); + if (!win) return; + Certificate* cert(dynamic_cast(win->widget())); + QString fileName(QFileDialog::getSaveFileName + (this, + win->windowTitle(), + win->windowTitle() + .replace(" ", "_").replace(QRegExp("\.(pem|der)$"), "") + +".pem", + tr("Certificates (*.der *.pem *.txt)"))); + if (fileName.isEmpty()) return; + QFile file(fileName); + file.open(QIODevice::WriteOnly); + QFileInfo fileInfo(fileName); + if (fileInfo.suffix()=="der") { + QDataStream out(&file); + out<certificate().toDer(); + } else if (fileInfo.suffix()=="pem") { + QDataStream out(&file); + out<certificate().toPem(); + } else if (fileInfo.suffix()=="txt") { + QTextStream out(&file); + out<certificate().toText(); + } else { // defaults to pem + QDataStream out(&file); + out<certificate().toPem(); + } + file.close(); + } + private: + OpenFromURL _openFromURL; }; #endif diff --git a/src/certman.ui b/src/certman.ui index 9fc439c..23dc00b 100644 --- a/src/certman.ui +++ b/src/certman.ui @@ -13,7 +13,13 @@ CertMan - + + + + + + + @@ -23,9 +29,108 @@ 22 + + + File + + + + + + + + + + + Windows + + + + + + + + + Open Certificate ... + + + + + Exit + + + + + Cascade + + + + + Tile + + + + + Open From URL ... + + + + + Save As ... + + - + + + _actionExit + triggered() + CertMan + close() + + + -1 + -1 + + + 399 + 299 + + + + + _actionTile + triggered() + _mdi + tileSubWindows() + + + -1 + -1 + + + 399 + 299 + + + + + _actionCascade + triggered() + _mdi + cascadeSubWindows() + + + -1 + -1 + + + 399 + 299 + + + + diff --git a/src/certman_de.ts b/src/certman_de.ts new file mode 100644 index 0000000..a5289b1 --- /dev/null +++ b/src/certman_de.ts @@ -0,0 +1,4 @@ + + + + diff --git a/src/certman_en.ts b/src/certman_en.ts new file mode 100644 index 0000000..5315a75 --- /dev/null +++ b/src/certman_en.ts @@ -0,0 +1,4 @@ + + + + diff --git a/src/certman_fr.ts b/src/certman_fr.ts new file mode 100644 index 0000000..d72cb03 --- /dev/null +++ b/src/certman_fr.ts @@ -0,0 +1,4 @@ + + + + diff --git a/src/certman_it.ts b/src/certman_it.ts new file mode 100644 index 0000000..b2e3584 --- /dev/null +++ b/src/certman_it.ts @@ -0,0 +1,4 @@ + + + + diff --git a/src/makefile.am b/src/makefile.am index b16405d..c9f5b53 100644 --- a/src/makefile.am +++ b/src/makefile.am @@ -33,10 +33,10 @@ QT_PLUGINS = iconengines imageformats platforms noinst_HEADERS = version.hxx ## list all %.hxx files with Q_OBJECT as moc_%.cxx -certman_MOCFILES = moc_certman.cxx +certman_MOCFILES = moc_certman.cxx moc_certificate.cxx moc_openfromurl.cxx ## list all %.ui files as ui_%.hxx -certman_UIFILES = ui_certman.hxx +certman_UIFILES = ui_certman.hxx ui_certificate.hxx ui_openfromurl.hxx ## list all %.qrc resource files as qrc_%.cxx ## note: if there exists a directory %, the file %.qrc is generated from that diff --git a/src/openfromurl.hxx b/src/openfromurl.hxx new file mode 100644 index 0000000..b761561 --- /dev/null +++ b/src/openfromurl.hxx @@ -0,0 +1,73 @@ +/*! @file + + @id $Id$ +*/ +// 1 2 3 4 5 6 7 8 +// 45678901234567890123456789012345678901234567890123456789012345678901234567890 + +#ifndef OPENFROMURL_HXX +#define OPENFROMURL_HXX + +#include +#include +#include +#include + +class OpenFromURL: public QDialog, public Ui::OpenFromURL { + Q_OBJECT; + public: + OpenFromURL(QWidget* p): QDialog(p) { + setupUi(this); + } + QString url() { + return _url->currentText(); + } + void read() { + QNetworkReply* reply + (_net.head(QNetworkRequest(QUrl("https://"+_url->currentText())))); + connect(reply, SIGNAL(finished()), SLOT(finished())); + connect(reply, SIGNAL(encrypted()), SLOT(encrypted())); + connect(reply, SIGNAL(sslErrors(const QList&)), + SLOT(sslErrors(const QList&))); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + SLOT(error(QNetworkReply::NetworkError))); + } + signals: + void certificate(QUrl, QSslCertificate); + protected slots: + void on__buttons_accepted() { + _url->addItem(_url->currentText()); + } + void finished() { + qDebug()<<"finished"; + QNetworkReply* reply(dynamic_cast(sender())); + QList certs(reply->sslConfiguration() + .peerCertificateChain()); + qDebug()<<"number of peer certificates: "<url(), cert); + } + } else { + QSslCertificate cert(reply->sslConfiguration().peerCertificate()); + if (!cert.isNull()) { + certificate(reply->url(), cert); + } else { + qDebug()<<"no peer certificate found"; + } + } + } + void encrypted() { + qDebug()<<"encrypted"; + } + void sslErrors(const QList& errors) { + qDebug()<<"sslErrors"; + } + void error(QNetworkReply::NetworkError code) { + qDebug()<<"error"; + } + private: + QNetworkAccessManager _net; +}; + +#endif diff --git a/src/openfromurl.ui b/src/openfromurl.ui new file mode 100644 index 0000000..5ac1c5e --- /dev/null +++ b/src/openfromurl.ui @@ -0,0 +1,101 @@ + + + OpenFromURL + + + + 0 + 0 + 434 + 89 + + + + Open Certificate From URL + + + + + + + + Hostname: + + + + + + + + 0 + 0 + + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 4 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + _buttons + accepted() + OpenFromURL + accept() + + + 248 + 254 + + + 157 + 274 + + + + + _buttons + rejected() + OpenFromURL + reject() + + + 316 + 260 + + + 286 + 274 + + + + +