GUI tool to display PKCS#11 crypto token information. Specially designed fot SuisseID.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

301 lines
13 KiB

/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#ifndef __Tokentool_HXX__
#define __Tokentool_HXX__
#include <QtGui/QMainWindow>
#include <QtCore/QDateTime>
#include <QtNetwork/QSslCertificate>
#include <ui_tokentool.h>
#include <QtCore/QDebug>
#include <cryptoki.hxx>
inline QString qs(const std::string& s) {
return QString::fromStdString(s);
}
template<std::string::size_type SIZE>
QString qs(const cryptoki::FixString<SIZE>& s) {
return QString::fromStdString(s);
}
class tokentool: public QMainWindow, protected Ui::tokentool {
Q_OBJECT;
public:
tokentool(std::string lib="libcvP11.so", QWidget* p=0):
QMainWindow(p), _cryptoki(lib), _slot(0) {
setupUi(this);
_certificates->addAction(actionDeleteSelectedCertificate);
libraryInfo(lib);
rescan();
}
void rescan() {
_slot = 0;
_slots->clear();
_slotList = _cryptoki.slotList();
for (cryptoki::SlotList::iterator it(_slotList.begin());
it!=_slotList.end(); ++it) {
QAction* a(_slots->addAction(qs(it->slotinfo().slotDescription)));
a->setCheckable(true);
a->setData((qulonglong)&*it);
assert(connect(a, SIGNAL(toggled(bool)), SLOT(chosen(bool))));
if (!_slot) a->setChecked(true);
}
on__certificates_itemSelectionChanged();
}
private Q_SLOTS:
void on_actionDeleteSelectedCertificate_triggered() {
QList<QTreeWidgetItem*> items(_certificates->selectedItems());
if (items.size()>0) deleteCert(items[0]);
}
void on__certificates_itemSelectionChanged() {
actionDeleteSelectedCertificate
->setEnabled(_certificates->selectedItems().size());
}
void chosen(bool checked) {
if (!checked) return;
_pin->clear();
QAction* a(qobject_cast<QAction*>(sender()));
QMenu* m(qobject_cast<QMenu*>(a->parent()));
QObjectList actions(m->children());
for (QObjectList::iterator it(actions.begin());it!=actions.end(); ++it)
if (qobject_cast<QAction*>(*it)!=a)
qobject_cast<QAction*>(*it)->setChecked(false);
_slot = (cryptoki::Slot*)a->data().toLongLong();
setup();
}
void setup() {
try {
if (!_slot) return;
slotInfo(_slot->slotinfo());
tokenInfo(_slot->tokeninfo());
certificates(*_slot);
} catch (...) {}
}
void libraryInfo(std::string lib) {
_soname->setTitle(qs(lib));
cryptoki::Info inf(_cryptoki.info());
_libraryCryptokiVersion->setText(QString("%1.%2")
.arg(inf.cryptokiVersion.major)
.arg(inf.cryptokiVersion.minor));
_libraryManufacturerID->setText(qs(inf.manufacturerID));
_libraryDescription->setText(qs(inf.libraryDescription));
_libraryVersion->setText(QString("%1.%2")
.arg(inf.libraryVersion.major)
.arg(inf.libraryVersion.minor));
}
void slotInfo(const cryptoki::SlotInfo& slotInfo) {
_slotDescription->setText(qs(slotInfo.slotDescription));
_slotManufacturerID->setText(qs(slotInfo.manufacturerID));
_slotHardwareVersion->setText(QString("%1.%2")
.arg(slotInfo.hardwareVersion.major)
.arg(slotInfo.hardwareVersion.minor));
_slotFirmwareVersion->setText(QString("%1.%2")
.arg(slotInfo.firmwareVersion.major)
.arg(slotInfo.firmwareVersion.minor));
_tokenPresent->setEnabled(slotInfo.flags&CKF_TOKEN_PRESENT);
_removableDevice->setEnabled(slotInfo.flags&CKF_REMOVABLE_DEVICE);
_hwSlot->setEnabled(slotInfo.flags&CKF_HW_SLOT);
_hasTokenInfo->setEnabled(slotInfo.flags&CKF_TOKEN_PRESENT);
}
void tokenInfo(const cryptoki::TokenInfo& tokenInfo) {
_tokenLabel->setText(qs(tokenInfo.label));
_tokenManufacturerID->setText(qs(tokenInfo.manufacturerID));
_tokenModel->setText(qs(tokenInfo.model));
_tokenSerialNumber->setText(qs(tokenInfo.serialNumber));
_tokenSessionCount->setText(QString("%2/%1")
.arg(qs(cryptoki::string
(tokenInfo.maxSessionCount)))
.arg(qs(cryptoki::string
(tokenInfo.sessionCount))));
_tokenRWSessionCount->setText(QString("%2/%1")
.arg(qs(cryptoki::string
(tokenInfo.maxRwSessionCount)))
.arg(qs(cryptoki::string
(tokenInfo.rwSessionCount))));
_tokenPinLen->setText(QString("%2-%1")
.arg(tokenInfo.maxPinLen)
.arg(tokenInfo.minPinLen));
_tokenPublicMemory->setText(QString("%2/%1")
.arg(qs(cryptoki::string
(tokenInfo.totalPublicMemory)))
.arg(qs(cryptoki::string
(tokenInfo.freePublicMemory))));
_tokenPrivateMemory->setText(QString("%2/%1")
.arg(qs(cryptoki::string
(tokenInfo.totalPrivateMemory)))
.arg(qs(cryptoki::string
(tokenInfo.freePrivateMemory))));
_tokenHardwareVersion->setText(QString("%1.%2")
.arg(tokenInfo.hardwareVersion.major)
.arg(tokenInfo.hardwareVersion.minor));
_tokenFirmwareVersion->setText(QString("%1.%2")
.arg(tokenInfo.firmwareVersion.major)
.arg(tokenInfo.firmwareVersion.minor));
_tokenUTCTime->setText(qs(tokenInfo.utcTime));
_tokenUTCTime->setVisible(tokenInfo.flags&CKF_CLOCK_ON_TOKEN);
_rng->setEnabled(tokenInfo.flags&CKF_RNG);
_writeProtected->setEnabled(tokenInfo.flags&CKF_WRITE_PROTECTED);
_loginRequired->setEnabled(tokenInfo.flags&CKF_LOGIN_REQUIRED);
_userPinInitialized->setEnabled(tokenInfo.flags&CKF_USER_PIN_INITIALIZED);
_restoreKeyNotNeeded->setEnabled
(tokenInfo.flags&CKF_RESTORE_KEY_NOT_NEEDED);
_clockOnToken->setEnabled(tokenInfo.flags&CKF_CLOCK_ON_TOKEN);
_protectedAuthenticationPath->setEnabled
(tokenInfo.flags&CKF_PROTECTED_AUTHENTICATION_PATH);
_dualCryptoOperations->setEnabled
(tokenInfo.flags&CKF_DUAL_CRYPTO_OPERATIONS);
_tokenInitialized->setEnabled(tokenInfo.flags&CKF_TOKEN_INITIALIZED);
_secondaryAuthentication->setEnabled
(tokenInfo.flags&CKF_SECONDARY_AUTHENTICATION);
_userPinCountLow->setEnabled(tokenInfo.flags&CKF_USER_PIN_COUNT_LOW);
_userPinFinalTry->setEnabled(tokenInfo.flags&CKF_USER_PIN_FINAL_TRY);
_userPinLocked->setEnabled(tokenInfo.flags&CKF_USER_PIN_LOCKED);
_userPinToBeChanged->setEnabled
(tokenInfo.flags&CKF_USER_PIN_TO_BE_CHANGED);
_soPinCountLow->setEnabled(tokenInfo.flags&CKF_SO_PIN_COUNT_LOW);
_soPinFinalTry->setEnabled(tokenInfo.flags&CKF_SO_PIN_FINAL_TRY);
_soPinLocked->setEnabled(tokenInfo.flags&CKF_SO_PIN_LOCKED);
_soPinToBeChanged->setEnabled(tokenInfo.flags&CKF_SO_PIN_TO_BE_CHANGED);
}
void certificates(cryptoki::Slot& slot) {
try {
_certificates->clear();
cryptoki::Session session(slot);
cryptoki::ObjectList certs
(session.find(cryptoki::Attribute(CKA_CLASS)
.from<CK_OBJECT_CLASS>(CKO_CERTIFICATE)));
for (cryptoki::ObjectList::iterator cert(certs.begin());
cert!=certs.end(); ++cert) {
std::string data(cert->attribute(CKA_VALUE).value);
QByteArray der(QByteArray(data.data(), data.size()));
QSslCertificate c(der, QSsl::Der);
qDebug()<<"ID="<<qs(crypto::hex(cert->attribute(CKA_ID).value));
addCertificate(cert->attribute(CKA_LABEL).value,
crypto::hex(cert->attribute(CKA_ID).value), c);
}
} catch (...) {}
}
void addCertificate(const std::string& label, const std::string& id,
const QSslCertificate& cert) {
if (cert.isNull()) return;
QTreeWidgetItem* c(new QTreeWidgetItem(QStringList()<<qs(label)));
c->setData(0, Qt::UserRole, qs(id));
_certificates->addTopLevelItem(c);
c->addChild
((new QTreeWidgetItem
(QStringList()<<tr("Valid Since")
<<cert.effectiveDate().toString(Qt::SystemLocaleLongDate))));
c->addChild
((new QTreeWidgetItem
(QStringList()<<tr("Valid Until")
<<cert.expiryDate().toString(Qt::SystemLocaleLongDate))));
c->addChild
((new QTreeWidgetItem
(QStringList()<<tr("Certificate Serial Number")
<<cert.serialNumber())));
QTreeWidgetItem *it(0);
c->addChild
((it = new QTreeWidgetItem(QStringList()<<tr("Subject Info")<<"")));
for (QSslCertificate::SubjectInfo
si(QSslCertificate::StateOrProvinceName);
si>=QSslCertificate::Organization;
si=(QSslCertificate::SubjectInfo)((int)si-1))
if (!cert.subjectInfo(si).isEmpty()) {
it->addChild
((new QTreeWidgetItem(subjectInfo(cert, si))));
}
it->setExpanded(true);
QMultiMap<QSsl::AlternateNameEntryType, QString>
asns(cert.alternateSubjectNames());
for (QMultiMap<QSsl::AlternateNameEntryType, QString>::iterator
asn(asns.begin()); asn!=asns.end(); ++asn)
it->addChild
((new QTreeWidgetItem
(QStringList()<<alternateName(asn.key())<<asn.value())));
c->addChild
((it = new QTreeWidgetItem(QStringList()<<tr("Issuer Info")<<"")));
for (QSslCertificate::SubjectInfo
si(QSslCertificate::StateOrProvinceName);
si>=QSslCertificate::Organization;
si=(QSslCertificate::SubjectInfo)((int)si-1))
if (!cert.issuerInfo(si).isEmpty())
it->addChild
((new QTreeWidgetItem(subjectInfo(cert, si))));
it->setExpanded(true);
_certificates->resizeColumnToContents(0);
_certificates->resizeColumnToContents(1);
}
private:
void deleteCert(QTreeWidgetItem* item) {
if (!_slot) return;
if (item->parent()) return deleteCert(item->parent());
QByteArray qid(QByteArray::fromHex(item->data(0, Qt::UserRole).toByteArray()));
std::string id(qid.data(), qid.size());
qDebug()<<"delete:"<<item->data(0, Qt::DisplayRole).toString();
qDebug()<<"ID="<<qs(crypto::hex(id));
cryptoki::Session session(*_slot, true);
if (!_pin->text().isEmpty()) session.login(_pin->text().toStdString());
cryptoki::ObjectList objs
(session.find(cryptoki::AttributeList()
<<cryptoki::Attribute(CKA_CLASS)
.from<CK_OBJECT_CLASS>(CKO_CERTIFICATE)
<<cryptoki::Attribute(CKA_ID, id)));
for (cryptoki::ObjectList::iterator obj(objs.begin());
obj!=objs.end(); ++obj) try {
qDebug()<<"DESTROY";
obj->destroy();
} catch (const std::exception& e) {
qDebug()<<"Cannot delete:"<<e.what();
}
setup();
}
QString alternateName(QSsl::AlternateNameEntryType an) {
switch (an) {
case QSsl::EmailEntry: return tr("E-Mail");
case QSsl::DnsEntry: return tr("URL");
}
return tr("error", "unknown certificate subject alternate name");
}
QStringList subjectInfo(const QSslCertificate& cert,
QSslCertificate::SubjectInfo si) {
QStringList res;
switch (si) {
case QSslCertificate::Organization:
res<<tr("Organization"); break;
case QSslCertificate::CommonName:
res<<tr("Common Name"); break;
case QSslCertificate::LocalityName:
res<<tr("Locality"); break;
case QSslCertificate::OrganizationalUnitName:
res<<tr("Organizational Unit"); break;
case QSslCertificate::CountryName:
res<<tr("Country"); break;
case QSslCertificate::StateOrProvinceName:
res<<tr("State or Province"); break;
default:
res<<tr("error", "unknown certificate subject info"); break;
}
res<<utfConv(cert.subjectInfo(si));
return res;
}
QString utfConv(const QString& txt) {
QByteArray value(txt.toAscii());
for (int i(-1); (i=value.indexOf("\\x"))!=-1 && i+3<value.size();)
value.replace(i, 4, QByteArray::fromHex(value.mid(i+2, 2)));
return QString::fromUtf8(value.data(), value.size());
}
private:
cryptoki::Init _cryptoki;
cryptoki::SlotList _slotList;
cryptoki::Slot* _slot;
};
#endif