This commit is contained in:
@@ -3,20 +3,239 @@
|
||||
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtNetwork/QSslSocket>
|
||||
#include <QtNetwork/QSslConfiguration>
|
||||
#include <QtNetwork/QSslCertificate>
|
||||
#include <QtGui/QMessageBox>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <pinentry.hxx>
|
||||
|
||||
#include <cryptoki.hxx>
|
||||
#include <openssl-engine.hxx>
|
||||
#include <openssl.hxx>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class CryptokiEngine: public openssl::Engine {
|
||||
public:
|
||||
CryptokiEngine(std::string lib):
|
||||
_cryptoki(lib) {
|
||||
OPENSSL_LOG("log");
|
||||
QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration());
|
||||
QList<QSslCertificate> cacerts(sslConfig.caCertificates());
|
||||
QList<QSslCertificate> authcerts;
|
||||
QList<QSslCertificate> allcerts;
|
||||
QSslCertificate cert;
|
||||
_slots = _cryptoki.slotList();
|
||||
for (cryptoki::SlotList::iterator slot(_slots.begin());
|
||||
slot!=_slots.end() && _pin.isEmpty(); ++slot) {
|
||||
_session =
|
||||
std::auto_ptr<cryptoki::Session>(new cryptoki::Session(*slot));
|
||||
cryptoki::ObjectList certs(_session->find
|
||||
(cryptoki::Attribute(CKA_CLASS)
|
||||
.from<CK_OBJECT_CLASS>(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: "<<label.value);
|
||||
cryptoki::ObjectList keys
|
||||
(_session->find(cryptoki::Attribute(CKA_CLASS)
|
||||
.from<CK_OBJECT_CLASS>(CKO_PUBLIC_KEY),
|
||||
id));
|
||||
OPENSSL_LOG("**** with keys: "<<keys.size());
|
||||
if (!keys.size()) { // add CA-certificate
|
||||
std::string data(cert->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<openssl::X509>
|
||||
(new openssl::X509(value.value));
|
||||
_session->login(pinEntry.pin().toStdString());
|
||||
cryptoki::ObjectList keys
|
||||
(_session->find(cryptoki::Attribute(CKA_CLASS)
|
||||
.from<CK_OBJECT_CLASS>(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"<<x.what());
|
||||
QMessageBox::critical(0, QMessageBox::tr("Wrong PIN"),
|
||||
QMessageBox::tr("Authentication failed,"
|
||||
" please try again."));
|
||||
}
|
||||
}
|
||||
//! @todo show certificate dialog
|
||||
// std::string data(cert->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="<<type);
|
||||
// CK_MECHANISM_TYPE mech(0);
|
||||
// switch (type) {
|
||||
// case NID_idea_ecb: mech=CKM_IDEA_ECB; break;
|
||||
// default: throw("unknown key mechanism");
|
||||
// }
|
||||
|
||||
//------------------------------------------------------------ PKCS11_sign
|
||||
int sigsize(openssl::BigNum(_modulus).size());
|
||||
{
|
||||
int ssl = ((type == NID_md5_sha1) ? 1 : 0);
|
||||
unsigned char *encoded = NULL;
|
||||
|
||||
if (ssl) {
|
||||
OPENSSL_LOG("It's SSL");
|
||||
if((inModded.size() != 36) /* SHA1 + MD5 */ ||
|
||||
((inModded.size() + RSA_PKCS1_PADDING_SIZE) > 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"<<std::endl
|
||||
<<"from:"<<std::endl
|
||||
<<crypto::readable(in)<<std::endl
|
||||
<<"to:"<<std::endl
|
||||
<<crypto::readable(inModded));
|
||||
return _privateKeys[0].sign(inModded, CKM_RSA_PKCS);
|
||||
}
|
||||
private:
|
||||
cryptoki::Init _cryptoki;
|
||||
cryptoki::SlotList _slots;
|
||||
std::auto_ptr<cryptoki::Session> _session;
|
||||
QString _pin;
|
||||
std::auto_ptr<openssl::X509> _cert;
|
||||
std::string _modulus;
|
||||
std::string _exponent;
|
||||
cryptoki::ObjectList _privateKeys;
|
||||
};
|
||||
|
||||
class SmartCardAuth: public QObject {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
|
||||
SmartCardAuth(const QString& actlib);
|
||||
~SmartCardAuth();
|
||||
|
||||
SmartCardAuth(const QString& lib):
|
||||
_reg(e(lib)) {
|
||||
qDebug()<<__PRETTY_FUNCTION__;
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
void extendedContextInitialization(ssl_ctx_st*, QSslSocket*);
|
||||
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<ssl_ctx_st*, QSslSocket*> sockets;
|
||||
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user