mirror of
https://github.com/QuasarApp/qca.git
synced 2025-04-26 11:34:32 +00:00
find . \( -name "*.cpp" -or -name "*.h" -or -name "*.c" -or -name "*.cc" \) -exec clang-format -i {} \; If you reached this file doing a git blame, please see README.clang-format (added 2 commits in the future of this one)
952 lines
29 KiB
C++
952 lines
29 KiB
C++
/*
|
|
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
#include <QtCore>
|
|
#include <QtCrypto>
|
|
#include <QtGui>
|
|
|
|
#include "certitem.h"
|
|
#include "certviewdlg.h"
|
|
#include "keyselectdlg.h"
|
|
#include "pkcs11configdlg/pkcs11configdlg.h"
|
|
#include "ui_mainwin.h"
|
|
|
|
#ifdef QT_STATICPLUGIN
|
|
#include "import_plugins.h"
|
|
#endif
|
|
|
|
#define VERSION "1.0.0"
|
|
|
|
class Icons
|
|
{
|
|
public:
|
|
QPixmap cert, crl, keybundle, pgppub, pgpsec;
|
|
};
|
|
|
|
Icons *g_icons = 0;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Operation
|
|
//----------------------------------------------------------------------------
|
|
class Operation : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
Operation(QObject *parent = 0)
|
|
: QObject(parent)
|
|
{
|
|
}
|
|
|
|
Q_SIGNALS:
|
|
void error(const QString &str);
|
|
};
|
|
|
|
static QString validityToString(QCA::Validity v)
|
|
{
|
|
QString s;
|
|
switch (v) {
|
|
case QCA::ValidityGood:
|
|
s = Operation::tr("Validated");
|
|
break;
|
|
case QCA::ErrorRejected:
|
|
s = Operation::tr("Root CA is marked to reject the specified purpose");
|
|
break;
|
|
case QCA::ErrorUntrusted:
|
|
s = Operation::tr("Certificate not trusted for the required purpose");
|
|
break;
|
|
case QCA::ErrorSignatureFailed:
|
|
s = Operation::tr("Invalid signature");
|
|
break;
|
|
case QCA::ErrorInvalidCA:
|
|
s = Operation::tr("Invalid CA certificate");
|
|
break;
|
|
case QCA::ErrorInvalidPurpose:
|
|
s = Operation::tr("Invalid certificate purpose");
|
|
break;
|
|
case QCA::ErrorSelfSigned:
|
|
s = Operation::tr("Certificate is self-signed");
|
|
break;
|
|
case QCA::ErrorRevoked:
|
|
s = Operation::tr("Certificate has been revoked");
|
|
break;
|
|
case QCA::ErrorPathLengthExceeded:
|
|
s = Operation::tr("Maximum certificate chain length exceeded");
|
|
break;
|
|
case QCA::ErrorExpired:
|
|
s = Operation::tr("Certificate has expired");
|
|
break;
|
|
case QCA::ErrorExpiredCA:
|
|
s = Operation::tr("CA has expired");
|
|
break;
|
|
case QCA::ErrorValidityUnknown:
|
|
default:
|
|
s = Operation::tr("General certificate validation error");
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static QString smErrorToString(QCA::SecureMessage::Error e)
|
|
{
|
|
QString s;
|
|
switch (e) {
|
|
case QCA::SecureMessage::ErrorPassphrase:
|
|
s = Operation::tr("Invalid passphrase.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorFormat:
|
|
s = Operation::tr("Bad input format.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorSignerExpired:
|
|
s = Operation::tr("Signer key is expired.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorSignerInvalid:
|
|
s = Operation::tr("Signer key is invalid.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorEncryptExpired:
|
|
s = Operation::tr("Encrypting key is expired.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorEncryptUntrusted:
|
|
s = Operation::tr("Encrypting key is untrusted.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorEncryptInvalid:
|
|
s = Operation::tr("Encrypting key is invalid.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorNeedCard:
|
|
s = Operation::tr("Card was needed but not found.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorCertKeyMismatch:
|
|
s = Operation::tr("Certificate and private key don't match.");
|
|
break;
|
|
case QCA::SecureMessage::ErrorUnknown:
|
|
default:
|
|
s = Operation::tr("General error.");
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static QString smsIdentityToString(const QCA::SecureMessageSignature &sig)
|
|
{
|
|
QString s;
|
|
switch (sig.identityResult()) {
|
|
case QCA::SecureMessageSignature::Valid:
|
|
break;
|
|
case QCA::SecureMessageSignature::InvalidSignature:
|
|
s = Operation::tr("Invalid signature");
|
|
break;
|
|
case QCA::SecureMessageSignature::InvalidKey:
|
|
s = Operation::tr("Invalid key: %1").arg(validityToString(sig.keyValidity()));
|
|
break;
|
|
case QCA::SecureMessageSignature::NoKey:
|
|
s = Operation::tr("Key not found");
|
|
break;
|
|
default: // this should not really be possible
|
|
s = Operation::tr("Unknown");
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
class SignOperation : public Operation
|
|
{
|
|
Q_OBJECT
|
|
private:
|
|
QByteArray in;
|
|
CertItemStore * store;
|
|
int id;
|
|
QCA::CMS * cms;
|
|
CertItemPrivateLoader *loader;
|
|
QCA::SecureMessage * msg;
|
|
int pending;
|
|
|
|
public:
|
|
SignOperation(const QByteArray &_in, CertItemStore *_store, int _id, QCA::CMS *_cms, QObject *parent = 0)
|
|
: Operation(parent)
|
|
, in(_in)
|
|
, store(_store)
|
|
, id(_id)
|
|
, cms(_cms)
|
|
, msg(0)
|
|
{
|
|
loader = new CertItemPrivateLoader(store, this);
|
|
connect(loader, SIGNAL(finished()), SLOT(loader_finished()));
|
|
loader->start(id);
|
|
}
|
|
|
|
Q_SIGNALS:
|
|
void loadError();
|
|
void finished(const QString &sig);
|
|
|
|
private Q_SLOTS:
|
|
void loader_finished()
|
|
{
|
|
QCA::PrivateKey privateKey = loader->privateKey();
|
|
delete loader;
|
|
loader = 0;
|
|
|
|
if (privateKey.isNull()) {
|
|
emit loadError();
|
|
return;
|
|
}
|
|
|
|
CertItem item = store->itemFromId(id);
|
|
|
|
QCA::SecureMessageKey signer;
|
|
signer.setX509CertificateChain(item.certificateChain());
|
|
signer.setX509PrivateKey(privateKey);
|
|
|
|
msg = new QCA::SecureMessage(cms);
|
|
connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
|
|
connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
|
|
msg->setFormat(QCA::SecureMessage::Ascii);
|
|
msg->setSigner(signer);
|
|
msg->startSign(QCA::SecureMessage::Detached);
|
|
|
|
pending = 0;
|
|
update();
|
|
}
|
|
|
|
void update()
|
|
{
|
|
QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
|
|
in = in.mid(buf.size());
|
|
pending += buf.size();
|
|
msg->update(buf);
|
|
}
|
|
|
|
void msg_bytesWritten(int x)
|
|
{
|
|
pending -= x;
|
|
|
|
if (in.isEmpty() && pending == 0)
|
|
msg->end();
|
|
else
|
|
update();
|
|
}
|
|
|
|
void msg_finished()
|
|
{
|
|
if (!msg->success()) {
|
|
QString str = smErrorToString(msg->errorCode());
|
|
delete msg;
|
|
msg = 0;
|
|
emit error(tr("Error during sign operation.\nReason: %1").arg(str));
|
|
return;
|
|
}
|
|
|
|
QByteArray result = msg->signature();
|
|
delete msg;
|
|
msg = 0;
|
|
emit finished(QString::fromLatin1(result));
|
|
}
|
|
};
|
|
|
|
class VerifyOperation : public Operation
|
|
{
|
|
Q_OBJECT
|
|
private:
|
|
QByteArray in, sig;
|
|
QCA::CMS * cms;
|
|
QCA::SecureMessage *msg;
|
|
int pending;
|
|
|
|
public:
|
|
QCA::SecureMessageSignature signer;
|
|
|
|
VerifyOperation(const QByteArray &_in, const QByteArray &_sig, QCA::CMS *_cms, QObject *parent = 0)
|
|
: Operation(parent)
|
|
, in(_in)
|
|
, sig(_sig)
|
|
, cms(_cms)
|
|
, msg(0)
|
|
{
|
|
msg = new QCA::SecureMessage(cms);
|
|
connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
|
|
connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
|
|
msg->setFormat(QCA::SecureMessage::Ascii);
|
|
msg->startVerify(sig);
|
|
|
|
pending = 0;
|
|
update();
|
|
}
|
|
|
|
Q_SIGNALS:
|
|
void finished();
|
|
|
|
private Q_SLOTS:
|
|
void update()
|
|
{
|
|
QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
|
|
in = in.mid(buf.size());
|
|
pending += buf.size();
|
|
msg->update(buf);
|
|
}
|
|
|
|
void msg_bytesWritten(int x)
|
|
{
|
|
pending -= x;
|
|
|
|
if (in.isEmpty() && pending == 0)
|
|
msg->end();
|
|
else
|
|
update();
|
|
}
|
|
|
|
void msg_finished()
|
|
{
|
|
if (!msg->success()) {
|
|
QString str = smErrorToString(msg->errorCode());
|
|
delete msg;
|
|
msg = 0;
|
|
emit error(tr("Error during verify operation.\nReason: %1").arg(str));
|
|
return;
|
|
}
|
|
|
|
signer = msg->signer();
|
|
delete msg;
|
|
msg = 0;
|
|
|
|
if (signer.identityResult() != QCA::SecureMessageSignature::Valid) {
|
|
QString str = smsIdentityToString(signer);
|
|
emit error(tr("Verification failed!\nReason: %1").arg(str));
|
|
return;
|
|
}
|
|
|
|
emit finished();
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// MainWin
|
|
//----------------------------------------------------------------------------
|
|
static QString get_fingerprint(const QCA::Certificate &cert)
|
|
{
|
|
QString hex = QCA::Hash("sha1").hashToString(cert.toDER());
|
|
QString out;
|
|
for (int n = 0; n < hex.count(); ++n) {
|
|
if (n != 0 && n % 2 == 0)
|
|
out += ':';
|
|
out += hex[n];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
class MainWin : public QMainWindow
|
|
{
|
|
Q_OBJECT
|
|
private:
|
|
Ui_MainWin ui;
|
|
CertItemStore * users, *roots;
|
|
QCA::CMS * cms;
|
|
Operation * op;
|
|
QAction * actionView, *actionRename, *actionRemove;
|
|
QCA::Certificate self_signed_verify_cert;
|
|
int auto_import_req_id;
|
|
|
|
public:
|
|
MainWin(QWidget *parent = 0)
|
|
: QMainWindow(parent)
|
|
, op(0)
|
|
, auto_import_req_id(-1)
|
|
{
|
|
ui.setupUi(this);
|
|
|
|
g_icons = new Icons;
|
|
g_icons->cert = QPixmap(":/gfx/icons/cert16.png");
|
|
g_icons->crl = QPixmap(":/gfx/icons/crl16.png");
|
|
g_icons->keybundle = QPixmap(":/gfx/icons/keybundle16.png");
|
|
g_icons->pgppub = QPixmap(":/gfx/icons/publickey16.png");
|
|
g_icons->pgpsec = QPixmap(":/gfx/icons/keypair16.png");
|
|
if (g_icons->cert.isNull() || g_icons->crl.isNull() || g_icons->keybundle.isNull() ||
|
|
g_icons->pgppub.isNull() || g_icons->pgpsec.isNull())
|
|
printf("Warning: not all icons loaded\n");
|
|
|
|
users = new CertItemStore(this);
|
|
roots = new CertItemStore(this);
|
|
|
|
setIcons(users);
|
|
setIcons(roots);
|
|
|
|
connect(users, SIGNAL(addSuccess(int, int)), SLOT(users_addSuccess(int, int)));
|
|
connect(users, SIGNAL(addFailed(int)), SLOT(users_addFailed(int)));
|
|
|
|
actionView = new QAction(tr("&View"), this);
|
|
actionRename = new QAction(tr("Re&name"), this);
|
|
actionRemove = new QAction(tr("Rem&ove"), this);
|
|
|
|
connect(ui.actionLoadIdentityFile, SIGNAL(triggered()), SLOT(load_file()));
|
|
connect(ui.actionLoadIdentityEntry, SIGNAL(triggered()), SLOT(load_device()));
|
|
connect(ui.actionLoadAuthority, SIGNAL(triggered()), SLOT(load_root()));
|
|
connect(ui.actionConfigurePkcs11, SIGNAL(triggered()), SLOT(mod_config()));
|
|
connect(ui.actionQuit, SIGNAL(triggered()), SLOT(close()));
|
|
connect(ui.actionAbout, SIGNAL(triggered()), SLOT(about()));
|
|
connect(ui.pb_sign, SIGNAL(clicked()), SLOT(do_sign()));
|
|
connect(ui.pb_verify, SIGNAL(clicked()), SLOT(do_verify()));
|
|
|
|
connect(actionView, SIGNAL(triggered()), SLOT(item_view()));
|
|
connect(actionRename, SIGNAL(triggered()), SLOT(item_rename()));
|
|
connect(actionRemove, SIGNAL(triggered()), SLOT(item_remove()));
|
|
|
|
ui.pb_sign->setEnabled(false);
|
|
|
|
ui.lv_users->setModel(users);
|
|
connect(ui.lv_users->selectionModel(),
|
|
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
|
SLOT(users_selectionChanged(const QItemSelection &, const QItemSelection &)));
|
|
|
|
ui.lv_users->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui.lv_users,
|
|
SIGNAL(customContextMenuRequested(const QPoint &)),
|
|
SLOT(users_customContextMenuRequested(const QPoint &)));
|
|
|
|
ui.lv_authorities->setModel(roots);
|
|
|
|
ui.lv_authorities->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui.lv_authorities,
|
|
SIGNAL(customContextMenuRequested(const QPoint &)),
|
|
SLOT(roots_customContextMenuRequested(const QPoint &)));
|
|
|
|
cms = new QCA::CMS(this);
|
|
|
|
QStringList ulist, rlist;
|
|
{
|
|
QSettings settings("Affinix", "CMS Signer");
|
|
ulist = settings.value("users").toStringList();
|
|
rlist = settings.value("roots").toStringList();
|
|
}
|
|
|
|
users->load(ulist);
|
|
roots->load(rlist);
|
|
}
|
|
|
|
~MainWin()
|
|
{
|
|
QStringList ulist = users->save();
|
|
QStringList rlist = roots->save();
|
|
|
|
QSettings settings("Affinix", "CMS Signer");
|
|
settings.setValue("users", ulist);
|
|
settings.setValue("roots", rlist);
|
|
|
|
delete g_icons;
|
|
g_icons = 0;
|
|
}
|
|
|
|
void setIcons(CertItemStore *store)
|
|
{
|
|
store->setIcon(CertItemStore::IconCert, g_icons->cert);
|
|
store->setIcon(CertItemStore::IconCrl, g_icons->crl);
|
|
store->setIcon(CertItemStore::IconKeyBundle, g_icons->keybundle);
|
|
store->setIcon(CertItemStore::IconPgpPub, g_icons->pgppub);
|
|
store->setIcon(CertItemStore::IconPgpSec, g_icons->pgpsec);
|
|
}
|
|
|
|
QCA::CertificateCollection allCerts()
|
|
{
|
|
QCA::CertificateCollection col;
|
|
|
|
// system store
|
|
col += QCA::systemStore();
|
|
|
|
// additional roots configured in application
|
|
foreach (const CertItem &i, roots->items())
|
|
col.addCertificate(i.certificateChain().primary());
|
|
|
|
// user chains
|
|
foreach (const CertItem &i, users->items()) {
|
|
foreach (const QCA::Certificate &cert, i.certificateChain())
|
|
col.addCertificate(cert);
|
|
}
|
|
|
|
return col;
|
|
}
|
|
|
|
QCA::CertificateChain complete(const QCA::CertificateChain &chain)
|
|
{
|
|
return chain.complete(allCerts().certificates());
|
|
}
|
|
|
|
private Q_SLOTS:
|
|
void load_file()
|
|
{
|
|
QString fileName =
|
|
QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Identities (*.p12 *.pfx)"));
|
|
if (fileName.isEmpty())
|
|
return;
|
|
|
|
setEnabled(false);
|
|
users->addFromFile(fileName);
|
|
}
|
|
|
|
void load_device()
|
|
{
|
|
KeySelectDlg *w = new KeySelectDlg(this);
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->setWindowModality(Qt::WindowModal);
|
|
connect(
|
|
w, SIGNAL(selected(const QCA::KeyStoreEntry &)), SLOT(load_device_finished(const QCA::KeyStoreEntry &)));
|
|
connect(w,
|
|
SIGNAL(viewCertificate(const QCA::CertificateChain &)),
|
|
SLOT(keyselect_viewCertificate(const QCA::CertificateChain &)));
|
|
w->setIcon(KeySelectDlg::IconCert, g_icons->cert);
|
|
w->setIcon(KeySelectDlg::IconCrl, g_icons->crl);
|
|
w->setIcon(KeySelectDlg::IconKeyBundle, g_icons->keybundle);
|
|
w->setIcon(KeySelectDlg::IconPgpPub, g_icons->pgppub);
|
|
w->setIcon(KeySelectDlg::IconPgpSec, g_icons->pgpsec);
|
|
w->show();
|
|
}
|
|
|
|
void load_device_finished(const QCA::KeyStoreEntry &entry)
|
|
{
|
|
users->addFromKeyStore(entry);
|
|
}
|
|
|
|
void load_root()
|
|
{
|
|
QString fileName =
|
|
QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Certificates (*.pem *.crt)"));
|
|
if (fileName.isEmpty())
|
|
return;
|
|
|
|
QCA::Certificate cert = QCA::Certificate::fromPEMFile(fileName);
|
|
if (cert.isNull()) {
|
|
QMessageBox::information(this, tr("Error"), tr("Error opening certificate file."));
|
|
return;
|
|
}
|
|
|
|
roots->addUser(cert);
|
|
}
|
|
|
|
void users_addSuccess(int req_id, int id)
|
|
{
|
|
if (req_id == auto_import_req_id) {
|
|
auto_import_req_id = -1;
|
|
|
|
CertItem i = users->itemFromId(id);
|
|
|
|
QMessageBox::information(this,
|
|
tr("User added"),
|
|
tr("This signature was made by a previously unknown user, and so the "
|
|
"user has now been added to the keyring as \"%1\".")
|
|
.arg(i.name()));
|
|
|
|
verify_next();
|
|
return;
|
|
}
|
|
|
|
ui.lv_users->selectionModel()->select(users->index(users->rowFromId(id)),
|
|
QItemSelectionModel::Clear | QItemSelectionModel::Select |
|
|
QItemSelectionModel::Current);
|
|
|
|
setEnabled(true);
|
|
}
|
|
|
|
void users_addFailed(int req_id)
|
|
{
|
|
Q_UNUSED(req_id);
|
|
|
|
setEnabled(true);
|
|
}
|
|
|
|
void mod_config()
|
|
{
|
|
if (!Pkcs11ConfigDlg::isSupported()) {
|
|
QMessageBox::information(
|
|
this, tr("Error"), tr("No provider available supporting standard PKCS#11 configuration."));
|
|
return;
|
|
}
|
|
|
|
Pkcs11ConfigDlg *w = new Pkcs11ConfigDlg(this);
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->setWindowModality(Qt::WindowModal);
|
|
w->show();
|
|
}
|
|
|
|
void users_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
|
{
|
|
Q_UNUSED(deselected);
|
|
|
|
int at = -1;
|
|
if (!selected.indexes().isEmpty()) {
|
|
QModelIndex index = selected.indexes().first();
|
|
at = index.row();
|
|
}
|
|
|
|
bool usable = false;
|
|
if (at != -1 && users->itemFromRow(at).isUsable())
|
|
usable = true;
|
|
|
|
if (usable && !ui.pb_sign->isEnabled())
|
|
ui.pb_sign->setEnabled(true);
|
|
else if (!usable && ui.pb_sign->isEnabled())
|
|
ui.pb_sign->setEnabled(false);
|
|
}
|
|
|
|
void item_view()
|
|
{
|
|
if (ui.lv_users->hasFocus()) {
|
|
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
QModelIndex index = selection.indexes().first();
|
|
users_view(index.row());
|
|
} else // lv_authorities
|
|
{
|
|
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
QModelIndex index = selection.indexes().first();
|
|
roots_view(index.row());
|
|
}
|
|
}
|
|
|
|
void item_rename()
|
|
{
|
|
if (ui.lv_users->hasFocus()) {
|
|
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
QModelIndex index = selection.indexes().first();
|
|
users_rename(index.row());
|
|
} else // lv_authorities
|
|
{
|
|
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
QModelIndex index = selection.indexes().first();
|
|
roots_rename(index.row());
|
|
}
|
|
}
|
|
|
|
void item_remove()
|
|
{
|
|
if (ui.lv_users->hasFocus()) {
|
|
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
QModelIndex index = selection.indexes().first();
|
|
users_remove(index.row());
|
|
} else // lv_authorities
|
|
{
|
|
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
QModelIndex index = selection.indexes().first();
|
|
roots_remove(index.row());
|
|
}
|
|
}
|
|
|
|
void users_view(int at)
|
|
{
|
|
CertItem i = users->itemFromRow(at);
|
|
CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->show();
|
|
}
|
|
|
|
void users_rename(int at)
|
|
{
|
|
QModelIndex index = users->index(at);
|
|
ui.lv_users->setFocus();
|
|
ui.lv_users->setCurrentIndex(index);
|
|
ui.lv_users->selectionModel()->select(
|
|
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
|
|
ui.lv_users->edit(index);
|
|
}
|
|
|
|
void users_remove(int at)
|
|
{
|
|
users->removeItem(users->idFromRow(at));
|
|
}
|
|
|
|
void roots_view(int at)
|
|
{
|
|
CertItem i = roots->itemFromRow(at);
|
|
CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->show();
|
|
}
|
|
|
|
void roots_rename(int at)
|
|
{
|
|
QModelIndex index = roots->index(at);
|
|
ui.lv_authorities->setFocus();
|
|
ui.lv_authorities->setCurrentIndex(index);
|
|
ui.lv_authorities->selectionModel()->select(
|
|
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
|
|
ui.lv_authorities->edit(index);
|
|
}
|
|
|
|
void roots_remove(int at)
|
|
{
|
|
roots->removeItem(roots->idFromRow(at));
|
|
}
|
|
|
|
void keyselect_viewCertificate(const QCA::CertificateChain &chain)
|
|
{
|
|
CertViewDlg *w = new CertViewDlg(complete(chain), (QWidget *)sender());
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->show();
|
|
}
|
|
|
|
void users_customContextMenuRequested(const QPoint &pos)
|
|
{
|
|
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
|
|
QMenu menu(this);
|
|
menu.addAction(actionView);
|
|
menu.addAction(actionRename);
|
|
menu.addAction(actionRemove);
|
|
menu.exec(ui.lv_users->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
void roots_customContextMenuRequested(const QPoint &pos)
|
|
{
|
|
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
|
|
QMenu menu(this);
|
|
menu.addAction(actionView);
|
|
menu.addAction(actionRename);
|
|
menu.addAction(actionRemove);
|
|
menu.exec(ui.lv_authorities->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
void do_sign()
|
|
{
|
|
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
|
if (selection.indexes().isEmpty())
|
|
return;
|
|
QModelIndex index = selection.indexes().first();
|
|
int at = index.row();
|
|
|
|
setEnabled(false);
|
|
|
|
op = new SignOperation(ui.te_data->toPlainText().toUtf8(), users, users->idFromRow(at), cms, this);
|
|
connect(op, SIGNAL(loadError()), SLOT(sign_loadError()));
|
|
connect(op, SIGNAL(finished(const QString &)), SLOT(sign_finished(const QString &)));
|
|
connect(op, SIGNAL(error(const QString &)), SLOT(sign_error(const QString &)));
|
|
}
|
|
|
|
void do_verify()
|
|
{
|
|
// prepare root certs
|
|
QCA::CertificateCollection col;
|
|
|
|
// system store
|
|
col += QCA::systemStore();
|
|
|
|
// additional roots configured in application
|
|
foreach (const CertItem &i, roots->items())
|
|
col.addCertificate(i.certificateChain().primary());
|
|
|
|
// consider self-signed users as roots
|
|
// (it is therefore not possible with this application to
|
|
// have people in your keyring that you don't trust)
|
|
foreach (const CertItem &i, users->items()) {
|
|
QCA::Certificate cert = i.certificateChain().primary();
|
|
if (cert.isSelfSigned())
|
|
col.addCertificate(cert);
|
|
}
|
|
|
|
// the self signed verify cert, if applicable
|
|
if (!self_signed_verify_cert.isNull()) {
|
|
col.addCertificate(self_signed_verify_cert);
|
|
self_signed_verify_cert = QCA::Certificate();
|
|
}
|
|
|
|
cms->setTrustedCertificates(col);
|
|
|
|
setEnabled(false);
|
|
|
|
op = new VerifyOperation(ui.te_data->toPlainText().toUtf8(), ui.te_sig->toPlainText().toUtf8(), cms, this);
|
|
connect(op, SIGNAL(finished()), SLOT(verify_finished()));
|
|
connect(op, SIGNAL(error(const QString &)), SLOT(verify_error(const QString &)));
|
|
}
|
|
|
|
void about()
|
|
{
|
|
int ver = qcaVersion();
|
|
int maj = (ver >> 16) & 0xff;
|
|
int min = (ver >> 8) & 0xff;
|
|
int bug = ver & 0xff;
|
|
QString verstr;
|
|
verstr.sprintf("%d.%d.%d", maj, min, bug);
|
|
|
|
QString str;
|
|
str += tr("CMS Signer version %1 by Justin Karneges").arg(VERSION) + '\n';
|
|
str += tr("A simple tool for creating and verifying digital signatures.") + '\n';
|
|
str += '\n';
|
|
str += tr("Using QCA version %1").arg(verstr) + '\n';
|
|
str += '\n';
|
|
str += tr("Icons by Jason Kim") + '\n';
|
|
|
|
QCA::ProviderList list = QCA::providers();
|
|
foreach (QCA::Provider *p, list) {
|
|
QString credit = p->credit();
|
|
if (!credit.isEmpty()) {
|
|
str += '\n';
|
|
str += credit;
|
|
}
|
|
}
|
|
|
|
QMessageBox::about(this, tr("About CMS Signer"), str);
|
|
}
|
|
|
|
void sign_loadError()
|
|
{
|
|
delete op;
|
|
op = 0;
|
|
|
|
setEnabled(true);
|
|
}
|
|
|
|
void sign_finished(const QString &sig)
|
|
{
|
|
delete op;
|
|
op = 0;
|
|
|
|
ui.te_sig->setPlainText(sig);
|
|
|
|
setEnabled(true);
|
|
}
|
|
|
|
void sign_error(const QString &msg)
|
|
{
|
|
delete op;
|
|
op = 0;
|
|
|
|
setEnabled(true);
|
|
|
|
QMessageBox::information(this, tr("Error"), msg);
|
|
}
|
|
|
|
void verify_finished()
|
|
{
|
|
QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
|
|
delete op;
|
|
op = 0;
|
|
|
|
// import the cert?
|
|
QCA::SecureMessageKey skey = signer.key();
|
|
if (!skey.isNull()) {
|
|
QCA::CertificateChain chain = skey.x509CertificateChain();
|
|
|
|
int at = -1;
|
|
QList<CertItem> items = users->items();
|
|
for (int n = 0; n < items.count(); ++n) {
|
|
const CertItem &i = items[n];
|
|
if (i.certificateChain().primary() == chain.primary()) {
|
|
at = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// add
|
|
if (at == -1) {
|
|
auto_import_req_id = users->addUser(chain);
|
|
return;
|
|
}
|
|
// update
|
|
else {
|
|
users->updateChain(users->idFromRow(at), chain);
|
|
}
|
|
}
|
|
|
|
verify_next();
|
|
}
|
|
|
|
void verify_next()
|
|
{
|
|
setEnabled(true);
|
|
|
|
QMessageBox::information(this, tr("Verify"), tr("Signature verified successfully."));
|
|
}
|
|
|
|
void verify_error(const QString &msg)
|
|
{
|
|
QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
|
|
delete op;
|
|
op = 0;
|
|
|
|
QCA::SecureMessageKey skey = signer.key();
|
|
if (signer.keyValidity() == QCA::ErrorSelfSigned && !skey.isNull()) {
|
|
QCA::CertificateChain chain = skey.x509CertificateChain();
|
|
if (chain.count() == 1 && chain.primary().isSelfSigned()) {
|
|
QCA::Certificate cert = chain.primary();
|
|
|
|
int ret = QMessageBox::warning(
|
|
this,
|
|
tr("Self-signed certificate"),
|
|
tr("<qt>The signature is made by an unknown user, and the certificate is self-signed.<br>\n"
|
|
"<br>\n"
|
|
"<nobr>Common Name: %1</nobr><br>\n"
|
|
"<nobr>SHA1 Fingerprint: %2</nobr><br>\n"
|
|
"<br>\n"
|
|
"Trust the certificate?</qt>")
|
|
.arg(cert.commonName(), get_fingerprint(cert)),
|
|
QMessageBox::Yes | QMessageBox::No,
|
|
QMessageBox::No);
|
|
|
|
if (ret == QMessageBox::Yes) {
|
|
self_signed_verify_cert = cert;
|
|
do_verify();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
setEnabled(true);
|
|
|
|
QMessageBox::information(this, tr("Error"), msg);
|
|
}
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
QCA::Initializer qcaInit;
|
|
QApplication qapp(argc, argv);
|
|
|
|
qapp.setApplicationName(MainWin::tr("CMS Signer"));
|
|
|
|
if (!QCA::isSupported("cert,crl,cms")) {
|
|
QMessageBox::critical(
|
|
0,
|
|
qapp.applicationName() + ": " + MainWin::tr("Error"),
|
|
MainWin::tr(
|
|
"No support for CMS is available. Please install an appropriate QCA plugin, such as qca-ossl."));
|
|
return 1;
|
|
}
|
|
|
|
QCA::KeyStoreManager::start();
|
|
|
|
MainWin mainWin;
|
|
mainWin.show();
|
|
return qapp.exec();
|
|
}
|
|
|
|
#include "main.moc"
|