#ifndef SMARTCARDAUTH_H #define SMARTCARDAUTH_H #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef LOG #define LOG qDebug()<<__PRETTY_FUNCTION__ #endif class CryptokiEngine: public QObject, public openssl::Engine { Q_OBJECT; Q_SIGNALS: void certRequired(); public: CryptokiEngine(std::string lib): _cryptoki(lib) { LOG; } operator bool() { 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) { LOG; _privateKey = std::auto_ptr (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: "<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 _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) { LOG; if (loginAtStart) login(); assert(connect(_e, SIGNAL(certRequired()), SLOT(login()))); } public Q_SLOTS: void login(bool force=true) { try { LOG; Lock lock; LOG<<"got lock"; if (!_e || (!force && *_e)) return; // no smartcard or already logged in LOG<<"start login to smartcard"; QList authcerts; QList 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(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: "<(CKO_PUBLIC_KEY), id)); LOG<<"**** with keys: "<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 (new cryptoki::Session(*c.slot)); LOG<<"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; _session->login(pinEntry.pin().toStdString()); LOG<<"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"; cryptoki::ObjectList keys (_session->find(cryptoki::Attribute(CKA_CLASS) .from(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"<=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() { LOG<1) loops().back()->exec(); // wait } ~Lock() { LOG<exit(0); // wake up next } private: QList& loops() { // same as a static member variable static QList _loops; return _loops; } }; private: QWidget* _parent; CryptokiEngine* _e; openssl::RegisterEngine _reg; cryptoki::SlotList _slots; std::auto_ptr _session; }; #endif // SMARTCARDAUTH_H