#ifndef SMARTCARDAUTH_H #define SMARTCARDAUTH_H #include #include #include #include #include #include #include #include #include #include extern const QByteArray SWISSSIGN_GOLD_CA_G2; 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") <<(_cert.get()?"cert defined":"cert undefined")); return _privateKey.get() && _cert.get(); } cryptoki::Init& cryptoki() { return _cryptoki; } void cert(cryptoki::Object privateKey, std::auto_ptr c) { OPENSSL_LOG("log"); _cert = c; _privateKey = std::auto_ptr (new cryptoki::Object(privateKey)); _modulus = privateKey.attribute(CKA_MODULUS).value; _exponent = privateKey.attribute(CKA_PUBLIC_EXPONENT).value; } const openssl::X509& cert() { return *_cert; } virtual EVP_PKEY* privkey() { OPENSSL_LOG("log"); EVP_PKEY* k(EVP_PKEY_new()); RSA* r(RSA_new_method(_e)); r->n = BN_bin2bn((const unsigned char*)_modulus.data(), _modulus.size(), r->n); r->e = BN_bin2bn((const unsigned char*)_exponent.data(), _exponent.size(), r->e); // otherwise OpenSSL emulates sign/verify with encrypt/decrypt r->flags |= RSA_FLAG_SIGN_VER; EVP_PKEY_set1_RSA(k, r); OPENSSL_LOG("RSA_free"); RSA_free(r); return k; } protected: virtual const char* id() { OPENSSL_LOG("log"); return "CryptokiEngine_ID"; } virtual const char* name() { OPENSSL_LOG("log"); return "CryptokiEngine_NAME"; } virtual std::string rsaSign(const std::string& in, unsigned int type) { OPENSSL_LOG("log"); OPENSSL_LOG("type="<sign(in, CKM_RSA_PKCS); } private: cryptoki::Init _cryptoki; std::string _modulus; std::string _exponent; std::auto_ptr _privateKey; std::auto_ptr _cert; }; class SmartCardAuth: public QObject { Q_OBJECT; public: SmartCardAuth(const QString& lib, QWidget* p=0, bool loginAtStart=true): _reg(e(lib)), _parent(p) { qDebug()<<__PRETTY_FUNCTION__; if (loginAtStart) login(); //assert(connect(e(), SIGNAL(certRequired()), SLOT(login()))); } private Q_SLOTS: void extendedContextInitialization(ssl_ctx_st* ctx, QSslSocket* socket) { qDebug()<<__PRETTY_FUNCTION__; QList expectedSslErrors; for (std::list::iterator cert(_cacerts.begin()); cert!=_cacerts.end(); ++cert) { SSL_CTX_add_extra_chain_cert(ctx, openssl::X509(*cert).lowLevelCopy()); expectedSslErrors.push_back(QSslError(QSslError::SelfSignedCertificateInChain, QSslCertificate::fromData (QByteArray(cert->data(), cert->size()), QSsl::Der).at(0))); //qDebug()<<"Added:\n"<data(), cert->size()), QSsl::Der).toPem(); } //socket->ignoreSslErrors(expectedSslErrors); SSL_CTX_set_client_cert_cb(ctx, &SmartCardAuth::clientCert); } private: static int clientCert(SSL* ssl, X509 **x509, EVP_PKEY **pkey) { qDebug()<<__PRETTY_FUNCTION__; if (!e() || !*e()) return 0; // no certificate found qDebug()<<"*** A "<<__PRETTY_FUNCTION__; *x509 = e()->cert().lowLevelCopy(); qDebug()<<"*** B "<<__PRETTY_FUNCTION__; *pkey = e()->privkey(); qDebug()<<"*** C "<<__PRETTY_FUNCTION__; return 1; } static CryptokiEngine* e(const QString& lib = QString()) try { static CryptokiEngine* _e(new CryptokiEngine(lib.toStdString())); return _e; } catch (...) { return 0; } private: openssl::RegisterEngine _reg; //std::map sockets; public: void login(bool force=false) { QMutexLocker lock(&_mutex); if (!e() || (!force && *e())) return; // no smartcard or already logged in try { _cacerts.clear(); QList authcerts; QList allcerts; QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration()); _slots = e()->cryptoki().slotList(); 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"); _cacerts.push_back(data); } 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)); } } } } 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.exec()==PinEntry::Accepted) try { _session = 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], std::auto_ptr (new openssl::X509(c.data))); break; } } catch (std::exception& x) { pinEntry.pin().clear(); OPENSSL_LOG("**** ERROR"< _session; QMutex _mutex; std::list _cacerts; }; #endif // SMARTCARDAUTH_H