cmssigner/main.cpp

The code below shows how to use Cryptographic Message Syntax (CMS) in a GUI application.
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #ifndef CERTITEM_H
00023 #define CERTITEM_H
00024 
00025 #include <QAbstractListModel>
00026 #include <QSharedDataPointer>
00027 
00028 class QString;
00029 class QStringList;
00030 
00031 namespace QCA
00032 {
00033         class PrivateKey;
00034         class CertificateChain;
00035         class KeyStoreEntry;
00036 }
00037 
00038 class CertItemStore;
00039 class CertItemStorePrivate;
00040 class CertItemPrivateLoaderPrivate;
00041 
00042 class CertItem
00043 {
00044 public:
00045         enum StorageType
00046         {
00047                 File,
00048                 KeyStore
00049         };
00050 
00051         CertItem();
00052         CertItem(const CertItem &from);
00053         ~CertItem();
00054         CertItem & operator=(const CertItem &from);
00055 
00056         QString name() const;
00057         QCA::CertificateChain certificateChain() const;
00058         bool havePrivate() const;
00059         StorageType storageType() const; // private key storage type
00060         bool isUsable() const; // file/provider present
00061 
00062 private:
00063         class Private;
00064         QSharedDataPointer<Private> d;
00065 
00066         friend class CertItemStore;
00067         friend class CertItemStorePrivate;
00068         friend class CertItemPrivateLoader;
00069         friend class CertItemPrivateLoaderPrivate;
00070 };
00071 
00072 class CertItemStore : public QAbstractListModel
00073 {
00074         Q_OBJECT
00075 public:
00076         enum IconType
00077         {
00078                 IconCert,
00079                 IconCrl,
00080                 IconKeyBundle,
00081                 IconPgpPub,
00082                 IconPgpSec
00083         };
00084 
00085         CertItemStore(QObject *parent = 0);
00086         ~CertItemStore();
00087 
00088         int idFromRow(int row) const;
00089         int rowFromId(int id) const;
00090         CertItem itemFromId(int id) const;
00091         CertItem itemFromRow(int row) const;
00092 
00093         QList<CertItem> items() const;
00094 
00095         QStringList save() const;
00096         bool load(const QStringList &in);
00097 
00098         // returns a reqId
00099         int addFromFile(const QString &fileName);
00100         int addFromKeyStore(const QCA::KeyStoreEntry &entry);
00101         int addUser(const QCA::CertificateChain &chain);
00102 
00103         void updateChain(int id, const QCA::CertificateChain &chain);
00104 
00105         void removeItem(int id);
00106 
00107         void setIcon(IconType type, const QPixmap &icon);
00108 
00109         // reimplemented
00110         int rowCount(const QModelIndex &parent = QModelIndex()) const;
00111         QVariant data(const QModelIndex &index, int role) const;
00112         Qt::ItemFlags flags(const QModelIndex &index) const;
00113         bool setData(const QModelIndex &index, const QVariant &value, int role);
00114 
00115 signals:
00116         void addSuccess(int reqId, int id);
00117         void addFailed(int reqId);
00118 
00119 private:
00120         friend class CertItemStorePrivate;
00121         CertItemStorePrivate *d;
00122 
00123         friend class CertItemPrivateLoader;
00124         friend class CertItemPrivateLoaderPrivate;
00125 };
00126 
00127 class CertItemPrivateLoader : public QObject
00128 {
00129         Q_OBJECT
00130 public:
00131         explicit CertItemPrivateLoader(CertItemStore *store, QObject *parent = 0);
00132         ~CertItemPrivateLoader();
00133 
00134         void start(int id);
00135 
00136         QCA::PrivateKey privateKey() const;
00137 
00138 signals:
00139         void finished();
00140 
00141 private:
00142         friend class CertItemPrivateLoaderPrivate;
00143         CertItemPrivateLoaderPrivate *d;
00144 };
00145 
00146 #endif
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #include "certitem.h"
00023 
00024 #include <QtCore>
00025 #include <QtGui>
00026 #include <QtCrypto>
00027 #include "prompter.h"
00028 
00029 typedef QMap<CertItemStore::IconType,QPixmap> CertItemIconset;
00030 
00031 //----------------------------------------------------------------------------
00032 // MyPrompter
00033 //----------------------------------------------------------------------------
00034 class MyPrompter : public Prompter
00035 {
00036         Q_OBJECT
00037 private:
00038         QMap<QString,QCA::SecureArray> known;
00039         QMap<QString,QCA::SecureArray> maybe;
00040 
00041 public:
00042         MyPrompter(QObject *parent = 0) :
00043                 Prompter(parent)
00044         {
00045         }
00046 
00047         void fileSuccess(const QString &fileName)
00048         {
00049                 if(maybe.contains(fileName))
00050                 {
00051                         known[fileName] = maybe[fileName];
00052                         maybe.remove(fileName);
00053                 }
00054         }
00055 
00056         void fileFailed(const QString &fileName)
00057         {
00058                 maybe.remove(fileName);
00059                 known.remove(fileName);
00060         }
00061 
00062 protected:
00063         virtual QCA::SecureArray knownPassword(const QCA::Event &event)
00064         {
00065                 if(event.source() == QCA::Event::Data && !event.fileName().isEmpty())
00066                         return known.value(event.fileName());
00067                 else
00068                         return QCA::SecureArray();
00069         }
00070 
00071         virtual void userSubmitted(const QCA::SecureArray &password, const QCA::Event &event)
00072         {
00073                 if(event.source() == QCA::Event::Data && !event.fileName().isEmpty())
00074                         maybe[event.fileName()] = password;
00075         }
00076 };
00077 
00078 //----------------------------------------------------------------------------
00079 // CertItem
00080 //----------------------------------------------------------------------------
00081 static QString escape(const QString &in)
00082 {
00083         QString out;
00084         for(int n = 0; n < in.length(); ++n)
00085         {
00086                 if(in[n] == '\\')
00087                         out += "\\\\";
00088                 else if(in[n] == ':')
00089                         out += "\\c";
00090                 else if(in[n] == '\n')
00091                         out += "\\n";
00092                 else
00093                         out += in[n];
00094         }
00095         return out;
00096 }
00097 
00098 static QString unescape(const QString &in)
00099 {
00100         QString out;
00101         for(int n = 0; n < in.length(); ++n)
00102         {
00103                 if(in[n] == '\\')
00104                 {
00105                         if(n + 1 < in.length())
00106                         {
00107                                 ++n;
00108                                 if(in[n] == '\\')
00109                                         out += '\\';
00110                                 else if(in[n] == 'c')
00111                                         out += ':';
00112                                 else if(in[n] == 'n')
00113                                         out += '\n';
00114                         }
00115                 }
00116                 else
00117                         out += in[n];
00118         }
00119         return out;
00120 }
00121 
00122 class CertItem::Private : public QSharedData
00123 {
00124 public:
00125         QString name;
00126         QCA::CertificateChain chain;
00127         bool havePrivate;
00128         StorageType storageType;
00129         bool usable;
00130 
00131         QString fileName;
00132         QCA::KeyStoreEntry keyStoreEntry;
00133         QString keyStoreEntryString;
00134 
00135         Private() :
00136                 havePrivate(false),
00137                 storageType(File),
00138                 usable(false)
00139         {
00140         }
00141 
00142         QString toString() const
00143         {
00144                 QStringList parts;
00145 
00146                 parts += name;
00147                 parts += QString::number(chain.count());
00148                 foreach(const QCA::Certificate &cert, chain)
00149                         parts += QCA::Base64().arrayToString(cert.toDER());
00150 
00151                 if(havePrivate)
00152                 {
00153                         if(storageType == File)
00154                         {
00155                                 parts += "privateFile";
00156                                 parts += fileName;
00157                         }
00158                         else // KeyStoreEntry
00159                         {
00160                                 parts += "privateEntry";
00161                                 if(!keyStoreEntry.isNull())
00162                                         parts += keyStoreEntry.toString();
00163                                 else
00164                                         parts += keyStoreEntryString;
00165                         }
00166                 }
00167 
00168                 for(int n = 0; n < parts.count(); ++n)
00169                         parts[n] = escape(parts[n]);
00170                 return parts.join(":");
00171         }
00172 
00173         bool fromString(const QString &in)
00174         {
00175                 QStringList parts = in.split(':');
00176                 for(int n = 0; n < parts.count(); ++n)
00177                         parts[n] = unescape(parts[n]);
00178 
00179                 if(parts.count() < 3)
00180                         return false;
00181 
00182                 name = parts[0];
00183                 int chainCount = parts[1].toInt();
00184                 if(chainCount < 1 || chainCount > parts.count() - 2)
00185                         return false;
00186                 chain.clear();
00187                 for(int n = 0; n < chainCount; ++n)
00188                 {
00189                         QCA::Certificate cert = QCA::Certificate::fromDER(QCA::Base64().stringToArray(parts[n + 2]).toByteArray());
00190                         if(cert.isNull())
00191                                 return false;
00192                         chain += cert;
00193                 }
00194                 int at = chain.count() + 2;
00195 
00196                 if(at < parts.count())
00197                 {
00198                         havePrivate = true;
00199                         usable = false;
00200 
00201                         if(parts[at] == "privateFile")
00202                         {
00203                                 storageType = File;
00204                                 fileName = parts[at + 1];
00205                                 if(QFile::exists(fileName))
00206                                         usable = true;
00207                         }
00208                         else if(parts[at] == "privateEntry")
00209                         {
00210                                 storageType = KeyStore;
00211                                 keyStoreEntryString = parts[at + 1];
00212                                 keyStoreEntry = QCA::KeyStoreEntry(keyStoreEntryString);
00213                                 if(!keyStoreEntry.isNull())
00214                                         usable = true;
00215                         }
00216                         else
00217                                 return false;
00218                 }
00219 
00220                 return true;
00221         }
00222 };
00223 
00224 CertItem::CertItem()
00225 {
00226 }
00227 
00228 CertItem::CertItem(const CertItem &from) :
00229         d(from.d)
00230 {
00231 }
00232 
00233 CertItem::~CertItem()
00234 {
00235 }
00236 
00237 CertItem & CertItem::operator=(const CertItem &from)
00238 {
00239         d = from.d;
00240         return *this;
00241 }
00242 
00243 QString CertItem::name() const
00244 {
00245         return d->name;
00246 }
00247 
00248 QCA::CertificateChain CertItem::certificateChain() const
00249 {
00250         return d->chain;
00251 }
00252 
00253 bool CertItem::havePrivate() const
00254 {
00255         return d->havePrivate;
00256 }
00257 
00258 CertItem::StorageType CertItem::storageType() const
00259 {
00260         return d->storageType;
00261 }
00262 
00263 bool CertItem::isUsable() const
00264 {
00265         return d->usable;
00266 }
00267 
00268 //----------------------------------------------------------------------------
00269 // CertItemStore
00270 //----------------------------------------------------------------------------
00271 static MyPrompter *g_prompter = 0;
00272 static int g_prompter_refs = 0;
00273 
00274 class CertItemStorePrivate : public QObject
00275 {
00276         Q_OBJECT
00277 public:
00278         CertItemStore *q;
00279         MyPrompter *prompter;
00280         QList<CertItem> list;
00281         QList<int> idList;
00282         CertItemIconset iconset;
00283         int next_id;
00284         int next_req_id;
00285 
00286         class LoaderItem
00287         {
00288         public:
00289                 int req_id;
00290                 QCA::KeyLoader *keyLoader;
00291                 QString fileName;
00292         };
00293 
00294         QList<LoaderItem> loaders;
00295 
00296         CertItemStorePrivate(CertItemStore *_q) :
00297                 QObject(_q),
00298                 q(_q),
00299                 next_id(0),
00300                 next_req_id(0)
00301         {
00302                 if(!g_prompter)
00303                 {
00304                         g_prompter = new MyPrompter;
00305                         g_prompter_refs = 1;
00306                 }
00307                 else
00308                         ++g_prompter_refs;
00309 
00310                 prompter = g_prompter;
00311         }
00312 
00313         ~CertItemStorePrivate()
00314         {
00315                 foreach(const LoaderItem &i, loaders)
00316                         delete i.keyLoader;
00317 
00318                 --g_prompter_refs;
00319                 if(g_prompter_refs == 0)
00320                 {
00321                         delete g_prompter;
00322                         g_prompter = 0;
00323                 }
00324         }
00325 
00326         QString getUniqueName(const QString &name)
00327         {
00328                 int num = 1;
00329                 while(1)
00330                 {
00331                         QString tryname;
00332                         if(num == 1)
00333                                 tryname = name;
00334                         else
00335                                 tryname = name + QString(" (%1)").arg(num);
00336 
00337                         bool found = false;
00338                         foreach(const CertItem &i, list)
00339                         {
00340                                 if(i.name() == tryname)
00341                                 {
00342                                         found = true;
00343                                         break;
00344                                 }
00345                         }
00346                         if(!found)
00347                                 return tryname;
00348 
00349                         ++num;
00350                 }
00351         }
00352 
00353         static QString convertErrorToString(QCA::ConvertResult r)
00354         {
00355                 QString str;
00356                 switch(r)
00357                 {
00358                         case QCA::ConvertGood:      break;
00359                         case QCA::ErrorPassphrase:  str = tr("Incorrect passphrase.");
00360                         case QCA::ErrorFile:        str = tr("Unable to open or read file.");
00361                         case QCA::ErrorDecode:
00362                         default:                    str = tr("Unable to decode format.");
00363                 }
00364                 return str;
00365         }
00366 
00367 public slots:
00368         void loader_finished()
00369         {
00370                 QCA::KeyLoader *keyLoader = (QCA::KeyLoader *)sender();
00371                 int at = -1;
00372                 for(int n = 0; n < loaders.count(); ++n)
00373                 {
00374                         if(loaders[n].keyLoader == keyLoader)
00375                         {
00376                                 at = n;
00377                                 break;
00378                         }
00379                 }
00380                 Q_ASSERT(at != -1);
00381 
00382                 int req_id = loaders[at].req_id;
00383                 QString fileName = loaders[at].fileName;
00384                 loaders.removeAt(at);
00385 
00386                 QCA::ConvertResult r = keyLoader->convertResult();
00387                 if(r != QCA::ConvertGood)
00388                 {
00389                         delete keyLoader;
00390                         prompter->fileFailed(fileName);
00391                         QMessageBox::information(0, tr("Error"),
00392                                 tr("Error importing certificate and private key.\nReason: %1").arg(convertErrorToString(r)));
00393                         emit q->addFailed(req_id);
00394                         return;
00395                 }
00396 
00397                 prompter->fileSuccess(fileName);
00398 
00399                 QCA::KeyBundle kb = keyLoader->keyBundle();
00400                 delete keyLoader;
00401 
00402                 QCA::CertificateChain chain = kb.certificateChain();
00403                 QCA::Certificate cert = chain.primary();
00404 
00405                 QString name = getUniqueName(cert.commonName());
00406 
00407                 CertItem i;
00408                 i.d = new CertItem::Private;
00409                 i.d->name = name;
00410                 i.d->chain = chain;
00411                 i.d->havePrivate = true;
00412                 i.d->storageType = CertItem::File;
00413                 i.d->usable = true;
00414                 i.d->fileName = fileName;
00415 
00416                 int id = next_id++;
00417 
00418                 q->beginInsertRows(QModelIndex(), list.size(), list.size());
00419                 list += i;
00420                 idList += id;
00421                 q->endInsertRows();
00422 
00423                 emit q->addSuccess(req_id, id);
00424         }
00425 };
00426 
00427 CertItemStore::CertItemStore(QObject *parent) :
00428         QAbstractListModel(parent)
00429 {
00430         d = new CertItemStorePrivate(this);
00431 }
00432 
00433 CertItemStore::~CertItemStore()
00434 {
00435         delete d;
00436 }
00437 
00438 int CertItemStore::idFromRow(int row) const
00439 {
00440         return d->idList[row];
00441 }
00442 
00443 int CertItemStore::rowFromId(int id) const
00444 {
00445         for(int n = 0; n < d->idList.count(); ++n)
00446         {
00447                 if(d->idList[n] == id)
00448                         return n;
00449         }
00450         return -1;
00451 }
00452 
00453 CertItem CertItemStore::itemFromId(int id) const
00454 {
00455         return d->list[rowFromId(id)];
00456 }
00457 
00458 CertItem CertItemStore::itemFromRow(int row) const
00459 {
00460         return d->list[row];
00461 }
00462 
00463 QList<CertItem> CertItemStore::items() const
00464 {
00465         return d->list;
00466 }
00467 
00468 QStringList CertItemStore::save() const
00469 {
00470         QStringList out;
00471         foreach(const CertItem &i, d->list)
00472                 out += i.d->toString();
00473         return out;
00474 }
00475 
00476 bool CertItemStore::load(const QStringList &in)
00477 {
00478         QList<CertItem> addList;
00479         QList<int> addIdList;
00480         foreach(const QString &s, in)
00481         {
00482                 CertItem i;
00483                 i.d = new CertItem::Private;
00484                 if(i.d->fromString(s))
00485                 {
00486                         addList += i;
00487                         addIdList += d->next_id++;
00488                 }
00489         }
00490 
00491         if(addList.isEmpty())
00492                 return true;
00493 
00494         beginInsertRows(QModelIndex(), d->list.size(), d->list.size() + addList.count() - 1);
00495         d->list += addList;
00496         d->idList += addIdList;
00497         endInsertRows();
00498 
00499         return true;
00500 }
00501 
00502 int CertItemStore::addFromFile(const QString &fileName)
00503 {
00504         CertItemStorePrivate::LoaderItem i;
00505         i.req_id = d->next_req_id++;
00506         i.keyLoader = new QCA::KeyLoader(d);
00507         i.fileName = fileName;
00508         connect(i.keyLoader, SIGNAL(finished()), d, SLOT(loader_finished()));
00509         d->loaders += i;
00510         i.keyLoader->loadKeyBundleFromFile(fileName);
00511         return i.req_id;
00512 }
00513 
00514 int CertItemStore::addFromKeyStore(const QCA::KeyStoreEntry &entry)
00515 {
00516         QCA::KeyBundle kb = entry.keyBundle();
00517 
00518         QCA::CertificateChain chain = kb.certificateChain();
00519         QCA::Certificate cert = chain.primary();
00520 
00521         QString name = d->getUniqueName(entry.name());
00522 
00523         CertItem i;
00524         i.d = new CertItem::Private;
00525         i.d->name = name;
00526         i.d->chain = chain;
00527         i.d->havePrivate = true;
00528         i.d->storageType = CertItem::KeyStore;
00529         i.d->usable = true;
00530         i.d->keyStoreEntry = entry;
00531 
00532         int id = d->next_id++;
00533 
00534         beginInsertRows(QModelIndex(), d->list.size(), d->list.size());
00535         d->list += i;
00536         d->idList += id;
00537         endInsertRows();
00538 
00539         int req_id = d->next_req_id++;
00540         QMetaObject::invokeMethod(this, "addSuccess", Qt::QueuedConnection, Q_ARG(int, req_id), Q_ARG(int, id));
00541         return req_id;
00542 }
00543 
00544 int CertItemStore::addUser(const QCA::CertificateChain &chain)
00545 {
00546         QCA::Certificate cert = chain.primary();
00547 
00548         QString name = d->getUniqueName(cert.commonName());
00549 
00550         CertItem i;
00551         i.d = new CertItem::Private;
00552         i.d->name = name;
00553         i.d->chain = chain;
00554 
00555         int id = d->next_id++;
00556 
00557         beginInsertRows(QModelIndex(), d->list.size(), d->list.size());
00558         d->list += i;
00559         d->idList += id;
00560         endInsertRows();
00561 
00562         int req_id = d->next_req_id++;
00563         QMetaObject::invokeMethod(this, "addSuccess", Qt::QueuedConnection, Q_ARG(int, req_id), Q_ARG(int, id));
00564         return req_id;
00565 }
00566 
00567 void CertItemStore::updateChain(int id, const QCA::CertificateChain &chain)
00568 {
00569         int at = rowFromId(id);
00570         d->list[at].d->chain = chain;
00571 }
00572 
00573 void CertItemStore::removeItem(int id)
00574 {
00575         int at = rowFromId(id);
00576 
00577         beginRemoveRows(QModelIndex(), at, at);
00578         d->list.removeAt(at);
00579         d->idList.removeAt(at);
00580         endRemoveRows();
00581 }
00582 
00583 void CertItemStore::setIcon(IconType type, const QPixmap &icon)
00584 {
00585         d->iconset[type] = icon;
00586 }
00587 
00588 int CertItemStore::rowCount(const QModelIndex &parent) const
00589 {
00590         Q_UNUSED(parent);
00591         return d->list.count();
00592 }
00593 
00594 QVariant CertItemStore::data(const QModelIndex &index, int role) const
00595 {
00596         if(!index.isValid())
00597                 return QVariant();
00598 
00599         int at = index.row();
00600         QList<CertItem> &list = d->list;
00601 
00602         if(at >= list.count())
00603                 return QVariant();
00604 
00605         if(role == Qt::DisplayRole)
00606         {
00607                 QString str = list[at].name();
00608                 if(list[at].havePrivate() && !list[at].isUsable())
00609                         str += QString(" ") + tr("(not usable)");
00610                 return str;
00611         }
00612         else if(role == Qt::EditRole)
00613                 return list[at].name();
00614         else if(role == Qt::DecorationRole)
00615         {
00616                 if(list[at].havePrivate())
00617                         return d->iconset[CertItemStore::IconKeyBundle];
00618                 else
00619                         return d->iconset[CertItemStore::IconCert];
00620         }
00621         else
00622                 return QVariant();
00623 }
00624 
00625 Qt::ItemFlags CertItemStore::flags(const QModelIndex &index) const
00626 {
00627         if(!index.isValid())
00628                 return Qt::ItemIsEnabled;
00629 
00630         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
00631 }
00632 
00633 bool CertItemStore::setData(const QModelIndex &index, const QVariant &value, int role)
00634 {
00635         if(index.isValid() && role == Qt::EditRole)
00636         {
00637                 QString str = value.toString();
00638                 d->list[index.row()].d->name = str;
00639                 emit dataChanged(index, index);
00640                 return true;
00641         }
00642         return false;
00643 }
00644 
00645 //----------------------------------------------------------------------------
00646 // CertItemPrivateLoader
00647 //----------------------------------------------------------------------------
00648 class CertItemPrivateLoaderPrivate : public QObject
00649 {
00650         Q_OBJECT
00651 public:
00652         CertItemPrivateLoader *q;
00653         CertItemStore *store;
00654         QCA::KeyLoader *loader;
00655         QString fileName;
00656         QCA::PrivateKey key;
00657 
00658         CertItemPrivateLoaderPrivate(CertItemPrivateLoader *_q) :
00659                 QObject(_q),
00660                 q(_q)
00661         {
00662         }
00663 
00664 public slots:
00665         void loader_finished()
00666         {
00667                 QCA::ConvertResult r = loader->convertResult();
00668                 if(r != QCA::ConvertGood)
00669                 {
00670                         delete loader;
00671                         loader = 0;
00672                         store->d->prompter->fileFailed(fileName);
00673                         QMessageBox::information(0, tr("Error"),
00674                                 tr("Error accessing private key.\nReason: %1").arg(CertItemStorePrivate::convertErrorToString(r)));
00675                         emit q->finished();
00676                         return;
00677                 }
00678 
00679                 store->d->prompter->fileSuccess(fileName);
00680 
00681                 key = loader->keyBundle().privateKey();
00682                 delete loader;
00683                 loader = 0;
00684                 emit q->finished();
00685         }
00686 };
00687 
00688 CertItemPrivateLoader::CertItemPrivateLoader(CertItemStore *store, QObject *parent) :
00689         QObject(parent)
00690 {
00691         d = new CertItemPrivateLoaderPrivate(this);
00692         d->store = store;
00693 }
00694 
00695 CertItemPrivateLoader::~CertItemPrivateLoader()
00696 {
00697         delete d;
00698 }
00699 
00700 void CertItemPrivateLoader::start(int id)
00701 {
00702         CertItem i = d->store->itemFromId(id);
00703 
00704         if(i.storageType() == CertItem::KeyStore)
00705         {
00706                 d->key = i.d->keyStoreEntry.keyBundle().privateKey();
00707                 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
00708                 return;
00709         }
00710 
00711         d->key = QCA::PrivateKey();
00712         d->fileName = i.d->fileName;
00713         d->loader = new QCA::KeyLoader(d);
00714         connect(d->loader, SIGNAL(finished()), d, SLOT(loader_finished()));
00715         d->loader->loadKeyBundleFromFile(d->fileName);
00716 }
00717 
00718 QCA::PrivateKey CertItemPrivateLoader::privateKey() const
00719 {
00720         return d->key;
00721 }
00722 
00723 #include "certitem.moc"
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #ifndef CERTVIEWDLG_H
00023 #define CERTVIEWDLG_H
00024 
00025 #include <QDialog>
00026 
00027 namespace QCA
00028 {
00029         class CertificateChain;
00030 }
00031 
00032 class CertViewDlg : public QDialog
00033 {
00034         Q_OBJECT
00035 public:
00036         explicit CertViewDlg(const QCA::CertificateChain &chain, QWidget *parent = 0);
00037         ~CertViewDlg();
00038 
00039 private:
00040         class Private;
00041         Private *d;
00042 };
00043 
00044 #endif
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #include "certviewdlg.h"
00023 
00024 #include <QtCore>
00025 #include <QtGui>
00026 #include <QtCrypto>
00027 #include "ui_certview.h"
00028 
00029 // from qcatool
00030 class InfoType
00031 {
00032 public:
00033         QCA::CertificateInfoType type;
00034         QString varname;
00035         QString shortname;
00036         QString name;
00037         QString desc;
00038 
00039         InfoType()
00040         {
00041         }
00042 
00043         InfoType(QCA::CertificateInfoType _type, const QString &_varname, const QString &_shortname, const QString &_name, const QString &_desc) :
00044                 type(_type),
00045                 varname(_varname),
00046                 shortname(_shortname),
00047                 name(_name),
00048                 desc(_desc)
00049         {
00050         }
00051 };
00052 
00053 static QList<InfoType> makeInfoTypeList(bool legacyEmail = false)
00054 {
00055         QList<InfoType> out;
00056         out += InfoType(QCA::CommonName,             "CommonName",             "CN",  CertViewDlg::tr("Common Name (CN)"),          "Full name, domain, anything");
00057         out += InfoType(QCA::Email,                  "Email",                  "",    CertViewDlg::tr("Email Address"),             "");
00058         if(legacyEmail)
00059                 out += InfoType(QCA::EmailLegacy,            "EmailLegacy",       "",    CertViewDlg::tr("PKCS#9 Email Address"),      "");
00060         out += InfoType(QCA::Organization,           "Organization",           "O",   CertViewDlg::tr("Organization (O)"),          "Company, group, etc");
00061         out += InfoType(QCA::OrganizationalUnit,     "OrganizationalUnit",     "OU",  CertViewDlg::tr("Organizational Unit (OU)"),  "Division/branch of organization");
00062         out += InfoType(QCA::Locality,               "Locality",               "",    CertViewDlg::tr("Locality (L)"),              "City, shire, part of a state");
00063         out += InfoType(QCA::State,                  "State",                  "",    CertViewDlg::tr("State (ST)"),                "State within the country");
00064         out += InfoType(QCA::Country,                "Country",                "C",   CertViewDlg::tr("Country Code (C)"),          "2-letter code");
00065         out += InfoType(QCA::IncorporationLocality,  "IncorporationLocality",  "",    CertViewDlg::tr("Incorporation Locality"),    "For EV certificates");
00066         out += InfoType(QCA::IncorporationState,     "IncorporationState",     "",    CertViewDlg::tr("Incorporation State"),       "For EV certificates");
00067         out += InfoType(QCA::IncorporationCountry,   "IncorporationCountry",   "",    CertViewDlg::tr("Incorporation Country"),     "For EV certificates");
00068         out += InfoType(QCA::URI,                    "URI",                    "",    CertViewDlg::tr("URI"),                       "");
00069         out += InfoType(QCA::DNS,                    "DNS",                    "",    CertViewDlg::tr("Domain Name"),               "Domain (dnsName)");
00070         out += InfoType(QCA::IPAddress,              "IPAddress",              "",    CertViewDlg::tr("IP Adddress"),               "");
00071         out += InfoType(QCA::XMPP,                   "XMPP",                   "",    CertViewDlg::tr("XMPP Address (JID)"),        "From RFC 3920 (id-on-xmppAddr)");
00072         return out;
00073 }
00074 
00075 static QString try_print_info(const QString &name, const QStringList &values)
00076 {
00077         QString out;
00078         if(!values.isEmpty())
00079         {
00080                 QString value = values.join(", ");
00081                 out = QString("   ") + CertViewDlg::tr("%1: %2").arg(name, value) + '\n';
00082         }
00083         return out;
00084 }
00085 
00086 static QString print_info(const QString &title, const QCA::CertificateInfo &info)
00087 {
00088         QString out;
00089         QList<InfoType> list = makeInfoTypeList();
00090         out += title + '\n';
00091         foreach(const InfoType &t, list)
00092                 out += try_print_info(t.name, info.values(t.type));
00093         return out;
00094 }
00095 
00096 static QString cert_info_string(const QCA::Certificate &cert)
00097 {
00098         QString out;
00099         out += CertViewDlg::tr("Serial Number: %1").arg(cert.serialNumber().toString()) + '\n';
00100         out += print_info(CertViewDlg::tr("Subject"), cert.subjectInfo());
00101         out += print_info(CertViewDlg::tr("Issuer"), cert.issuerInfo());
00102         out += CertViewDlg::tr("Validity") + '\n';
00103         out += QString("   ") + CertViewDlg::tr("Not before: %1").arg(cert.notValidBefore().toString()) + '\n';
00104         out += QString("   ") + CertViewDlg::tr("Not after:  %1").arg(cert.notValidAfter().toString()) + '\n';
00105         return out;
00106 }
00107 
00108 class CertViewDlg::Private : public QObject
00109 {
00110         Q_OBJECT
00111 public:
00112         CertViewDlg *q;
00113         Ui_CertView ui;
00114         QCA::CertificateChain chain;
00115 
00116         Private(CertViewDlg *_q) :
00117                 QObject(_q),
00118                 q(_q)
00119         {
00120                 ui.setupUi(q);
00121                 connect(ui.cb_chain, SIGNAL(activated(int)), SLOT(cb_activated(int)));
00122                 ui.lb_info->setTextInteractionFlags(Qt::TextSelectableByMouse);
00123         }
00124 
00125         void update()
00126         {
00127                 QStringList names = QCA::makeFriendlyNames(chain);
00128                 ui.cb_chain->clear();
00129                 foreach(const QString &s, names)
00130                         ui.cb_chain->insertItem(ui.cb_chain->count(), s);
00131                 updateInfo();
00132         }
00133 
00134         void updateInfo()
00135         {
00136                 int x = ui.cb_chain->currentIndex();
00137                 if(x == -1)
00138                 {
00139                         ui.lb_info->setText("");
00140                         return;
00141                 }
00142 
00143                 ui.lb_info->setText(cert_info_string(chain[x]));
00144         }
00145 
00146 private slots:
00147         void cb_activated(int)
00148         {
00149                 updateInfo();
00150         }
00151 };
00152 
00153 CertViewDlg::CertViewDlg(const QCA::CertificateChain &chain, QWidget *parent) :
00154         QDialog(parent)
00155 {
00156         d = new Private(this);
00157         d->chain = chain;
00158         d->update();
00159 }
00160 
00161 CertViewDlg::~CertViewDlg()
00162 {
00163         delete d;
00164 }
00165 
00166 #include "certviewdlg.moc"
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #ifndef KEYSELECTDLG_H
00023 #define KEYSELECTDLG_H
00024 
00025 #include <QDialog>
00026 
00027 class QPixmap;
00028 
00029 namespace QCA
00030 {
00031         class CertificateChain;
00032         class KeyStoreEntry;
00033 }
00034 
00035 class KeySelectDlg : public QDialog
00036 {
00037         Q_OBJECT
00038 public:
00039         enum IconType
00040         {
00041                 IconCert,
00042                 IconCrl,
00043                 IconKeyBundle,
00044                 IconPgpPub,
00045                 IconPgpSec
00046         };
00047 
00048         KeySelectDlg(QWidget *parent = 0);
00049         ~KeySelectDlg();
00050 
00051         void setIcon(IconType type, const QPixmap &icon);
00052 
00053 signals:
00054         void selected(const QCA::KeyStoreEntry &entry);
00055         void viewCertificate(const QCA::CertificateChain &chain);
00056 
00057 protected slots:
00058         virtual void accept();
00059 
00060 private:
00061         class Private;
00062         friend class Private;
00063         Private *d;
00064 };
00065 
00066 #endif
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #include "keyselectdlg.h"
00023 
00024 #include <QtCore>
00025 #include <QtGui>
00026 #include <QtCrypto>
00027 #include "ui_keyselect.h"
00028 
00029 #define ONLY_SHOW_KEYBUNDLE
00030 
00031 typedef QMap<KeySelectDlg::IconType,QPixmap> KeyStoreIconset;
00032 
00033 class KeyStoreItemShared
00034 {
00035 public:
00036         KeyStoreIconset iconset;
00037         QString notAvailableString;
00038 };
00039 
00040 class KeyStoreItem : public QStandardItem
00041 {
00042 public:
00043         enum Type
00044         {
00045                 Store = UserType,
00046                 Entry
00047         };
00048 
00049         enum Role
00050         {
00051                 NameRole = Qt::UserRole,
00052                 SubTypeRole,
00053                 AvailabilityRole,
00054                 PositionRole
00055         };
00056 
00057         QPixmap entryTypeToIcon(QCA::KeyStoreEntry::Type type) const
00058         {
00059                 QPixmap out;
00060                 if(!_shared)
00061                         return out;
00062                 const KeyStoreIconset &iconset = _shared->iconset;
00063                 switch(type)
00064                 {
00065                         case QCA::KeyStoreEntry::TypeKeyBundle:     out = iconset[KeySelectDlg::IconKeyBundle]; break;
00066                         case QCA::KeyStoreEntry::TypeCertificate:   out = iconset[KeySelectDlg::IconCert]; break;
00067                         case QCA::KeyStoreEntry::TypeCRL:           out = iconset[KeySelectDlg::IconCrl]; break;
00068                         case QCA::KeyStoreEntry::TypePGPSecretKey:  out = iconset[KeySelectDlg::IconPgpSec]; break;
00069                         case QCA::KeyStoreEntry::TypePGPPublicKey:  out = iconset[KeySelectDlg::IconPgpPub]; break;
00070                         default:                                    break;
00071                 }
00072                 return out;
00073         }
00074 
00075         Type _type;
00076         KeyStoreItemShared *_shared;
00077 
00078         QCA::KeyStore *keyStore;
00079         QCA::KeyStoreEntry keyStoreEntry;
00080 
00081         KeyStoreItem(Type type, KeyStoreItemShared *shared) :
00082                 _type(type),
00083                 _shared(shared)
00084         {
00085                 setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
00086         }
00087 
00088         void setStore(const QString &name, QCA::KeyStore::Type type)
00089         {
00090                 setData(name, NameRole);
00091                 setData((int)type, SubTypeRole);
00092         }
00093 
00094         void setEntry(const QString &name, QCA::KeyStoreEntry::Type type, bool available, int pos)
00095         {
00096                 setData(name, NameRole);
00097                 setData((int)type, SubTypeRole);
00098                 setData(available, AvailabilityRole);
00099                 setData(pos, PositionRole);
00100         }
00101 
00102         virtual QVariant data(int role) const
00103         {
00104                 if(role == Qt::DisplayRole)
00105                 {
00106                         if(_type == Store)
00107                         {
00108                                 return data(NameRole).toString();
00109                         }
00110                         else if(_type == Entry)
00111                         {
00112                                 QString str = data(NameRole).toString();
00113                                 if(_shared && !data(AvailabilityRole).toBool())
00114                                         str += QString(" ") + _shared->notAvailableString;
00115                                 return str;
00116                         }
00117                         else
00118                                 return QStandardItem::data(role);
00119                 }
00120                 else if(role == Qt::DecorationRole)
00121                 {
00122                         if(_type == Entry)
00123                         {
00124                                 QCA::KeyStoreEntry::Type type = (QCA::KeyStoreEntry::Type)data(SubTypeRole).toInt();
00125                                 return entryTypeToIcon(type);
00126                         }
00127                         else
00128                                 return QStandardItem::data(role);
00129                 }
00130                 else
00131                         return QStandardItem::data(role);
00132         }
00133 
00134         virtual int type() const
00135         {
00136                 return _type;
00137         }
00138 
00139         virtual QStandardItem *clone() const
00140         {
00141                 return new KeyStoreItem(*this);
00142         }
00143 };
00144 
00145 class KeyStoreModel : public QStandardItemModel
00146 {
00147         Q_OBJECT
00148 public:
00149         KeyStoreItemShared shared;
00150 
00151         QCA::KeyStoreManager ksm;
00152 
00153         KeyStoreModel(QObject *parent = 0) :
00154                 QStandardItemModel(parent), ksm(this)
00155         {
00156                 shared.notAvailableString = tr("(not available)");
00157 
00158                 // make sure keystores are started
00159                 QCA::KeyStoreManager::start();
00160 
00161                 connect(&ksm, SIGNAL(keyStoreAvailable(const QString &)), SLOT(ks_available(const QString &)));
00162                 QStringList list = ksm.keyStores();
00163                 foreach(const QString &s, list)
00164                         ks_available(s);
00165 
00166                 setSortRole(KeyStoreItem::PositionRole);
00167         }
00168 
00169         KeyStoreItem *itemFromStore(QCA::KeyStore *ks) const
00170         {
00171                 for(int n = 0; n < rowCount(); ++n)
00172                 {
00173                         KeyStoreItem *i = (KeyStoreItem *)item(n);
00174                         if(i->keyStore == ks)
00175                                 return i;
00176                 }
00177                 return 0;
00178         }
00179 
00180 private slots:
00181         void ks_available(const QString &keyStoreId)
00182         {
00183                 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm);
00184 
00185 #ifdef ONLY_SHOW_KEYBUNDLE
00186                 // only list stores containing keybundles (non-pgp identities)
00187                 if(!ks->holdsIdentities() || ks->type() == QCA::KeyStore::PGPKeyring)
00188                         return;
00189 #endif
00190 
00191                 connect(ks, SIGNAL(updated()), SLOT(ks_updated()));
00192                 connect(ks, SIGNAL(unavailable()), SLOT(ks_unavailable()));
00193 
00194                 KeyStoreItem *store_item = new KeyStoreItem(KeyStoreItem::Store, &shared);
00195                 store_item->setStore(ks->name(), ks->type());
00196                 store_item->keyStore = ks;
00197                 ks->startAsynchronousMode();
00198                 appendRow(store_item);
00199         }
00200 
00201         void ks_updated()
00202         {
00203                 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
00204                 KeyStoreItem *store_item = itemFromStore(ks);
00205                 Q_ASSERT(store_item);
00206 
00207                 QList<QCA::KeyStoreEntry> newEntries = ks->entryList();
00208 
00209 #ifdef ONLY_SHOW_KEYBUNDLE
00210                 // ignore entries that are not keybundles
00211                 for(int n = 0; n < newEntries.count(); ++n)
00212                 {
00213                         if(newEntries[n].type() != QCA::KeyStoreEntry::TypeKeyBundle)
00214                         {
00215                                 newEntries.removeAt(n);
00216                                 --n; // adjust position
00217                         }
00218                 }
00219 #endif
00220 
00221                 // update the store item itself
00222                 store_item->setStore(ks->name(), ks->type());
00223 
00224                 // handle removed child entries
00225                 for(int n = 0; n < store_item->rowCount(); ++n)
00226                 {
00227                         KeyStoreItem *i = (KeyStoreItem *)store_item->child(n);
00228 
00229                         // is the existing entry in the new list?
00230                         bool found = false;
00231                         foreach(const QCA::KeyStoreEntry &ne, newEntries)
00232                         {
00233                                 if(ne.id() == i->keyStoreEntry.id())
00234                                 {
00235                                         found = true;
00236                                         break;
00237                                 }
00238                         }
00239 
00240                         // if not, remove it
00241                         if(!found)
00242                         {
00243                                 store_item->removeRow(n);
00244                                 --n; // adjust position
00245                         }
00246                 }
00247 
00248                 // handle added/updated child entries
00249                 for(int n = 0; n < newEntries.count(); ++n)
00250                 {
00251                         const QCA::KeyStoreEntry &ne = newEntries[n];
00252 
00253                         // was this entry in the original list?
00254                         KeyStoreItem *entry_item = 0;
00255                         for(int k = 0; k < store_item->rowCount(); ++k)
00256                         {
00257                                 KeyStoreItem *i = (KeyStoreItem *)store_item->child(k);
00258                                 if(i->keyStoreEntry.id() == ne.id())
00259                                 {
00260                                         entry_item = i;
00261                                         break;
00262                                 }
00263                         }
00264 
00265                         // if not, add it
00266                         if(!entry_item)
00267                         {
00268                                 entry_item = new KeyStoreItem(KeyStoreItem::Entry, &shared);
00269                                 entry_item->keyStoreEntry = ne;
00270                                 entry_item->setEntry(newEntries[n].name(), newEntries[n].type(), newEntries[n].isAvailable(), n);
00271                                 store_item->appendRow(entry_item);
00272                         }
00273                         // if so, update it
00274                         else
00275                         {
00276                                 entry_item->keyStoreEntry = ne;
00277                                 entry_item->setEntry(newEntries[n].name(), newEntries[n].type(), newEntries[n].isAvailable(), n);
00278                         }
00279                 }
00280 
00281                 store_item->sortChildren(0);
00282         }
00283 
00284         void ks_unavailable()
00285         {
00286                 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
00287                 KeyStoreItem *store_item = itemFromStore(ks);
00288                 Q_ASSERT(store_item);
00289 
00290                 store_item->removeRows(0, store_item->rowCount());
00291                 removeRow(store_item->row());
00292                 delete ks;
00293         }
00294 };
00295 
00296 class KeySelectDlg::Private : public QObject
00297 {
00298         Q_OBJECT
00299 public:
00300         KeySelectDlg *q;
00301         Ui_KeySelect ui;
00302         KeyStoreModel *model;
00303         QCA::KeyStoreEntry cur_entry;
00304         QAction *actionView;
00305 
00306         Private(KeySelectDlg *_q) :
00307                 QObject(_q),
00308                 q(_q)
00309         {
00310                 ui.setupUi(q);
00311 
00312                 model = new KeyStoreModel(this);
00313                 connect(&model->ksm, SIGNAL(busyStarted()), SLOT(ksm_busyStarted()));
00314                 connect(&model->ksm, SIGNAL(busyFinished()), SLOT(ksm_busyFinished()));
00315                 if(model->ksm.isBusy())
00316                         ksm_busyStarted();
00317 
00318                 ui.lv_stores->header()->hide();
00319                 ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
00320                 ui.lv_stores->setModel(model);
00321                 ui.lv_stores->setContextMenuPolicy(Qt::CustomContextMenu);
00322                 connect(ui.lv_stores->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(stores_selectionChanged(const QItemSelection &, const QItemSelection &)));
00323                 connect(ui.lv_stores, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(stores_customContextMenuRequested(const QPoint &)));
00324 
00325                 actionView = new QAction(tr("&View"), this);
00326                 connect(actionView, SIGNAL(triggered()), SLOT(view()));
00327                 actionView->setEnabled(false);
00328         }
00329 
00330 private slots:
00331         void ksm_busyStarted()
00332         {
00333                 ui.lb_busy->setText(tr("Looking for devices..."));
00334         }
00335 
00336         void ksm_busyFinished()
00337         {
00338                 ui.lb_busy->setText("");
00339         }
00340 
00341         void stores_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
00342         {
00343                 Q_UNUSED(deselected);
00344 
00345                 KeyStoreItem *i = 0;
00346                 if(!selected.indexes().isEmpty())
00347                 {
00348                         QModelIndex index = selected.indexes().first();
00349                         i = (KeyStoreItem *)model->itemFromIndex(index);
00350                 }
00351 
00352                 bool viewable = false;
00353                 bool choosable = false;
00354                 if(i && i->type() == KeyStoreItem::Entry)
00355                 {
00356                         QCA::KeyStoreEntry entry = i->keyStoreEntry;
00357                         if(entry.type() == QCA::KeyStoreEntry::TypeKeyBundle)
00358                         {
00359                                 viewable = true;
00360                                 choosable = true;
00361                                 cur_entry = entry;
00362                         }
00363                 }
00364 
00365                 if(!choosable)
00366                         cur_entry = QCA::KeyStoreEntry();
00367 
00368                 actionView->setEnabled(viewable);
00369 
00370                 QPushButton *ok = ui.buttonBox->button(QDialogButtonBox::Ok);
00371                 if(choosable && !ok->isEnabled())
00372                         ok->setEnabled(true);
00373                 else if(!choosable && ok->isEnabled())
00374                         ok->setEnabled(false);
00375         }
00376 
00377         void stores_customContextMenuRequested(const QPoint &pos)
00378         {
00379                 QItemSelection selection = ui.lv_stores->selectionModel()->selection();
00380                 if(selection.indexes().isEmpty())
00381                         return;
00382 
00383                 QModelIndex index = selection.indexes().first();
00384                 KeyStoreItem *i = (KeyStoreItem *)model->itemFromIndex(index);
00385                 if(i && i->type() == KeyStoreItem::Entry)
00386                 {
00387                         QMenu menu(q);
00388                         menu.addAction(actionView);
00389                         menu.exec(ui.lv_stores->viewport()->mapToGlobal(pos));
00390                 }
00391         }
00392 
00393         void view()
00394         {
00395                 emit q->viewCertificate(cur_entry.keyBundle().certificateChain());
00396         }
00397 };
00398 
00399 KeySelectDlg::KeySelectDlg(QWidget *parent) :
00400         QDialog(parent)
00401 {
00402         d = new Private(this);
00403 }
00404 
00405 KeySelectDlg::~KeySelectDlg()
00406 {
00407         delete d;
00408 }
00409 
00410 void KeySelectDlg::setIcon(IconType type, const QPixmap &icon)
00411 {
00412         d->model->shared.iconset[type] = icon;
00413 }
00414 
00415 void KeySelectDlg::accept()
00416 {
00417         QCA::KeyStoreEntry entry = d->cur_entry;
00418         QDialog::accept();
00419         emit selected(entry);
00420 }
00421 
00422 #include "keyselectdlg.moc"
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #ifndef PROMPTER_H
00023 #define PROMPTER_H
00024 
00025 #include <QObject>
00026 
00027 namespace QCA
00028 {
00029         class SecureArray;
00030         class Event;
00031 }
00032 
00033 class Prompter : public QObject
00034 {
00035         Q_OBJECT
00036 public:
00037         Prompter(QObject *parent = 0);
00038         ~Prompter();
00039 
00040 protected:
00041         // called with every password event, to check for a known value.
00042         //   reimplement it to provide known/cached passwords.
00043         virtual QCA::SecureArray knownPassword(const QCA::Event &event);
00044 
00045         // called when a user-entered password is submitted.  note that this
00046         //   does not mean the password was correct.  to know if the password
00047         //   was correct, you'll have to match up the event information with
00048         //   the operation that triggered it.
00049         virtual void userSubmitted(const QCA::SecureArray &password, const QCA::Event &event);
00050 
00051 private:
00052         class Private;
00053         friend class Private;
00054         Private *d;
00055 };
00056 
00057 #endif
00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #include "prompter.h"
00023 
00024 #include <QtCore>
00025 #include <QtGui>
00026 #include <QtCrypto>
00027 
00028 class Prompter::Private : public QObject
00029 {
00030         Q_OBJECT
00031 public:
00032         Prompter *q;
00033 
00034         class Item
00035         {
00036         public:
00037                 int id;
00038                 QCA::Event event;
00039         };
00040 
00041         QCA::EventHandler handler;
00042         QList<Item> pending;
00043         bool prompting;
00044         QMessageBox *token_prompt;
00045         bool auto_accept;
00046 
00047         QCA::KeyStoreManager ksm;
00048         QList<QCA::KeyStore*> keyStores;
00049 
00050         Private(Prompter *_q) :
00051                 QObject(_q),
00052                 q(_q),
00053                 handler(this),
00054                 prompting(false),
00055                 token_prompt(0),
00056                 ksm(this)
00057         {
00058                 connect(&handler, SIGNAL(eventReady(int, const QCA::Event &)), SLOT(ph_eventReady(int, const QCA::Event &)));
00059                 handler.start();
00060 
00061                 connect(&ksm, SIGNAL(keyStoreAvailable(const QString &)), SLOT(ks_available(const QString &)));
00062                 foreach(const QString &keyStoreId, ksm.keyStores())
00063                         ks_available(keyStoreId);
00064         }
00065 
00066         ~Private()
00067         {
00068                 qDeleteAll(keyStores);
00069 
00070                 while(!pending.isEmpty())
00071                         handler.reject(pending.takeFirst().id);
00072         }
00073 
00074 private slots:
00075         void ph_eventReady(int id, const QCA::Event &event)
00076         {
00077                 Item i;
00078                 i.id = id;
00079                 i.event = event;
00080                 pending += i;
00081                 nextEvent();
00082         }
00083 
00084         void nextEvent()
00085         {
00086                 if(prompting || pending.isEmpty())
00087                         return;
00088 
00089                 prompting = true;
00090 
00091                 const Item &i = pending.first();
00092                 const int &id = i.id;
00093                 const QCA::Event &event = i.event;
00094 
00095                 if(event.type() == QCA::Event::Password)
00096                 {
00097                         QCA::SecureArray known = q->knownPassword(event);
00098                         if(!known.isEmpty())
00099                         {
00100                                 handler.submitPassword(id, known);
00101                                 goto end;
00102                         }
00103 
00104                         QString type = Prompter::tr("password");
00105                         if(event.passwordStyle() == QCA::Event::StylePassphrase)
00106                                 type = Prompter::tr("passphrase");
00107                         else if(event.passwordStyle() == QCA::Event::StylePIN)
00108                                 type = Prompter::tr("PIN");
00109 
00110                         QString str;
00111                         if(event.source() == QCA::Event::KeyStore)
00112                         {
00113                                 QString name;
00114                                 QCA::KeyStoreEntry entry = event.keyStoreEntry();
00115                                 if(!entry.isNull())
00116                                 {
00117                                         name = entry.name();
00118                                 }
00119                                 else
00120                                 {
00121                                         if(event.keyStoreInfo().type() == QCA::KeyStore::SmartCard)
00122                                                 name = Prompter::tr("the '%1' token").arg(event.keyStoreInfo().name());
00123                                         else
00124                                                 name = event.keyStoreInfo().name();
00125                                 }
00126                                 str = Prompter::tr("Enter %1 for %2").arg(type, name);
00127                         }
00128                         else if(!event.fileName().isEmpty())
00129                         {
00130                                 QFileInfo fi(event.fileName());
00131                                 str = Prompter::tr("Enter %1 for %2:").arg(type, fi.fileName());
00132                         }
00133                         else
00134                                 str = Prompter::tr("Enter %1:").arg(type);
00135 
00136                         bool ok;
00137                         QString pass = QInputDialog::getText(0, QApplication::instance()->applicationName() + ": " + tr("Prompt"), str, QLineEdit::Password, QString(), &ok);
00138                         if(ok)
00139                         {
00140                                 QCA::SecureArray password = pass.toUtf8();
00141                                 q->userSubmitted(password, event);
00142                                 handler.submitPassword(id, password);
00143                         }
00144                         else
00145                                 handler.reject(id);
00146                 }
00147                 else if(event.type() == QCA::Event::Token)
00148                 {
00149                         // even though we're being prompted for a missing token,
00150                         //   we should still check if the token is present, due to
00151                         //   a possible race between insert and token request.
00152                         bool found = false;
00153 
00154                         // token-only
00155                         if(event.keyStoreEntry().isNull())
00156                         {
00157                                 foreach(QCA::KeyStore *ks, keyStores)
00158                                 {
00159                                         if(ks->id() == event.keyStoreInfo().id())
00160                                         {
00161                                                 found = true;
00162                                                 break;
00163                                         }
00164                                 }
00165                         }
00166                         // token-entry
00167                         else
00168                         {
00169                                 QCA::KeyStoreEntry kse = event.keyStoreEntry();
00170 
00171                                 QCA::KeyStore *ks = 0;
00172                                 foreach(QCA::KeyStore *i, keyStores)
00173                                 {
00174                                         if(i->id() == event.keyStoreInfo().id())
00175                                         {
00176                                                 ks = i;
00177                                                 break;
00178                                         }
00179                                 }
00180                                 if(ks)
00181                                 {
00182                                         QList<QCA::KeyStoreEntry> list = ks->entryList();
00183                                         foreach(const QCA::KeyStoreEntry &e, list)
00184                                         {
00185                                                 if(e.id() == kse.id() && kse.isAvailable())
00186                                                 {
00187                                                         found = true;
00188                                                         break;
00189                                                 }
00190                                         }
00191                                 }
00192                         }
00193                         if(found)
00194                         {
00195                                 // auto-accept
00196                                 handler.tokenOkay(id);
00197                                 return;
00198                         }
00199 
00200                         QCA::KeyStoreEntry entry = event.keyStoreEntry();
00201                         QString name;
00202                         if(!entry.isNull())
00203                         {
00204                                 name = Prompter::tr("Please make %1 (of %2) available").arg(entry.name(), entry.storeName());
00205                         }
00206                         else
00207                         {
00208                                 name = Prompter::tr("Please insert the '%1' token").arg(event.keyStoreInfo().name());
00209                         }
00210 
00211                         QString str = Prompter::tr("%1 and click OK.").arg(name);
00212 
00213                         QMessageBox msgBox(QMessageBox::Information, QApplication::instance()->applicationName() + ": " + tr("Prompt"), str, QMessageBox::Ok | QMessageBox::Cancel, 0);
00214                         token_prompt = &msgBox;
00215                         auto_accept = false;
00216                         if(msgBox.exec() == QMessageBox::Ok || auto_accept)
00217                                 handler.tokenOkay(id);
00218                         else
00219                                 handler.reject(id);
00220                         token_prompt = 0;
00221                 }
00222                 else
00223                         handler.reject(id);
00224 
00225         end:
00226                 pending.removeFirst();
00227                 prompting = false;
00228 
00229                 if(!pending.isEmpty())
00230                         QMetaObject::invokeMethod(this, "nextEvent", Qt::QueuedConnection);
00231         }
00232 
00233         void ks_available(const QString &keyStoreId)
00234         {
00235                 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm);
00236                 connect(ks, SIGNAL(updated()), SLOT(ks_updated()));
00237                 connect(ks, SIGNAL(unavailable()), SLOT(ks_unavailable()));
00238                 keyStores += ks;
00239                 ks->startAsynchronousMode();
00240 
00241                 // are we currently in a token-only prompt?
00242                 if(token_prompt && pending.first().event.type() == QCA::Event::Token && pending.first().event.keyStoreEntry().isNull())
00243                 {
00244                         // was the token we're looking for just inserted?
00245                         if(pending.first().event.keyStoreInfo().id() == keyStoreId)
00246                         {
00247                                 // auto-accept
00248                                 auto_accept = true;
00249                                 token_prompt->accept();
00250                         }
00251                 }
00252         }
00253 
00254         void ks_unavailable()
00255         {
00256                 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
00257                 keyStores.removeAll(ks);
00258                 delete ks;
00259         }
00260 
00261         void ks_updated()
00262         {
00263                 QCA::KeyStore *ks = (QCA::KeyStore *)sender();
00264 
00265                 // are we currently in a token-entry prompt?
00266                 if(token_prompt && pending.first().event.type() == QCA::Event::Token && !pending.first().event.keyStoreEntry().isNull())
00267                 {
00268                         QCA::KeyStoreEntry kse = pending.first().event.keyStoreEntry();
00269 
00270                         // was the token of the entry we're looking for updated?
00271                         if(pending.first().event.keyStoreInfo().id() == ks->id())
00272                         {
00273                                 // is the entry available?
00274                                 bool avail = false;
00275                                 QList<QCA::KeyStoreEntry> list = ks->entryList();
00276                                 foreach(const QCA::KeyStoreEntry &e, list)
00277                                 {
00278                                         if(e.id() == kse.id())
00279                                         {
00280                                                 avail = kse.isAvailable();
00281                                                 break;
00282                                         }
00283                                 }
00284                                 if(avail)
00285                                 {
00286                                         // auto-accept
00287                                         auto_accept = true;
00288                                         token_prompt->accept();
00289                                 }
00290                         }
00291                 }
00292         }
00293 };
00294 
00295 Prompter::Prompter(QObject *parent) :
00296         QObject(parent)
00297 {
00298         d = new Private(this);
00299 }
00300 
00301 Prompter::~Prompter()
00302 {
00303         delete d;
00304 }
00305 
00306 QCA::SecureArray Prompter::knownPassword(const QCA::Event &event)
00307 {
00308         Q_UNUSED(event);
00309         return QCA::SecureArray();
00310 }
00311 
00312 void Prompter::userSubmitted(const QCA::SecureArray &password, const QCA::Event &event)
00313 {
00314         Q_UNUSED(password);
00315         Q_UNUSED(event);
00316 }
00317 
00318 #include "prompter.moc"

00001 /*
00002  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #include <QtCore>
00023 #include <QtGui>
00024 #include <QtCrypto>
00025 
00026 #include "ui_mainwin.h"
00027 #include "certviewdlg.h"
00028 #include "keyselectdlg.h"
00029 #include "pkcs11configdlg/pkcs11configdlg.h"
00030 #include "certitem.h"
00031 
00032 #define VERSION "1.0.0"
00033 
00034 class Icons
00035 {
00036 public:
00037         QPixmap cert, crl, keybundle, pgppub, pgpsec;
00038 };
00039 
00040 Icons *g_icons = 0;
00041 
00042 //----------------------------------------------------------------------------
00043 // Operation
00044 //----------------------------------------------------------------------------
00045 class Operation : public QObject
00046 {
00047         Q_OBJECT
00048 public:
00049         Operation(QObject *parent = 0) :
00050                 QObject(parent)
00051         {
00052         }
00053 
00054 signals:
00055         void error(const QString &str);
00056 };
00057 
00058 static QString validityToString(QCA::Validity v)
00059 {
00060         QString s;
00061         switch(v)
00062         {
00063                 case QCA::ValidityGood:
00064                         s = Operation::tr("Validated");
00065                         break;
00066                 case QCA::ErrorRejected:
00067                         s = Operation::tr("Root CA is marked to reject the specified purpose");
00068                         break;
00069                 case QCA::ErrorUntrusted:
00070                         s = Operation::tr("Certificate not trusted for the required purpose");
00071                         break;
00072                 case QCA::ErrorSignatureFailed:
00073                         s = Operation::tr("Invalid signature");
00074                         break;
00075                 case QCA::ErrorInvalidCA:
00076                         s = Operation::tr("Invalid CA certificate");
00077                         break;
00078                 case QCA::ErrorInvalidPurpose:
00079                         s = Operation::tr("Invalid certificate purpose");
00080                         break;
00081                 case QCA::ErrorSelfSigned:
00082                         s = Operation::tr("Certificate is self-signed");
00083                         break;
00084                 case QCA::ErrorRevoked:
00085                         s = Operation::tr("Certificate has been revoked");
00086                         break;
00087                 case QCA::ErrorPathLengthExceeded:
00088                         s = Operation::tr("Maximum certificate chain length exceeded");
00089                         break;
00090                 case QCA::ErrorExpired:
00091                         s = Operation::tr("Certificate has expired");
00092                         break;
00093                 case QCA::ErrorExpiredCA:
00094                         s = Operation::tr("CA has expired");
00095                         break;
00096                 case QCA::ErrorValidityUnknown:
00097                 default:
00098                         s = Operation::tr("General certificate validation error");
00099                         break;
00100         }
00101         return s;
00102 }
00103 
00104 static QString smErrorToString(QCA::SecureMessage::Error e)
00105 {
00106         QString s;
00107         switch(e)
00108         {
00109                 case QCA::SecureMessage::ErrorPassphrase:
00110                         s = Operation::tr("Invalid passphrase.");
00111                         break;
00112                 case QCA::SecureMessage::ErrorFormat:
00113                         s = Operation::tr("Bad input format.");
00114                         break;
00115                 case QCA::SecureMessage::ErrorSignerExpired:
00116                         s = Operation::tr("Signer key is expired.");
00117                         break;
00118                 case QCA::SecureMessage::ErrorSignerInvalid:
00119                         s = Operation::tr("Signer key is invalid.");
00120                         break;
00121                 case QCA::SecureMessage::ErrorEncryptExpired:
00122                         s = Operation::tr("Encrypting key is expired.");
00123                         break;
00124                 case QCA::SecureMessage::ErrorEncryptUntrusted:
00125                         s = Operation::tr("Encrypting key is untrusted.");
00126                         break;
00127                 case QCA::SecureMessage::ErrorEncryptInvalid:
00128                         s = Operation::tr("Encrypting key is invalid.");
00129                         break;
00130                 case QCA::SecureMessage::ErrorNeedCard:
00131                         s = Operation::tr("Card was needed but not found.");
00132                         break;
00133                 case QCA::SecureMessage::ErrorCertKeyMismatch:
00134                         s = Operation::tr("Certificate and private key don't match.");
00135                         break;
00136                 case QCA::SecureMessage::ErrorUnknown:
00137                 default:
00138                         s = Operation::tr("General error.");
00139                         break;
00140         }
00141         return s;
00142 }
00143 
00144 static QString smsIdentityToString(const QCA::SecureMessageSignature &sig)
00145 {
00146         QString s;
00147         switch(sig.identityResult())
00148         {
00149                 case QCA::SecureMessageSignature::Valid:
00150                         break;
00151                 case QCA::SecureMessageSignature::InvalidSignature:
00152                         s = Operation::tr("Invalid signature");
00153                         break;
00154                 case QCA::SecureMessageSignature::InvalidKey:
00155                         s = Operation::tr("Invalid key: %1").arg(validityToString(sig.keyValidity()));
00156                         break;
00157                 case QCA::SecureMessageSignature::NoKey:
00158                         s = Operation::tr("Key not found");
00159                         break;
00160                 default: // this should not really be possible
00161                         s = Operation::tr("Unknown");
00162                         break;
00163         }
00164         return s;
00165 }
00166 
00167 class SignOperation : public Operation
00168 {
00169         Q_OBJECT
00170 private:
00171         QByteArray in;
00172         CertItemStore *store;
00173         int id;
00174         QCA::CMS *cms;
00175         CertItemPrivateLoader *loader;
00176         QCA::SecureMessage *msg;
00177         int pending;
00178 
00179 public:
00180         SignOperation(const QByteArray &_in, CertItemStore *_store, int _id, QCA::CMS *_cms, QObject *parent = 0) :
00181                 Operation(parent),
00182                 in(_in),
00183                 store(_store),
00184                 id(_id),
00185                 cms(_cms),
00186                 msg(0)
00187         {
00188                 loader = new CertItemPrivateLoader(store, this);
00189                 connect(loader, SIGNAL(finished()), SLOT(loader_finished()));
00190                 loader->start(id);
00191         }
00192 
00193 signals:
00194         void loadError();
00195         void finished(const QString &sig);
00196 
00197 private slots:
00198         void loader_finished()
00199         {
00200                 QCA::PrivateKey privateKey = loader->privateKey();
00201                 delete loader;
00202                 loader = 0;
00203 
00204                 if(privateKey.isNull())
00205                 {
00206                         emit loadError();
00207                         return;
00208                 }
00209 
00210                 CertItem item = store->itemFromId(id);
00211 
00212                 QCA::SecureMessageKey signer;
00213                 signer.setX509CertificateChain(item.certificateChain());
00214                 signer.setX509PrivateKey(privateKey);
00215 
00216                 msg = new QCA::SecureMessage(cms);
00217                 connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
00218                 connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
00219                 msg->setFormat(QCA::SecureMessage::Ascii);
00220                 msg->setSigner(signer);
00221                 msg->startSign(QCA::SecureMessage::Detached);
00222 
00223                 pending = 0;
00224                 update();
00225         }
00226 
00227         void update()
00228         {
00229                 QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
00230                 in = in.mid(buf.size());
00231                 pending += buf.size();
00232                 msg->update(buf);
00233         }
00234 
00235         void msg_bytesWritten(int x)
00236         {
00237                 pending -= x;
00238 
00239                 if(in.isEmpty() && pending == 0)
00240                         msg->end();
00241                 else
00242                         update();
00243         }
00244 
00245         void msg_finished()
00246         {
00247                 if(!msg->success())
00248                 {
00249                         QString str = smErrorToString(msg->errorCode());
00250                         delete msg;
00251                         msg = 0;
00252                         emit error(tr("Error during sign operation.\nReason: %1").arg(str));
00253                         return;
00254                 }
00255 
00256                 QByteArray result = msg->signature();
00257                 delete msg;
00258                 msg = 0;
00259                 emit finished(QString::fromLatin1(result));
00260         }
00261 };
00262 
00263 class VerifyOperation : public Operation
00264 {
00265         Q_OBJECT
00266 private:
00267         QByteArray in, sig;
00268         QCA::CMS *cms;
00269         QCA::SecureMessage *msg;
00270         int pending;
00271 
00272 public:
00273         QCA::SecureMessageSignature signer;
00274 
00275         VerifyOperation(const QByteArray &_in, const QByteArray &_sig, QCA::CMS *_cms, QObject *parent = 0) :
00276                 Operation(parent),
00277                 in(_in),
00278                 sig(_sig),
00279                 cms(_cms),
00280                 msg(0)
00281         {
00282                 msg = new QCA::SecureMessage(cms);
00283                 connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
00284                 connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
00285                 msg->setFormat(QCA::SecureMessage::Ascii);
00286                 msg->startVerify(sig);
00287 
00288                 pending = 0;
00289                 update();
00290         }
00291 
00292 signals:
00293         void finished();
00294 
00295 private slots:
00296         void update()
00297         {
00298                 QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
00299                 in = in.mid(buf.size());
00300                 pending += buf.size();
00301                 msg->update(buf);
00302         }
00303 
00304         void msg_bytesWritten(int x)
00305         {
00306                 pending -= x;
00307 
00308                 if(in.isEmpty() && pending == 0)
00309                         msg->end();
00310                 else
00311                         update();
00312         }
00313 
00314         void msg_finished()
00315         {
00316                 if(!msg->success())
00317                 {
00318                         QString str = smErrorToString(msg->errorCode());
00319                         delete msg;
00320                         msg = 0;
00321                         emit error(tr("Error during verify operation.\nReason: %1").arg(str));
00322                         return;
00323                 }
00324 
00325                 signer = msg->signer();
00326                 delete msg;
00327                 msg = 0;
00328 
00329                 if(signer.identityResult() != QCA::SecureMessageSignature::Valid)
00330                 {
00331                         QString str = smsIdentityToString(signer);
00332                         emit error(tr("Verification failed!\nReason: %1").arg(str));
00333                         return;
00334                 }
00335 
00336                 emit finished();
00337         }
00338 };
00339 
00340 //----------------------------------------------------------------------------
00341 // MainWin
00342 //----------------------------------------------------------------------------
00343 static QString get_fingerprint(const QCA::Certificate &cert)
00344 {
00345         QString hex = QCA::Hash("sha1").hashToString(cert.toDER());
00346         QString out;
00347         for(int n = 0; n < hex.count(); ++n)
00348         {
00349                 if(n != 0 && n % 2 == 0)
00350                         out += ':';
00351                 out += hex[n];
00352         }
00353         return out;
00354 }
00355 
00356 class MainWin : public QMainWindow
00357 {
00358         Q_OBJECT
00359 private:
00360         Ui_MainWin ui;
00361         CertItemStore *users, *roots;
00362         QCA::CMS *cms;
00363         Operation *op;
00364         QAction *actionView, *actionRename, *actionRemove;
00365         QCA::Certificate self_signed_verify_cert;
00366         int auto_import_req_id;
00367 
00368 public:
00369         MainWin(QWidget *parent = 0) :
00370                 QMainWindow(parent),
00371                 op(0),
00372                 auto_import_req_id(-1)
00373         {
00374                 ui.setupUi(this);
00375 
00376                 g_icons = new Icons;
00377                 g_icons->cert = QPixmap(":/gfx/icons/cert16.png");
00378                 g_icons->crl = QPixmap(":/gfx/icons/crl16.png");
00379                 g_icons->keybundle = QPixmap(":/gfx/icons/keybundle16.png");
00380                 g_icons->pgppub = QPixmap(":/gfx/icons/publickey16.png");
00381                 g_icons->pgpsec = QPixmap(":/gfx/icons/keypair16.png");
00382                 if(g_icons->cert.isNull() || g_icons->crl.isNull() || g_icons->keybundle.isNull() || g_icons->pgppub.isNull() || g_icons->pgpsec.isNull())
00383                         printf("Warning: not all icons loaded\n");
00384 
00385                 users = new CertItemStore(this);
00386                 roots = new CertItemStore(this);
00387 
00388                 setIcons(users);
00389                 setIcons(roots);
00390 
00391                 connect(users, SIGNAL(addSuccess(int, int)), SLOT(users_addSuccess(int, int)));
00392                 connect(users, SIGNAL(addFailed(int)), SLOT(users_addFailed(int)));
00393 
00394                 actionView = new QAction(tr("&View"), this);
00395                 actionRename = new QAction(tr("Re&name"), this);
00396                 actionRemove = new QAction(tr("Rem&ove"), this);
00397 
00398                 connect(ui.actionLoadIdentityFile, SIGNAL(triggered()), SLOT(load_file()));
00399                 connect(ui.actionLoadIdentityEntry, SIGNAL(triggered()), SLOT(load_device()));
00400                 connect(ui.actionLoadAuthority, SIGNAL(triggered()), SLOT(load_root()));
00401                 connect(ui.actionConfigurePkcs11, SIGNAL(triggered()), SLOT(mod_config()));
00402                 connect(ui.actionQuit, SIGNAL(triggered()), SLOT(close()));
00403                 connect(ui.actionAbout, SIGNAL(triggered()), SLOT(about()));
00404                 connect(ui.pb_sign, SIGNAL(clicked()), SLOT(do_sign()));
00405                 connect(ui.pb_verify, SIGNAL(clicked()), SLOT(do_verify()));
00406 
00407                 connect(actionView, SIGNAL(triggered()), SLOT(item_view()));
00408                 connect(actionRename, SIGNAL(triggered()), SLOT(item_rename()));
00409                 connect(actionRemove, SIGNAL(triggered()), SLOT(item_remove()));
00410 
00411                 ui.pb_sign->setEnabled(false);
00412 
00413                 ui.lv_users->setModel(users);
00414                 connect(ui.lv_users->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(users_selectionChanged(const QItemSelection &, const QItemSelection &)));
00415 
00416                 ui.lv_users->setContextMenuPolicy(Qt::CustomContextMenu);
00417                 connect(ui.lv_users, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(users_customContextMenuRequested(const QPoint &)));
00418 
00419                 ui.lv_authorities->setModel(roots);
00420 
00421                 ui.lv_authorities->setContextMenuPolicy(Qt::CustomContextMenu);
00422                 connect(ui.lv_authorities, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(roots_customContextMenuRequested(const QPoint &)));
00423 
00424                 cms = new QCA::CMS(this);
00425 
00426                 QStringList ulist, rlist;
00427                 {
00428                         QSettings settings("Affinix", "CMS Signer");
00429                         ulist = settings.value("users").toStringList();
00430                         rlist = settings.value("roots").toStringList();
00431                 }
00432 
00433                 users->load(ulist);
00434                 roots->load(rlist);
00435         }
00436 
00437         ~MainWin()
00438         {
00439                 QStringList ulist = users->save();
00440                 QStringList rlist = roots->save();
00441 
00442                 QSettings settings("Affinix", "CMS Signer");
00443                 settings.setValue("users", ulist);
00444                 settings.setValue("roots", rlist);
00445 
00446                 delete g_icons;
00447                 g_icons = 0;
00448         }
00449 
00450         void setIcons(CertItemStore *store)
00451         {
00452                 store->setIcon(CertItemStore::IconCert, g_icons->cert);
00453                 store->setIcon(CertItemStore::IconCrl, g_icons->crl);
00454                 store->setIcon(CertItemStore::IconKeyBundle, g_icons->keybundle);
00455                 store->setIcon(CertItemStore::IconPgpPub, g_icons->pgppub);
00456                 store->setIcon(CertItemStore::IconPgpSec, g_icons->pgpsec);
00457         }
00458 
00459         QCA::CertificateCollection allCerts()
00460         {
00461                 QCA::CertificateCollection col;
00462 
00463                 // system store
00464                 col += QCA::systemStore();
00465 
00466                 // additional roots configured in application
00467                 foreach(const CertItem &i, roots->items())
00468                         col.addCertificate(i.certificateChain().primary());
00469 
00470                 // user chains
00471                 foreach(const CertItem &i, users->items())
00472                 {
00473                         foreach(const QCA::Certificate &cert, i.certificateChain())
00474                                 col.addCertificate(cert);
00475                 }
00476 
00477                 return col;
00478         }
00479 
00480         QCA::CertificateChain complete(const QCA::CertificateChain &chain)
00481         {
00482                 return chain.complete(allCerts().certificates());
00483         }
00484 
00485 private slots:
00486         void load_file()
00487         {
00488                 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Identities (*.p12 *.pfx)"));
00489                 if(fileName.isEmpty())
00490                         return;
00491 
00492                 setEnabled(false);
00493                 users->addFromFile(fileName);
00494         }
00495 
00496         void load_device()
00497         {
00498                 KeySelectDlg *w = new KeySelectDlg(this);
00499                 w->setAttribute(Qt::WA_DeleteOnClose, true);
00500                 w->setWindowModality(Qt::WindowModal);
00501                 connect(w, SIGNAL(selected(const QCA::KeyStoreEntry &)), SLOT(load_device_finished(const QCA::KeyStoreEntry &)));
00502                 connect(w, SIGNAL(viewCertificate(const QCA::CertificateChain &)), SLOT(keyselect_viewCertificate(const QCA::CertificateChain &)));
00503                 w->setIcon(KeySelectDlg::IconCert, g_icons->cert);
00504                 w->setIcon(KeySelectDlg::IconCrl, g_icons->crl);
00505                 w->setIcon(KeySelectDlg::IconKeyBundle, g_icons->keybundle);
00506                 w->setIcon(KeySelectDlg::IconPgpPub, g_icons->pgppub);
00507                 w->setIcon(KeySelectDlg::IconPgpSec, g_icons->pgpsec);
00508                 w->show();
00509         }
00510 
00511         void load_device_finished(const QCA::KeyStoreEntry &entry)
00512         {
00513                 users->addFromKeyStore(entry);
00514         }
00515 
00516         void load_root()
00517         {
00518                 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Certificates (*.pem *.crt)"));
00519                 if(fileName.isEmpty())
00520                         return;
00521 
00522                 QCA::Certificate cert = QCA::Certificate::fromPEMFile(fileName);
00523                 if(cert.isNull())
00524                 {
00525                         QMessageBox::information(this, tr("Error"), tr("Error opening certificate file."));
00526                         return;
00527                 }
00528 
00529                 roots->addUser(cert);
00530         }
00531 
00532         void users_addSuccess(int req_id, int id)
00533         {
00534                 if(req_id == auto_import_req_id)
00535                 {
00536                         auto_import_req_id = -1;
00537 
00538                         CertItem i = users->itemFromId(id);
00539 
00540                         QMessageBox::information(this, tr("User added"), tr(
00541                                 "This signature was made by a previously unknown user, and so the "
00542                                 "user has now been added to the keyring as \"%1\"."
00543                                 ).arg(i.name()));
00544 
00545                         verify_next();
00546                         return;
00547                 }
00548 
00549                 ui.lv_users->selectionModel()->select(users->index(users->rowFromId(id)), QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
00550 
00551                 setEnabled(true);
00552         }
00553 
00554         void users_addFailed(int req_id)
00555         {
00556                 Q_UNUSED(req_id);
00557 
00558                 setEnabled(true);
00559         }
00560 
00561         void mod_config()
00562         {
00563                 if(!Pkcs11ConfigDlg::isSupported())
00564                 {
00565                         QMessageBox::information(this, tr("Error"), tr("No provider available supporting standard PKCS#11 configuration."));
00566                         return;
00567                 }
00568 
00569                 Pkcs11ConfigDlg *w = new Pkcs11ConfigDlg(this);
00570                 w->setAttribute(Qt::WA_DeleteOnClose, true);
00571                 w->setWindowModality(Qt::WindowModal);
00572                 w->show();
00573         }
00574 
00575         void users_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
00576         {
00577                 Q_UNUSED(deselected);
00578 
00579                 int at = -1;
00580                 if(!selected.indexes().isEmpty())
00581                 {
00582                         QModelIndex index = selected.indexes().first();
00583                         at = index.row();
00584                 }
00585 
00586                 bool usable = false;
00587                 if(at != -1 && users->itemFromRow(at).isUsable())
00588                         usable = true;
00589 
00590                 if(usable && !ui.pb_sign->isEnabled())
00591                         ui.pb_sign->setEnabled(true);
00592                 else if(!usable && ui.pb_sign->isEnabled())
00593                         ui.pb_sign->setEnabled(false);
00594         }
00595 
00596         void item_view()
00597         {
00598                 if(ui.lv_users->hasFocus())
00599                 {
00600                         QItemSelection selection = ui.lv_users->selectionModel()->selection();
00601                         if(selection.indexes().isEmpty())
00602                                 return;
00603                         QModelIndex index = selection.indexes().first();
00604                         users_view(index.row());
00605                 }
00606                 else // lv_authorities
00607                 {
00608                         QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
00609                         if(selection.indexes().isEmpty())
00610                                 return;
00611                         QModelIndex index = selection.indexes().first();
00612                         roots_view(index.row());
00613                 }
00614         }
00615 
00616         void item_rename()
00617         {
00618                 if(ui.lv_users->hasFocus())
00619                 {
00620                         QItemSelection selection = ui.lv_users->selectionModel()->selection();
00621                         if(selection.indexes().isEmpty())
00622                                 return;
00623                         QModelIndex index = selection.indexes().first();
00624                         users_rename(index.row());
00625                 }
00626                 else // lv_authorities
00627                 {
00628                         QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
00629                         if(selection.indexes().isEmpty())
00630                                 return;
00631                         QModelIndex index = selection.indexes().first();
00632                         roots_rename(index.row());
00633                 }
00634         }
00635 
00636         void item_remove()
00637         {
00638                 if(ui.lv_users->hasFocus())
00639                 {
00640                         QItemSelection selection = ui.lv_users->selectionModel()->selection();
00641                         if(selection.indexes().isEmpty())
00642                                 return;
00643                         QModelIndex index = selection.indexes().first();
00644                         users_remove(index.row());
00645                 }
00646                 else // lv_authorities
00647                 {
00648                         QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
00649                         if(selection.indexes().isEmpty())
00650                                 return;
00651                         QModelIndex index = selection.indexes().first();
00652                         roots_remove(index.row());
00653                 }
00654         }
00655 
00656         void users_view(int at)
00657         {
00658                 CertItem i = users->itemFromRow(at);
00659                 CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
00660                 w->setAttribute(Qt::WA_DeleteOnClose, true);
00661                 w->show();
00662         }
00663 
00664         void users_rename(int at)
00665         {
00666                 QModelIndex index = users->index(at);
00667                 ui.lv_users->setFocus();
00668                 ui.lv_users->setCurrentIndex(index);
00669                 ui.lv_users->selectionModel()->select(index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
00670                 ui.lv_users->edit(index);
00671         }
00672 
00673         void users_remove(int at)
00674         {
00675                 users->removeItem(users->idFromRow(at));
00676         }
00677 
00678         void roots_view(int at)
00679         {
00680                 CertItem i = roots->itemFromRow(at);
00681                 CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
00682                 w->setAttribute(Qt::WA_DeleteOnClose, true);
00683                 w->show();
00684         }
00685 
00686         void roots_rename(int at)
00687         {
00688                 QModelIndex index = roots->index(at);
00689                 ui.lv_authorities->setFocus();
00690                 ui.lv_authorities->setCurrentIndex(index);
00691                 ui.lv_authorities->selectionModel()->select(index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
00692                 ui.lv_authorities->edit(index);
00693         }
00694 
00695         void roots_remove(int at)
00696         {
00697                 roots->removeItem(roots->idFromRow(at));
00698         }
00699 
00700         void keyselect_viewCertificate(const QCA::CertificateChain &chain)
00701         {
00702                 CertViewDlg *w = new CertViewDlg(complete(chain), (QWidget *)sender());
00703                 w->setAttribute(Qt::WA_DeleteOnClose, true);
00704                 w->show();
00705         }
00706 
00707         void users_customContextMenuRequested(const QPoint &pos)
00708         {
00709                 QItemSelection selection = ui.lv_users->selectionModel()->selection();
00710                 if(selection.indexes().isEmpty())
00711                         return;
00712 
00713                 QMenu menu(this);
00714                 menu.addAction(actionView);
00715                 menu.addAction(actionRename);
00716                 menu.addAction(actionRemove);
00717                 menu.exec(ui.lv_users->viewport()->mapToGlobal(pos));
00718         }
00719 
00720         void roots_customContextMenuRequested(const QPoint &pos)
00721         {
00722                 QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
00723                 if(selection.indexes().isEmpty())
00724                         return;
00725 
00726                 QMenu menu(this);
00727                 menu.addAction(actionView);
00728                 menu.addAction(actionRename);
00729                 menu.addAction(actionRemove);
00730                 menu.exec(ui.lv_authorities->viewport()->mapToGlobal(pos));
00731         }
00732 
00733         void do_sign()
00734         {
00735                 QItemSelection selection = ui.lv_users->selectionModel()->selection();
00736                 if(selection.indexes().isEmpty())
00737                         return;
00738                 QModelIndex index = selection.indexes().first();
00739                 int at = index.row();
00740 
00741                 setEnabled(false);
00742 
00743                 op = new SignOperation(ui.te_data->toPlainText().toUtf8(), users, users->idFromRow(at), cms, this);
00744                 connect(op, SIGNAL(loadError()), SLOT(sign_loadError()));
00745                 connect(op, SIGNAL(finished(const QString &)), SLOT(sign_finished(const QString &)));
00746                 connect(op, SIGNAL(error(const QString &)), SLOT(sign_error(const QString &)));
00747         }
00748 
00749         void do_verify()
00750         {
00751                 // prepare root certs
00752                 QCA::CertificateCollection col;
00753 
00754                 // system store
00755                 col += QCA::systemStore();
00756 
00757                 // additional roots configured in application
00758                 foreach(const CertItem &i, roots->items())
00759                         col.addCertificate(i.certificateChain().primary());
00760 
00761                 // consider self-signed users as roots
00762                 // (it is therefore not possible with this application to
00763                 // have people in your keyring that you don't trust)
00764                 foreach(const CertItem &i, users->items())
00765                 {
00766                         QCA::Certificate cert = i.certificateChain().primary();
00767                         if(cert.isSelfSigned())
00768                                 col.addCertificate(cert);
00769                 }
00770 
00771                 // the self signed verify cert, if applicable
00772                 if(!self_signed_verify_cert.isNull())
00773                 {
00774                         col.addCertificate(self_signed_verify_cert);
00775                         self_signed_verify_cert = QCA::Certificate();
00776                 }
00777 
00778                 cms->setTrustedCertificates(col);
00779 
00780                 setEnabled(false);
00781 
00782                 op = new VerifyOperation(ui.te_data->toPlainText().toUtf8(), ui.te_sig->toPlainText().toUtf8(), cms, this);
00783                 connect(op, SIGNAL(finished()), SLOT(verify_finished()));
00784                 connect(op, SIGNAL(error(const QString &)), SLOT(verify_error(const QString &)));
00785         }
00786 
00787         void about()
00788         {
00789                 int ver = qcaVersion();
00790                 int maj = (ver >> 16) & 0xff;
00791                 int min = (ver >> 8) & 0xff;
00792                 int bug = ver & 0xff;
00793                 QString verstr;
00794                 verstr.sprintf("%d.%d.%d", maj, min, bug);
00795 
00796                 QString str;
00797                 str += tr("CMS Signer version %1 by Justin Karneges").arg(VERSION) + '\n';
00798                 str += tr("A simple tool for creating and verifying digital signatures.") + '\n';
00799                 str += '\n';
00800                 str += tr("Using QCA version %1").arg(verstr) + '\n';
00801                 str += '\n';
00802                 str += tr("Icons by Jason Kim") + '\n';
00803 
00804                 QCA::ProviderList list = QCA::providers();
00805                 foreach(QCA::Provider *p, list)
00806                 {
00807                         QString credit = p->credit();
00808                         if(!credit.isEmpty())
00809                         {
00810                                 str += '\n';
00811                                 str += credit;
00812                         }
00813                 }
00814 
00815                 QMessageBox::about(this, tr("About CMS Signer"), str);
00816         }
00817 
00818         void sign_loadError()
00819         {
00820                 delete op;
00821                 op = 0;
00822 
00823                 setEnabled(true);
00824         }
00825 
00826         void sign_finished(const QString &sig)
00827         {
00828                 delete op;
00829                 op = 0;
00830 
00831                 ui.te_sig->setPlainText(sig);
00832 
00833                 setEnabled(true);
00834         }
00835 
00836         void sign_error(const QString &msg)
00837         {
00838                 delete op;
00839                 op = 0;
00840 
00841                 setEnabled(true);
00842 
00843                 QMessageBox::information(this, tr("Error"), msg);
00844         }
00845 
00846         void verify_finished()
00847         {
00848                 QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
00849                 delete op;
00850                 op = 0;
00851 
00852                 // import the cert?
00853                 QCA::SecureMessageKey skey = signer.key();
00854                 if(!skey.isNull())
00855                 {
00856                         QCA::CertificateChain chain = skey.x509CertificateChain();
00857 
00858                         int at = -1;
00859                         QList<CertItem> items = users->items();
00860                         for(int n = 0; n < items.count(); ++n)
00861                         {
00862                                 const CertItem &i = items[n];
00863                                 if(i.certificateChain().primary() == chain.primary())
00864                                 {
00865                                         at = n;
00866                                         break;
00867                                 }
00868                         }
00869 
00870                         // add
00871                         if(at == -1)
00872                         {
00873                                 auto_import_req_id = users->addUser(chain);
00874                                 return;
00875                         }
00876                         // update
00877                         else
00878                         {
00879                                 users->updateChain(users->idFromRow(at), chain);
00880                         }
00881                 }
00882 
00883                 verify_next();
00884         }
00885 
00886         void verify_next()
00887         {
00888                 setEnabled(true);
00889 
00890                 QMessageBox::information(this, tr("Verify"), tr("Signature verified successfully."));
00891         }
00892 
00893         void verify_error(const QString &msg)
00894         {
00895                 QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
00896                 delete op;
00897                 op = 0;
00898 
00899                 QCA::SecureMessageKey skey = signer.key();
00900                 if(signer.keyValidity() == QCA::ErrorSelfSigned && !skey.isNull())
00901                 {
00902                         QCA::CertificateChain chain = skey.x509CertificateChain();
00903                         if(chain.count() == 1 && chain.primary().isSelfSigned())
00904                         {
00905                                 QCA::Certificate cert = chain.primary();
00906 
00907                                 int ret = QMessageBox::warning(this, tr("Self-signed certificate"), tr(
00908                                         "<qt>The signature is made by an unknown user, and the certificate is self-signed.<br>\n"
00909                                         "<br>\n"
00910                                         "<nobr>Common Name: %1</nobr><br>\n"
00911                                         "<nobr>SHA1 Fingerprint: %2</nobr><br>\n"
00912                                         "<br>\n"
00913                                         "Trust the certificate?</qt>"
00914                                         ).arg(cert.commonName(), get_fingerprint(cert)),
00915                                         QMessageBox::Yes | QMessageBox::No,
00916                                         QMessageBox::No);
00917 
00918                                 if(ret == QMessageBox::Yes)
00919                                 {
00920                                         self_signed_verify_cert = cert;
00921                                         do_verify();
00922                                         return;
00923                                 }
00924                         }
00925                 }
00926 
00927                 setEnabled(true);
00928 
00929                 QMessageBox::information(this, tr("Error"), msg);
00930         }
00931 };
00932 
00933 int main(int argc, char **argv)
00934 {
00935         QCA::Initializer qcaInit;
00936         QApplication qapp(argc, argv);
00937 
00938         qapp.setApplicationName(MainWin::tr("CMS Signer"));
00939 
00940         if(!QCA::isSupported("cert,crl,cms"))
00941         {
00942                 QMessageBox::critical(0, qapp.applicationName() + ": " + MainWin::tr("Error"),
00943                         MainWin::tr("No support for CMS is available.  Please install an appropriate QCA plugin, such as qca-ossl."));
00944                 return 1;
00945         }
00946 
00947         QCA::KeyStoreManager::start();
00948 
00949         MainWin mainWin;
00950         mainWin.show();
00951         return qapp.exec();
00952 }
00953 
00954 #include "main.moc"

Generated on Thu Sep 6 19:13:35 2007 for Qt Cryptographic Architecture by  doxygen 1.5.2