#ifndef SMARTCARDAUTH_H #define SMARTCARDAUTH_H #include #include #include #include #include #include #include #include #include #include class CryptokiEngine: public openssl::Engine { public: CryptokiEngine(std::string lib): _cryptoki(lib) { OPENSSL_LOG("log"); QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration()); QList cacerts(sslConfig.caCertificates()); QList authcerts; QList allcerts; QSslCertificate cert; _slots = _cryptoki.slotList(); for (cryptoki::SlotList::iterator slot(_slots.begin()); slot!=_slots.end() && _pin.isEmpty(); ++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() && _pin.isEmpty(); ++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); cacerts.push_back(QSslCertificate (QByteArray(data.data(), data.size()), QSsl::Der)); } else { if (label.value.find("auth")==0) { std::string data(cert->attribute(CKA_VALUE).value); QSslCertificate c(QByteArray(data.data(), data.size()), QSsl::Der); PinEntry pinEntry(c); /*! @todo set widget */ while (pinEntry.exec()==PinEntry::Accepted) try { cryptoki::Attribute value(cert->attribute(CKA_VALUE)); _cert = std::auto_ptr (new openssl::X509(value.value)); _session->login(pinEntry.pin().toStdString()); cryptoki::ObjectList keys (_session->find(cryptoki::Attribute(CKA_CLASS) .from(CKO_PRIVATE_KEY), id)); if (keys.size()==1) { OPENSSL_LOG("**** found one private key"); _privateKeys = keys; _modulus = keys[0].attribute(CKA_MODULUS).value; _exponent = keys[0].attribute(CKA_PUBLIC_EXPONENT).value; } _pin = pinEntry.pin(); break; } catch (std::exception& x) { _pin.clear(); OPENSSL_LOG("**** ERROR"<attribute(CKA_VALUE).value); // allcerts.push_back(QSslCertificate // (QByteArray(data.data(), data.size()), // QSsl::Der)); // if (label.value.find("auth")==0) { // authcerts.push_back(*allcerts.rbegin()); // } } } } sslConfig.setCaCertificates(cacerts); } //! Was initialization successful? operator bool() { return !_pin.isEmpty() && _cert.get() && _session.get(); } const openssl::X509& cert() { return *_cert; } virtual EVP_PKEY* privkey(const char* id, UI_METHOD*, void*) { 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); 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"< _session; QString _pin; std::auto_ptr _cert; std::string _modulus; std::string _exponent; cryptoki::ObjectList _privateKeys; }; class SmartCardAuth: public QObject { Q_OBJECT; public: SmartCardAuth(const QString& lib): _reg(e(lib)) { qDebug()<<__PRETTY_FUNCTION__; } private Q_SLOTS: void extendedContextInitialization(ssl_ctx_st* ctx, QSslSocket* socket) { qDebug()<<__PRETTY_FUNCTION__; if (!*e()) return; // no certificate found SSL_CTX_set_client_cert_cb(ctx, clientCert); } static int clientCert(SSL* ssl, X509 **x509, EVP_PKEY **pkey) { qDebug()<<__PRETTY_FUNCTION__; *x509 = e()->cert().lowLevelCopy(); *pkey = e()->privkey(0, 0, 0); return 1; } static CryptokiEngine* e(const QString& lib = QString()) { static CryptokiEngine* _e(new CryptokiEngine(lib.toStdString())); return _e; } private: openssl::RegisterEngine _reg; //std::map sockets; }; #endif // SMARTCARDAUTH_H