|
|
|
#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 <QtGui/QMessageBox>
|
|
|
|
|
|
|
|
#include <pinentry.hxx>
|
|
|
|
|
|
|
|
#include <cryptoki.hxx>
|
|
|
|
#include <pcsc.hxx>
|
|
|
|
#include <openssl-engine.hxx>
|
|
|
|
#include <openssl.hxx>
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
class CryptokiEngine: public QObject, public openssl::Engine {
|
|
|
|
|
|
|
|
Q_OBJECT;
|
|
|
|
|
|
|
|
Q_SIGNALS:
|
|
|
|
|
|
|
|
void certRequired();
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
CryptokiEngine(std::string lib):
|
|
|
|
_cryptoki(lib) {
|
|
|
|
TRC;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator bool() {
|
|
|
|
TRC; LOG<<"Status of CryptokiEngine: "
|
|
|
|
<<(_privateKey.get()
|
|
|
|
?"privateKey defined, ":"privateKey undefined");
|
|
|
|
return _privateKey.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
cryptoki::Init& cryptoki() {
|
|
|
|
return _cryptoki;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
assert(localcert.isValid());
|
|
|
|
|
|
|
|
//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 "CryptokiEngine_ID";
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual const char* name() {
|
|
|
|
TRC;
|
|
|
|
return "CryptokiEngine_NAME";
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual std::string rsaSign(const std::string& in, unsigned int type) {
|
|
|
|
TRC; LOG<<"log; type="<<type<<"; size="<<in.size();
|
|
|
|
LOG<<crypto::readable(in).c_str();
|
|
|
|
if (type != NID_md5_sha1) throw std::runtime_error("wrong sign type");
|
|
|
|
if (in.size() != 36) throw std::runtime_error("wrong msg size to sign");
|
|
|
|
LOG<<"ready to sign";
|
|
|
|
try {
|
|
|
|
return _privateKey->sign(in, CKM_RSA_PKCS);
|
|
|
|
} catch (const std::exception& x) {
|
|
|
|
certRequired(); // get new certificate
|
|
|
|
return _privateKey->sign(in, CKM_RSA_PKCS); // try again
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
cryptoki::Init _cryptoki;
|
|
|
|
std::auto_ptr<cryptoki::Object> _privateKey;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class SmartCardAuth: public QObject {
|
|
|
|
Q_OBJECT;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
SmartCardAuth(const QString& lib, QWidget* p=0, bool loginAtStart=true):
|
|
|
|
_parent(p), _e(new CryptokiEngine(lib.toStdString())), _reg(_e) {
|
|
|
|
TRC;
|
|
|
|
if (loginAtStart) login();
|
|
|
|
assert(connect(_e, SIGNAL(certRequired()), SLOT(login())));
|
|
|
|
}
|
|
|
|
|
|
|
|
public Q_SLOTS:
|
|
|
|
|
|
|
|
void login(bool force=true) {
|
|
|
|
TRC;
|
|
|
|
try {
|
|
|
|
Lock lock;
|
|
|
|
LOG<<"got lock";
|
|
|
|
if (!_e || (!force && *_e)) return; // no smartcard or already logged in
|
|
|
|
LOG<<"start login to smartcard";
|
|
|
|
QList<CertInfo> authcerts;
|
|
|
|
QList<CertInfo> allcerts;
|
|
|
|
QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration());
|
|
|
|
_slots = _e->cryptoki().slotList();
|
|
|
|
LOG<<"number of slots"<<_slots.size();
|
|
|
|
// look for login certificates ----------------------------------------
|
|
|
|
for (cryptoki::SlotList::iterator slot(_slots.begin());
|
|
|
|
slot!=_slots.end(); ++slot) {
|
|
|
|
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) {
|
|
|
|
cryptoki::Attribute label(cert->attribute(CKA_LABEL));
|
|
|
|
cryptoki::Attribute id(cert->attribute(CKA_ID));
|
|
|
|
LOG<<"**** FOUND CERTIFICATE: "<<label.value.c_str();
|
|
|
|
cryptoki::ObjectList keys
|
|
|
|
(session.find(cryptoki::Attribute(CKA_CLASS)
|
|
|
|
.from<CK_OBJECT_CLASS>(CKO_PUBLIC_KEY),
|
|
|
|
id));
|
|
|
|
LOG<<"**** with keys: "<<keys.size();
|
|
|
|
std::string data(cert->attribute(CKA_VALUE).value);
|
|
|
|
if (!keys.size()) { // add CA-certificate
|
|
|
|
LOG<<"**** add to CA-certificates";
|
|
|
|
} else {
|
|
|
|
LOG<<"**** user cert, check for authentictaion";
|
|
|
|
if (label.value.find("auth")==0 ||
|
|
|
|
label.value.find("Authentication")!=std::string::npos) {
|
|
|
|
LOG<<"**** it's our authentication cert";
|
|
|
|
authcerts.push_back(CertInfo(data, slot, id));
|
|
|
|
} else {
|
|
|
|
LOG<<"**** it's an unknown cert";
|
|
|
|
allcerts.push_back(CertInfo(data, slot, id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// get pin and install client certificate ------------------------------
|
|
|
|
LOG<<"00000000000000000000000000000000000000000000000000";
|
|
|
|
if (!authcerts.isEmpty() || !allcerts.isEmpty()) {
|
|
|
|
LOG<<"11111111111111111111111111111111111111111111111111";
|
|
|
|
CertInfo c(authcerts.size()?authcerts[0]:allcerts[0]);
|
|
|
|
PinEntry pinEntry(QSslCertificate(QByteArray(c.data.data(),
|
|
|
|
c.data.size()),
|
|
|
|
QSsl::Der), _parent);
|
|
|
|
while (true) try {
|
|
|
|
LOG<<"******************************************1*******";
|
|
|
|
pinEntry.retries(retries(c.slot->slotinfo().slotDescription));
|
|
|
|
LOG<<"******************************************2*******";
|
|
|
|
int res(pinEntry.myexec());
|
|
|
|
LOG<<"******************************************3*******";
|
|
|
|
if (res!=PinEntry::Accepted) return;
|
|
|
|
LOG<<"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
|
|
_session = // session login with pin
|
|
|
|
std::auto_ptr<cryptoki::Session>
|
|
|
|
(new cryptoki::Session(*c.slot));
|
|
|
|
LOG<<"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
|
|
|
_session->login(pinEntry.pin().toStdString());
|
|
|
|
LOG<<"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
|
|
|
|
cryptoki::ObjectList keys
|
|
|
|
(_session->find(cryptoki::Attribute(CKA_CLASS)
|
|
|
|
.from<CK_OBJECT_CLASS>(CKO_PRIVATE_KEY),
|
|
|
|
c.id));
|
|
|
|
LOG<<"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD";
|
|
|
|
if (keys.size()==1) {
|
|
|
|
LOG<<"**** found one private key";
|
|
|
|
_e->cert(keys[0], c.data); // install client cert
|
|
|
|
LOG<<"==================================================";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
LOG<<"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
|
|
|
|
} 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."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG<<"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY";
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
LOG<<"**** ERROR"<<x.what();
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
LOG<<"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
int retries(const std::string& name) {
|
|
|
|
TRC; LOG<<name.c_str();
|
|
|
|
try {
|
|
|
|
pcsc::Connection pcsc;
|
|
|
|
pcsc::Connection::Reader& reader(pcsc.reader(name));
|
|
|
|
#ifndef Q_OS_MAC
|
|
|
|
pcsc::Connection::Reader::Transaction lock(reader);
|
|
|
|
#endif
|
|
|
|
// first try to read version info
|
|
|
|
if (reader.transmit(0x00, 0xA4, 0x08, 0x0C, "\x3f\x00\x56\x49", 4)
|
|
|
|
!= std::string("\x90\x00", 2) || !reader) {
|
|
|
|
LOG<<"Select File failed";
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
std::string res(reader.transmit(0x00, 0xB0, 0x00, 0x00));
|
|
|
|
if (res.substr(res.size()-2)!=std::string("\x90\x00", 2)) {
|
|
|
|
LOG<<"read error";
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
LOG<<"version text is: "<<res.substr(4, res[3]).c_str();
|
|
|
|
// if (res.substr(4, res[3]) != "RAPost 2009" &&
|
|
|
|
// res.substr(4, res[3]) != "RAPost 2010") {
|
|
|
|
// LOG<<"unsupported card";
|
|
|
|
// return -2;
|
|
|
|
// }
|
|
|
|
if (retCode(reader.transmit(0x00, 0xA4, 0x00, 0x0C)) == 0x9000) {
|
|
|
|
int value(retCode(reader.transmit(0x00, 0x20, 0x00, 0x81)));
|
|
|
|
if ((value&0x63C0)==0x63C0) return value&0x0F;
|
|
|
|
} else {
|
|
|
|
LOG<<"**** ERROR in select MF while reading pin status";
|
|
|
|
}
|
|
|
|
return -1; // locked
|
|
|
|
} catch (const std::exception& x) {
|
|
|
|
LOG<<"**** ERROR while reading pin status: "<<x.what();
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int retCode(const std::string& res) {
|
|
|
|
if (res.size()>=2)
|
|
|
|
return ((((unsigned int)(unsigned char)res[res.size()-2])*256)
|
|
|
|
+((unsigned int)(unsigned char)res[res.size()-1]));
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
struct CertInfo {
|
|
|
|
CertInfo(std::string d, cryptoki::SlotList::iterator s,
|
|
|
|
cryptoki::Attribute i):
|
|
|
|
data(d), slot(s), id(i) {
|
|
|
|
}
|
|
|
|
std::string data;
|
|
|
|
cryptoki::SlotList::iterator slot;
|
|
|
|
cryptoki::Attribute id;
|
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
cryptoki::SlotList _slots;
|
|
|
|
std::auto_ptr<cryptoki::Session> _session;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // SMARTCARDAUTH_H
|