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.
273 lines
9.2 KiB
273 lines
9.2 KiB
#ifndef SMARTCARDAUTH_H |
|
#define SMARTCARDAUTH_H |
|
|
|
#include <qbrowserlib/log.hxx> |
|
#include <QtCore/QMutex> |
|
#include <QtCore/QWaitCondition> |
|
#include <QtNetwork/QSslSocket> |
|
#include <QtNetwork/QSslConfiguration> |
|
#include <QtNetwork/QSslCertificate> |
|
#include <QtNetwork/QSslKey> |
|
#include <QMessageBox> |
|
|
|
#include <qbrowserlib/pinentry.hxx> |
|
|
|
#include <suisseid.hxx> |
|
#include <openssl-engine.hxx> |
|
|
|
#include <memory> |
|
|
|
namespace qbrowserlib { |
|
|
|
class CryptokiEngine: public QObject, public openssl::Engine { |
|
|
|
Q_OBJECT; |
|
|
|
Q_SIGNALS: |
|
|
|
void certRequired(); |
|
|
|
public: |
|
|
|
CryptokiEngine() { |
|
TRC; |
|
} |
|
|
|
operator bool() { |
|
TRC; LOG<<"Status of CryptokiEngine: " |
|
<<(_privateKey.get() |
|
?"privateKey defined, ":"privateKey undefined"); |
|
return _privateKey.get(); |
|
} |
|
|
|
void cert(cryptoki::Object& privateKey, const std::string& certVal) { |
|
TRC; |
|
_privateKey = std::auto_ptr<cryptoki::Object> |
|
(new cryptoki::Object(privateKey)); |
|
try { // new |
|
QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration()); |
|
QSslCertificate localcert(QByteArray(certVal.data(), |
|
certVal.size()), |
|
QSsl::Der); |
|
sslConfig.setLocalCertificate(localcert); |
|
|
|
//RSA_set_default_method(ENGINE_get_RSA(_e)); |
|
QByteArray pem // empty dummy key for qt object instantiation |
|
("-----BEGIN RSA PRIVATE KEY-----\n" |
|
"MIIBOwIBAAJBAMH2yqAGeVNPdgeZ2GoHo31m9aUxZ7QfK2Go2qLTahLpQ3UL1C8G\n" |
|
"LkuMS8SNK0ZGfRMalIpIhv6bW5l3kjogOncCAwEAAQJABVGECtFCoGMsZFb2lSmy\n" |
|
"dOzOzYHGSy0TnnDn1dEgNnZ8sIljElPtUzm9dyXs2P3ICL1sOd7qjpzfJeyxknDL\n" |
|
"AQIhAO5iKdLmhyuW+EDEH19vDs1Pmqs3/ZnT5UgUiJnTJqz3AiEA0ExIfUOCnxq2\n" |
|
"a3Z46KEivcr8JB2P9VqouBbVryiq/oECIQDj8bPCejMoiEzMSX0iWWTTB9qC/KAg\n" |
|
"FtF4skHIrXKfEwIgPCs86Uo+Ch2aQjKHvJMHSRHAgeI0OmiEwiB+e0lhE4ECIQDd\n" |
|
"IbUmHIXt6oHLJmoGFX46bCcfil5eE5FXfiaw7Q9iPw==\n" |
|
"-----END RSA PRIVATE KEY-----\n"); |
|
QSslKey privkey(pem, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
|
RSA* rsa((RSA*)privkey.handle()); |
|
if (!ENGINE_init(_e)) return; |
|
rsa->engine=_e; |
|
rsa->meth=ENGINE_get_RSA(_e); |
|
if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_RSA, rsa, &rsa->ex_data)) { |
|
ENGINE_finish(_e); |
|
return; |
|
} |
|
set(rsa->n, privateKey, CKA_MODULUS, "CKA_MODULUS"); |
|
set(rsa->e, privateKey, CKA_PUBLIC_EXPONENT, "CKA_PUBLIC_EXPONENT"); |
|
set(rsa->d, privateKey, CKA_PRIVATE_EXPONENT, "CKA_PRIVATE_EXPONENT"); |
|
set(rsa->p, privateKey, CKA_PRIME_1, "CKA_PRIME_1"); |
|
set(rsa->q, privateKey, CKA_PRIME_2, "CKA_PRIME_2"); |
|
set(rsa->dmp1, privateKey, CKA_EXPONENT_1, "CKA_EXPONENT_1"); |
|
set(rsa->dmq1, privateKey, CKA_EXPONENT_2, "CKA_EXPONENT_2"); |
|
set(rsa->iqmp, privateKey, CKA_COEFFICIENT, "CKA_COEFFICIENT"); |
|
rsa->flags |= RSA_FLAG_SIGN_VER; // don't emulate with encrypt/decrypt |
|
assert(!privkey.isNull()); |
|
LOG<<"Setup RSA finished"; |
|
sslConfig.setPrivateKey(privkey); |
|
QSslConfiguration::setDefaultConfiguration(sslConfig); |
|
} catch (const std::exception& e) { |
|
LOG<<"SETUP ERROR: "<<e.what(); |
|
} |
|
} |
|
|
|
protected: |
|
|
|
void set(BIGNUM*& num, cryptoki::Object& key, int type, std::string name) { |
|
TRC; |
|
try { |
|
std::string value(key.attribute(type).value); |
|
num = BN_bin2bn((const unsigned char*)value.data(), |
|
value.size(), num); |
|
} catch (const std::exception& x) { |
|
LOG<<"**** ERROR: key attribute missing:"<<name.c_str()<<x.what(); |
|
} |
|
} |
|
|
|
virtual const char* id() { |
|
TRC; |
|
return "SuisseID Engine ID"; |
|
} |
|
|
|
virtual const char* name() { |
|
TRC; |
|
return "SuisseID Engine NAME"; |
|
} |
|
|
|
virtual std::string rsaSign(const std::string& in, unsigned int type) try { |
|
TRC; LOG<<"log; type="<<type<<"; size="<<in.size(); |
|
LOG<<crypto::readable(in).c_str(); |
|
/* std::string data; |
|
switch (type) { |
|
case NID_sha1: |
|
data += QString(QByteArray::fromHex("3021300906052b0e03021a05000414")) |
|
.toStdString(); |
|
break; |
|
case NID_sha224: |
|
data += QString(QByteArray::fromHex("302d300d06096086480165030402040500041c")) |
|
.toStdString(); |
|
break; |
|
case NID_sha256: |
|
data += QString(QByteArray::fromHex("3031300d060960864801650304020105000420")) |
|
.toStdString(); |
|
break; |
|
case NID_sha384: |
|
data += QString(QByteArray::fromHex("3041300d060960864801650304020205000430")) |
|
.toStdString(); |
|
break; |
|
case NID_sha512: |
|
data += QString(QByteArray::fromHex("3051300d060960864801650304020305000440")) |
|
.toStdString(); |
|
break; |
|
default: |
|
break; |
|
} |
|
data += in; */ |
|
unsigned int algo(CKM_RSA_PKCS); |
|
switch (type) { |
|
case NID_md5_sha1: algo = CKM_RSA_PKCS; break; |
|
case NID_md2: algo = CKM_MD2_RSA_PKCS; break; |
|
case NID_md5: algo = CKM_MD5_RSA_PKCS; break; |
|
case NID_sha1: algo = CKM_SHA1_RSA_PKCS; break; |
|
case NID_sha256: algo = CKM_SHA256_RSA_PKCS; break; |
|
case NID_sha384: algo = CKM_SHA384_RSA_PKCS; break; |
|
case NID_sha512: algo = CKM_SHA512_RSA_PKCS; break; |
|
case NID_ripemd160: algo = CKM_RIPEMD160_RSA_PKCS; break; |
|
default: throw std::runtime_error("unknown sign mechanism"); |
|
} |
|
LOG<<"ready to sign with algorith "<<algo; |
|
try { |
|
return _privateKey->sign(in, algo); |
|
} catch (const std::exception& x) { |
|
LOG<<"signature failed, reason: "<<x.what(); |
|
certRequired(); // get new certificate |
|
return _privateKey->sign(in, algo); // try again |
|
} |
|
} catch (const std::exception& x) { |
|
TRC; LOG<<"rsaSign failed, reason: "<<x.what(); |
|
throw; |
|
} |
|
|
|
|
|
private: |
|
|
|
std::auto_ptr<cryptoki::Object> _privateKey; |
|
|
|
}; |
|
|
|
class SmartCardAuth: public QObject { |
|
Q_OBJECT; |
|
|
|
public: |
|
|
|
SmartCardAuth(suisseid::Cards cards, QWidget* p=0, bool loginAtStart=true): |
|
_parent(p), _e(new CryptokiEngine()), _reg(_e), _cards(cards) { |
|
TRC; |
|
if (loginAtStart) login(); |
|
assert(connect(_e, SIGNAL(certRequired()), SLOT(login()))); |
|
} |
|
|
|
public Q_SLOTS: |
|
|
|
void login(bool force=true) { |
|
TRC; |
|
Lock lock; |
|
LOG<<"got lock"; |
|
if (!_e || (!force && *_e)) return; // no smartcard or already logged in |
|
LOG<<"get new certificate"; |
|
try { |
|
for (suisseid::Cards::iterator card(_cards.begin()); |
|
card!=_cards.end(); ++card) { |
|
suisseid::Certificate cert((*card)->authenticationCertificate()); |
|
PinEntry pinEntry(QSslCertificate(QByteArray(cert.data(), |
|
cert.size()), |
|
QSsl::Der), _parent); |
|
while (true) try { |
|
if (pinEntry |
|
.tokeninfo((*card)->minimalPinLength(), |
|
(*card)->maximalPinLength()) |
|
.retries((*card)->pkcs15PinRetries()) |
|
.myexec() |
|
!=PinEntry::Accepted) |
|
return; |
|
_session = // session login with pin |
|
std::shared_ptr<cryptoki::Session> |
|
(new cryptoki::Session((*card)->session())); |
|
_session->login(pinEntry.pin().toStdString()); |
|
cryptoki::ObjectList keys |
|
(_session->find(cryptoki::Attribute(CKA_CLASS) |
|
.from<CK_OBJECT_CLASS>(CKO_PRIVATE_KEY), |
|
cert.id())); |
|
if (keys.size()==1) { |
|
_e->cert(keys[0], cert); // install client cert |
|
break; |
|
} |
|
} catch (std::exception& x) { |
|
pinEntry.pin().clear(); |
|
LOG<<"**** ERROR"<<x.what(); |
|
QMessageBox::critical(0, QMessageBox::tr("Wrong PIN"), |
|
QMessageBox::tr("Authentication failed," |
|
" please try again.")); |
|
} |
|
} |
|
} catch (std::exception& x) { |
|
LOG<<"**** ERROR"<<x.what(); |
|
throw; |
|
} |
|
} |
|
|
|
private: |
|
|
|
private: |
|
|
|
class Lock { |
|
public: |
|
Lock() { |
|
TRC; LOG<<loops().size(); |
|
loops().append(new QEventLoop); // add to queue |
|
if (loops().size()>1) loops().back()->exec(); // wait |
|
} |
|
~Lock() { |
|
TRC; LOG<<loops().size(); |
|
delete loops().front(); // mine is the first; |
|
loops().erase(loops().begin()); // mine is the first; |
|
if (loops().begin()!=loops().end()) |
|
loops().front()->exit(0); // wake up next |
|
} |
|
private: |
|
QList<QEventLoop*>& loops() { // same as a static member variable |
|
static QList<QEventLoop*> _loops; |
|
return _loops; |
|
} |
|
}; |
|
|
|
private: |
|
|
|
QWidget* _parent; |
|
CryptokiEngine* _e; |
|
openssl::RegisterEngine<CryptokiEngine> _reg; |
|
suisseid::Cards _cards; |
|
std::shared_ptr<cryptoki::Session> _session; |
|
|
|
}; |
|
|
|
} |
|
#endif // SMARTCARDAUTH_H
|
|
|