#ifndef SMARTCARDAUTH_H #define SMARTCARDAUTH_H #include #include #include #include #include #include #include #include #include #include #include #include class CryptokiEngine: public QObject, public openssl::Engine { Q_OBJECT; Q_SIGNALS: void certRequired(); public: CryptokiEngine(std::string lib): _cryptoki(lib) { OPENSSL_LOG("log"); } operator bool() { OPENSSL_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) { OPENSSL_LOG("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()); 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(0); do { rsa = ((RSA*)privkey.handle()); RSA_free(rsa); //→ occasional crashes? rsa = RSA_new_method(_e); 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()); } while (rsa!=(RSA*)privkey.handle()); sslConfig.setPrivateKey(privkey); QSslConfiguration::setDefaultConfiguration(sslConfig); } catch (const std::exception& e) { OPENSSL_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) { qDebug()<<__PRETTY_FUNCTION__; if (loginAtStart) login(); assert(connect(_e, SIGNAL(certRequired()), SLOT(login()))); } public Q_SLOTS: void login(bool force=true) { qDebug()<<__PRETTY_FUNCTION__; QMutexLocker lock(&_mutex); if (!_e || (!force && *_e)) return; // no smartcard or already logged in try { QList authcerts; QList allcerts; QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration()); _slots = _e->cryptoki().slotList(); // look for login certificates ---------------------------------------- for (cryptoki::SlotList::iterator slot(_slots.begin()); slot!=_slots.end(); ++slot) { _session = std::auto_ptr(new cryptoki::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)); OPENSSL_LOG("**** FOUND CERTIFICATE: "<find(cryptoki::Attribute(CKA_CLASS) .from(CKO_PUBLIC_KEY), id)); OPENSSL_LOG("**** with keys: "<attribute(CKA_VALUE).value); if (!keys.size()) { // add CA-certificate OPENSSL_LOG("**** add to CA-certificates"); } else { OPENSSL_LOG("**** user cert, check for authentictaion"); if (label.value.find("auth")==0 || label.value.find("Authentication")!=std::string::npos) { OPENSSL_LOG("**** it's our authentication cert"); authcerts.push_back(CertInfo(data, slot, id)); } else { OPENSSL_LOG("**** it's an unknown cert"); allcerts.push_back(CertInfo(data, slot, id)); } } } } // get pin and install client certificate ------------------------------ if (!authcerts.isEmpty() || !allcerts.isEmpty()) { CertInfo c(authcerts.size()?authcerts[0]:allcerts[0]); PinEntry pinEntry(QSslCertificate(QByteArray(c.data.data(), c.data.size()), QSsl::Der), _parent); while (pinEntry.retries(retries(c.slot->slotinfo().slotDescription)) .exec()==PinEntry::Accepted) try { _session = // session login with pin std::auto_ptr (new cryptoki::Session(*c.slot)); _session->login(pinEntry.pin().toStdString()); cryptoki::ObjectList keys (_session->find(cryptoki::Attribute(CKA_CLASS) .from(CKO_PRIVATE_KEY), c.id)); if (keys.size()==1) { OPENSSL_LOG("**** found one private key"); _e->cert(keys[0], c.data); // install client cert break; } } catch (std::exception& x) { pinEntry.pin().clear(); OPENSSL_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; }; private: QWidget* _parent; CryptokiEngine* _e; openssl::RegisterEngine _reg; cryptoki::SlotList _slots; std::auto_ptr _session; QMutex _mutex; //std::map sockets; // std::list _cacerts; }; #endif // SMARTCARDAUTH_H