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.
541 lines
21 KiB
541 lines
21 KiB
/*! @file |
|
|
|
@id $Id$ |
|
*/ |
|
// 1 2 3 4 5 6 7 8 |
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
|
|
#ifndef __Tokentool_HXX__ |
|
#define __Tokentool_HXX__ |
|
|
|
#include <QMainWindow> |
|
#include <QMessageBox> |
|
#include <QProgressBar> |
|
#include <QDateTime> |
|
#include <QColor> |
|
#include <QSslCertificate> |
|
#include <QSslKey> |
|
#include <QSslCertificateExtension> |
|
#include <ui_tokentool.hxx> |
|
#include <memory> |
|
|
|
#include <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 Progress: public QProgressBar { |
|
public: |
|
Progress() { |
|
reset(); |
|
} |
|
Progress& steps(int incr) { |
|
setMaximum(maximum()+incr); |
|
setVisible(value()<maximum()); |
|
return *this; |
|
} |
|
Progress& operator++() { |
|
setValue(value()+1); |
|
setVisible(value()<maximum()); |
|
if (value()>=maximum()) { |
|
setMaximum(0); |
|
setValue(0); |
|
} |
|
return *this; |
|
} |
|
Progress& reset() { |
|
setMinimum(0); |
|
setMaximum(0); |
|
setValue(0); |
|
setVisible(false); |
|
return *this; |
|
} |
|
|
|
}; |
|
|
|
class tokentool: public QMainWindow, protected Ui::tokentool { |
|
Q_OBJECT; |
|
public: |
|
tokentool(std::string lib="libcvP11.so", QWidget* p=0): |
|
QMainWindow(p), _slot(0), _progress(new Progress) { |
|
try { |
|
setupUi(this); |
|
statusBar()->addPermanentWidget(_progress, 2); |
|
show(); |
|
_progress->steps(3); |
|
_cryptoki = std::auto_ptr<cryptoki::Library>(new cryptoki::Library(lib)); |
|
++*_progress; |
|
_certificates->addAction(actionDeleteSelectedCertificate); |
|
libraryInfo(lib); |
|
++*_progress; |
|
on_actionRescan_triggered(); |
|
++*_progress; |
|
} catch (...) { |
|
QMessageBox::critical(this, tr("Initialization Failed"), |
|
tr("SmardCard Initialization Failed")); |
|
} |
|
} |
|
private Q_SLOTS: |
|
void on_actionRescan_triggered() { |
|
qDebug()<<__PRETTY_FUNCTION__; |
|
static bool lock(false); |
|
if (lock) return; |
|
lock = true; |
|
try { |
|
_slot = 0; |
|
_slots->clear(); |
|
_slotList = _cryptoki->slotList(false); |
|
_progress->steps(_slotList.size()); |
|
for (cryptoki::SlotList::iterator it(_slotList.begin()); |
|
it!=_slotList.end(); ++it) { |
|
qDebug()<<"Add Slot"; |
|
QAction* a(_slots->addAction(qs(it->slotinfo().slotDescription))); |
|
a->setCheckable(true); |
|
a->setData((qulonglong)&*it); |
|
assert(connect(a, SIGNAL(triggered(bool)), SLOT(chosen(bool)))); |
|
if (!_slot) a->trigger(); |
|
++*_progress; |
|
} |
|
qDebug()<<"DONE"; |
|
} catch (...) {} // ignore |
|
lock = false; |
|
on__certificates_itemSelectionChanged(); |
|
} |
|
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() |
|
&&!_pin->text().isEmpty()); |
|
} |
|
void on__pin_textChanged(const QString&) { |
|
on__certificates_itemSelectionChanged(); |
|
} |
|
void chosen(bool) { |
|
_pin->clear(); |
|
QAction* a(qobject_cast<QAction*>(sender())); |
|
a->setChecked(true); |
|
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 { |
|
_progress->steps(3); |
|
slotInfo(); |
|
++*_progress; |
|
tokenInfo(); |
|
++*_progress; |
|
certificates(); |
|
++*_progress; |
|
objects(); |
|
} catch (...) { |
|
_progress->reset(); |
|
on_actionRescan_triggered(); |
|
} |
|
} |
|
void libraryInfo(std::string lib) { |
|
_soname->setText(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() { |
|
try { |
|
if (!_slot) return; |
|
_hardware->setTabEnabled(1, true); |
|
cryptoki::SlotInfo slotInfo(_slot->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); |
|
} catch (...) { |
|
_hardware->setTabEnabled(1, false); |
|
} |
|
} |
|
void tokenInfo() { |
|
try { |
|
if (!_slot) return; |
|
_hardware->setTabEnabled(2, true); |
|
_hardware->setTabEnabled(3, true); |
|
cryptoki::TokenInfo tokenInfo(_slot->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); |
|
} catch (...) { |
|
_hardware->setTabEnabled(2, false); |
|
_hardware->setTabEnabled(3, false); |
|
} |
|
} |
|
void certificates() { |
|
try { |
|
_certificates->clear(); |
|
if (!_slot) return; |
|
_certificates->setEnabled(true); |
|
cryptoki::Session session(*_slot); |
|
cryptoki::ObjectList certs |
|
(session.find(cryptoki::Attribute(CKA_CLASS) |
|
.from<CK_OBJECT_CLASS>(CKO_CERTIFICATE))); |
|
_progress->steps(certs.size()); |
|
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); |
|
addCertificate(cert->attribute(CKA_LABEL).value, |
|
crypto::hex(cert->attribute(CKA_ID).value), c); |
|
++*_progress; |
|
} |
|
} catch (...) { |
|
_certificates->setEnabled(false); |
|
} |
|
} |
|
void objects() { |
|
try { |
|
_objects->clear(); |
|
if (!_slot) return; |
|
_objects->setEnabled(true); |
|
cryptoki::Session session(*_slot); |
|
cryptoki::ObjectList objs(session.find()); |
|
_progress->steps(objs.size()); |
|
for (cryptoki::ObjectList::iterator obj(objs.begin()); |
|
obj!=objs.end(); ++obj) { |
|
_objects->addTopLevelItem |
|
(new QTreeWidgetItem |
|
(QStringList() |
|
<<qs(obj->attribute(CKA_LABEL).value) |
|
<<qs(crypto::hex(obj->attribute(CKA_ID).value)))); |
|
++*_progress; |
|
} |
|
_objects->resizeColumnToContents(0); |
|
_objects->resizeColumnToContents(1); |
|
} catch (...) { |
|
_objects->setEnabled(false); |
|
} |
|
} |
|
void addCertificate(const std::string& label, const std::string& id, |
|
const QSslCertificate& cert) { |
|
if (cert.isNull()) return; |
|
QString txt; |
|
QSslKey pubkey(cert.publicKey()); |
|
QTreeWidgetItem *twi(0); |
|
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("Version") |
|
<<cert.version())); |
|
c->addChild |
|
(new QTreeWidgetItem |
|
(QStringList()<<tr("Digest") |
|
<<cert.digest().toHex())); |
|
c->addChild |
|
(new QTreeWidgetItem |
|
(QStringList()<<tr("Serial Number") |
|
<<cert.serialNumber())); |
|
c->addChild |
|
(twi = new QTreeWidgetItem |
|
(QStringList()<<tr("Issuer Info"))); |
|
for (QByteArray attr: cert.issuerInfoAttributes()) |
|
twi->addChild |
|
(new QTreeWidgetItem |
|
(QStringList()<<attr |
|
<<cert.issuerInfo(attr))); |
|
twi->setExpanded(true); |
|
c->addChild |
|
(twi = new QTreeWidgetItem |
|
(QStringList()<<tr("Subject Info"))); |
|
for (QByteArray attr: cert.subjectInfoAttributes()) |
|
twi->addChild |
|
(new QTreeWidgetItem |
|
(QStringList()<<attr |
|
<<cert.subjectInfo(attr))); |
|
twi->setExpanded(true); |
|
c->addChild |
|
(twi = new QTreeWidgetItem |
|
(QStringList()<<tr("Extensions"))); |
|
for (QSslCertificateExtension ext: cert.extensions()) |
|
for (const auto& v: stringify(ext.value()).toStdMap()) |
|
twi->addChild |
|
(new QTreeWidgetItem // not used: oid |
|
(QStringList()<<(v.first.isEmpty() |
|
? ext.name() |
|
: tr("%1[%2]", "element of a certificate extension map;" |
|
" %1: extension name, %2: map entry name") |
|
.arg(ext.name()).arg(v.first))<<v.second)); |
|
twi->setExpanded(true); |
|
c->addChild |
|
(twi = new QTreeWidgetItem |
|
(QStringList()<<tr("Public Key"))); |
|
switch (pubkey.algorithm()) { |
|
case QSsl::Rsa: txt="RSA"; break; |
|
case QSsl::Dsa: txt="DSA"; break; |
|
/// @bug error: ‘Ec’ is not a member of ‘QSsl’ |
|
//case QSsl::Ec: txt="EC"; break; |
|
case QSsl::Opaque: txt="Opaque"; break; |
|
default: txt="****ERROR****"; break; |
|
} |
|
twi->addChild(new QTreeWidgetItem(QStringList()<<tr("Algorithm")<<txt)); |
|
twi->setExpanded(true); |
|
/// @bug error: ‘const class QSslCertificate’ has no member named ‘isSelfSigned’ |
|
// if (cert.isSelfSigned()) |
|
// for (int i=0; i<c->columnCount(); ++i) |
|
// c->setBackground(i, Qt::darkYellow); |
|
if (cert.isBlacklisted()) |
|
for (int i=0; i<c->columnCount(); ++i) |
|
c->setBackground(i, Qt::red); |
|
|
|
// 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(si) |
|
// <<utfConv(cert.subjectInfo(si))))); |
|
// } |
|
// it->setExpanded(true); |
|
// QMultiMap<QSsl::AlternateNameEntryType, QString> |
|
// #if QT_VERSION <0x050000 |
|
// asns(cert.alternateSubjectNames()) |
|
// #else |
|
// asns(cert.subjectAlternativeNames()) |
|
// #endif |
|
// ; |
|
// 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(si) |
|
// <<utfConv(cert.issuerInfo(si))))); |
|
// it->setExpanded(true); |
|
_certificates->resizeColumnToContents(0); |
|
_certificates->resizeColumnToContents(1); |
|
} |
|
private: |
|
QMultiMap<QString, QString> stringify(const QVariant& v) { |
|
QMultiMap<QString, QString> res; |
|
switch (v.type()) { |
|
case QVariant::ByteArray: { |
|
bool nonprint(false); |
|
QString txt(QString::fromUtf8(v.toByteArray())); |
|
for (QChar& c: txt) { |
|
if (!c.isPrint()) |
|
if (c=='\n'||c=='\r') { |
|
c = '\n'; |
|
} else { |
|
txt.replace(c, tr("[%1]", |
|
"stringified representation of nonprintable character;" |
|
" %1: value in hex") |
|
.arg(QString::fromLocal8Bit(QString(c).toUtf8().toHex()))); |
|
nonprint = true; |
|
} |
|
} |
|
res.insert(QString(), txt); |
|
} break; |
|
case QVariant::List: |
|
for (const QVariant& vv: v.toList()) |
|
res.insert(QString(), QStringList(stringify(vv).values()).join('\n')); |
|
break; |
|
case QVariant::Map: |
|
for (auto vv: v.toMap().toStdMap()) |
|
res.insert(vv.first, QStringList(stringify(vv.second).values()).join('\n')); |
|
break; |
|
case QVariant::StringList: |
|
for (const auto& vv: v.toStringList()) |
|
res.insert(QString(), vv); |
|
break; |
|
case QVariant::String: |
|
res.insert(QString(), v.toString()); |
|
break; |
|
default: |
|
res.insert(v.typeName(), v.toString()); |
|
} |
|
return res; |
|
} |
|
void deleteCert(QTreeWidgetItem* item) { |
|
if (!_slot) return; |
|
if (_pin->text().isEmpty()) { |
|
QMessageBox::critical(this, tr("PIN required"), tr("Please enter PIN")); |
|
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()); |
|
cryptoki::Session session(*_slot, true); |
|
try { |
|
session.login(_pin->text().toStdString()); |
|
} catch (...) { |
|
QMessageBox::critical(this, tr("Login Failed"), tr("Wrong PIN")); |
|
return; |
|
} |
|
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 { |
|
obj->destroy(); |
|
} catch (const std::exception& e) { |
|
QMessageBox::critical(this, tr("Delete Failed"), |
|
tr("Certificate Deletion Failes")); |
|
} |
|
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(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; |
|
} |
|
return res; |
|
} |
|
QString utfConv(const QString& txt) { |
|
QByteArray value(txt.toLatin1()); |
|
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()); |
|
} |
|
QStringList utfConv(const QStringList& txts) { |
|
QStringList res; |
|
Q_FOREACH(QString txt, txts) { |
|
QByteArray value(txt.toLatin1()); |
|
for (int i(-1); (i=value.indexOf("\\x"))!=-1 && i+3<value.size();) { |
|
value.replace(i, 4, QByteArray::fromHex(value.mid(i+2, 2))); |
|
res<<QString::fromUtf8(value.data(), value.size()); |
|
} |
|
} |
|
return res; |
|
} |
|
private: |
|
std::auto_ptr<cryptoki::Library> _cryptoki; |
|
cryptoki::SlotList _slotList; |
|
cryptoki::Slot* _slot; |
|
Progress* _progress; |
|
}; |
|
|
|
#endif
|
|
|