• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE Support
  • Sitemap
  • Contact Us
 

qca

certitem.cpp

Go to the documentation of this file.
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"

qca

Skip menu "qca"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE Support

Skip menu "KDE Support"
  • akonadi
  • Decibel
  • grantlee
  • kdewin
  • phonon
  •     Backend
  • polkit-qt
  • qca
  • qimageblitz
  • soprano
  • strigi
  •     searchclient
  •     streamanalyzer
  •     streams
Generated for KDE Support by doxygen 1.5.9-20090814
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal