#ifndef SMARTCARDAUTH_H #define SMARTCARDAUTH_H #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, QWidget* p): _cryptoki(lib), _parent(p) { OPENSSL_LOG("log"); } operator bool() { return _privateKey.get() && _cert.get(); } cryptoki::Init& cryptoki() { return _cryptoki; } void cert(cryptoki::Object privateKey, std::auto_ptr c) { _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) { std::string inModded(in); OPENSSL_LOG("log"); OPENSSL_LOG("type="< sigsize)) { // throw std::runtime_error("the size is wrong"); // } // } else { // OPENSSL_LOG("It's not SSL"); // ASN1_TYPE parameter; // parameter.type = V_ASN1_NULL; // parameter.value.ptr = 0; // X509_ALGOR algor; // algor.algorithm = OBJ_nid2obj(type); // algor.parameter = ¶meter; // ASN1_STRING digest; // digest.data = (unsigned char *)inModded.data(); // digest.length = inModded.size(); // X509_SIG sig; // sig.algor = &algor; // sig.digest = &digest; // int size (i2d_X509_SIG(&sig, 0)); // if (!algor.algorithm) throw std::runtime_error("algor wrong nid"); // if (!algor.algorithm->length) // throw std::runtime_error("algor length"); // if (size) throw std::runtime_error("digest size"); // if (size + RSA_PKCS1_PADDING_SIZE < sigsize) // throw std::runtime_error("incompatible size"); // unsigned char* buf(new unsigned char[size]); // i2d_X509_SIG(&sig, &buf); // inModded.assign((char*)buf, size); // delete[] buf; // } // } // //------------------------------------------------- PKCS11_private_encrypt // { // char padding(RSA_PKCS1_PADDING); // if ((inModded.size() + RSA_PKCS1_PADDING_SIZE) > sigsize) // throw std::runtime_error("the size is wrong"); // } // //----------------------------------------------------------------------- // if (in!=inModded) // OPENSSL_LOG("changed input"<sign(inModded, CKM_RSA_PKCS); } private: cryptoki::Init _cryptoki; std::string _modulus; std::string _exponent; std::auto_ptr _privateKey; std::auto_ptr _cert; public: void login() { QMutexLocker lock(&_mutex); try { QList authcerts; QList allcerts; QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration()); QList cacerts(sslConfig.caCertificates()); _slots = _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(QSslCertificate (QByteArray(data.data(), data.size()), QSsl::Der)); } 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"); cert(keys[0], std::auto_ptr (new openssl::X509(c.data))); sslConfig.setCaCertificates(cacerts); break; } } catch (std::exception& x) { pinEntry.pin().clear(); OPENSSL_LOG("**** ERROR"< _session; QMutex _mutex; std::auto_ptr _pinEntry; }; class SmartCardAuth: public QObject { Q_OBJECT; public: SmartCardAuth(const QString& lib, QWidget* p=0, bool login=true): _reg(e(lib, p)) { qDebug()<<__PRETTY_FUNCTION__; if (login) e()->login(); //assert(connect(e(), SIGNAL(certRequired()), SLOT(login()))); } private Q_SLOTS: void extendedContextInitialization(ssl_ctx_st* ctx, QSslSocket* socket) { qDebug()<<__PRETTY_FUNCTION__; SSL_CTX_set_client_cert_cb(ctx, clientCert); } private: static int clientCert(SSL* ssl, X509 **x509, EVP_PKEY **pkey) { qDebug()<<__PRETTY_FUNCTION__; if (!*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(), QWidget* p = 0) { static CryptokiEngine* _e(new CryptokiEngine(lib.toStdString(), p)); return _e; } private: openssl::RegisterEngine _reg; //std::map sockets; }; #endif // SMARTCARDAUTH_H