|
|
|
#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
|