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"