4
0
mirror of https://github.com/QuasarApp/qca.git synced 2025-05-08 16:59:33 +00:00

ssl should work now

svn path=/trunk/kdesupport/qca/; revision=238705
This commit is contained in:
Justin Karneges 2003-07-23 01:22:46 +00:00
parent 273c717362
commit 2c5ec0260e
9 changed files with 390 additions and 52 deletions

9
TODO

@ -1,4 +1,11 @@
* ssl/tls (also server support)
* ciphertest: segfault??
* rsatest: segfault??
* ssltest: segfault on exit??
* cipher: option to specify size for genkey
* cert: properly check hostnames with wildcards
* ssl: server support
* sasl (also server support)
* dsa

@ -8,6 +8,10 @@ UI_DIR = .ui
INCLUDEPATH += src
INCLUDEPATH += plugins
HEADERS += src/qca.h
HEADERS += src/qca.h src/qcaprovider.h
SOURCES += hashtest.cpp src/qca.cpp
#DEFINES += USE_OPENSSL
#SOURCES += plugins/qcaopenssl.cpp
#LIBS += -lcrypto -lssl

@ -13,6 +13,8 @@
#include<openssl/ssl.h>
#include<openssl/err.h>
#define NO_AES
// FIXME: use openssl for entropy instead of stdlib
#include<stdlib.h>
static bool seeded = false;
@ -316,6 +318,7 @@ public:
}
};
#ifndef NO_AES
class AES128Context : public EVPCipherContext
{
public:
@ -345,6 +348,7 @@ public:
return 0;
}
};
#endif
class RSAKeyContext : public QCA_RSAKeyContext
{
@ -751,7 +755,7 @@ public:
reset();
}
QCA_CertContext *clone()
QCA_CertContext *clone() const
{
CertContext *c = new CertContext(*this);
if(x) {
@ -915,6 +919,7 @@ public:
static bool ssl_init = false;
class SSLContext : public QCA_SSLContext
{
Q_OBJECT
public:
enum { Success, TryAgain, Error };
enum { Idle, Connect, Handshake, Active };
@ -951,6 +956,7 @@ public:
recvQueue.resize(0);
mode = Idle;
cc.reset();
vr = QCA::SSL::Unknown;
}
bool begin(const QString &_host, const QPtrList<QCA_CertContext> &list)
@ -1045,7 +1051,7 @@ public:
}
else if(ret == Error) {
reset();
// FIXME: handshaken(false);
handshaken(false);
return;
}
}
@ -1073,27 +1079,28 @@ public:
cc.reset();
code = QCA::SSL::NoCert;
}
validityResult = code;
vr = code;
mode = Active;
// FIXME: handshaken(true);
handshaken(true);
}
else if(ret == Error) {
reset();
// FIXME: handshaken(false);
handshaken(false);
return;
}
}
//if(isOutgoingSSLData()) {
// outgoingSSLDataReady();
//}
if(outgoingDataReady()) {
readyReadOutgoing();
}
// try to read incoming unencrypted data
sslReadAll();
//if(isRecvData())
// readyRead();
if(dataReady()) {
readyRead();
}
}
int resultToCV(int ret) const
@ -1154,11 +1161,16 @@ public:
return rc;
}
QCA_CertContext *peerCertificate()
QCA_CertContext *peerCertificate() const
{
return cc.clone();
}
int validityResult() const
{
return vr;
}
bool dataReady() const
{
return (recvQueue.size() > 0) ? true: false;
@ -1169,27 +1181,22 @@ public:
return (BIO_pending(wbio) > 0) ? true: false;
}
/*void putIncomingSSLData(const QByteArray &a)
void writeIncoming(const QByteArray &a)
{
BIO_write(d->rbio, a.data(), a.size());
BIO_write(rbio, a.data(), a.size());
sslUpdate();
}
bool isOutgoingSSLData()
{
return (BIO_pending(d->wbio) > 0) ? true: false;
}
QByteArray getOutgoingSSLData()
QByteArray readOutgoing()
{
QByteArray a;
int size = BIO_pending(d->wbio);
int size = BIO_pending(wbio);
if(size <= 0)
return a;
a.resize(size);
int r = BIO_read(d->wbio, a.data(), size);
int r = BIO_read(wbio, a.data(), size);
if(r <= 0) {
a.resize(0);
return a;
@ -1200,30 +1207,20 @@ public:
return a;
}
void send(const QByteArray &a)
void write(const QByteArray &a)
{
if(d->mode != Active)
if(mode != Active)
return;
int oldsize = d->sendQueue.size();
d->sendQueue.resize(oldsize + a.size());
memcpy(d->sendQueue.data() + oldsize, a.data(), a.size());
appendArray(&sendQueue, a);
processSendQueue();
}
bool isRecvData()
QByteArray read()
{
return (d->recvQueue.size() > 0) ? true: false;
}
QByteArray recv()
{
QByteArray a = d->recvQueue;
a.detach();
d->recvQueue.resize(0);
QByteArray a = recvQueue.copy();
recvQueue.resize(0);
return a;
}*/
}
void sslReadAll()
{
@ -1258,7 +1255,7 @@ public:
BIO *rbio, *wbio;
QString host;
CertContext cc;
int validityResult;
int vr;
};
class QCAOpenSSL : public QCAProvider
@ -1274,8 +1271,10 @@ public:
QCA::CAP_MD5 |
QCA::CAP_BlowFish |
QCA::CAP_TripleDES |
#ifndef NO_AES
QCA::CAP_AES128 |
QCA::CAP_AES256 |
#endif
QCA::CAP_RSA |
QCA::CAP_X509 |
QCA::CAP_SSL;
@ -1292,10 +1291,12 @@ public:
return new BlowFishContext;
else if(cap == QCA::CAP_TripleDES)
return new TripleDESContext;
#ifndef NO_AES
else if(cap == QCA::CAP_AES128)
return new AES128Context;
else if(cap == QCA::CAP_AES256)
return new AES256Context;
#endif
else if(cap == QCA::CAP_RSA)
return new RSAKeyContext;
else if(cap == QCA::CAP_X509)
@ -1314,3 +1315,5 @@ QCAProvider *createProviderOpenSSL()
{
return (new QCAOpenSSL);
}
#include"qcaopenssl.moc"

@ -6,16 +6,13 @@ TARGET = qcaopenssl
INCLUDEPATH += ../src
# Justin
INCLUDEPATH += /usr/local/include
# RH 9
INCLUDEPATH += /usr/kerberos/include
HEADERS = qcaopenssl.h
HEADERS = ../src/qcaprovider.h qcaopenssl.h
SOURCES = qcaopenssl.cpp
DEFINES += QCA_PLUGIN
# link with OpenSSL
LIBS += -L/usr/local/lib -lcrypto
LIBS += -lcrypto -lssl

@ -599,6 +599,12 @@ Cert::~Cert()
delete d;
}
void Cert::fromContext(QCA_CertContext *ctx)
{
delete d->c;
d->c = ctx;
}
bool Cert::isNull() const
{
return d->c->isNull();
@ -715,6 +721,9 @@ public:
SSL::SSL()
{
d = new Private;
connect(d->c, SIGNAL(handshaken(bool)), SLOT(ctx_handshaken(bool)));
connect(d->c, SIGNAL(readyRead()), SLOT(ctx_readyRead()));
connect(d->c, SIGNAL(readyReadOutgoing()), SLOT(ctx_readyReadOutgoing()));
}
SSL::~SSL()
@ -724,25 +733,41 @@ SSL::~SSL()
bool SSL::begin(const QString &host, const QPtrList<Cert> &store)
{
return false;
d->cert = Cert();
// convert the cert list into a context list
QPtrList<QCA_CertContext> list;
QPtrListIterator<Cert> it(store);
for(Cert *cert; (cert = it.current()); ++it)
list.append(cert->d->c);
// begin!
if(!d->c->begin(host, list))
return false;
// we don't need this anymore
list.setAutoDelete(true);
return true;
}
void SSL::write(const QByteArray &a)
{
d->c->write(a);
}
QByteArray SSL::read()
{
return QByteArray();
return d->c->read();
}
void SSL::writeIncoming(const QByteArray &a)
{
d->c->writeIncoming(a);
}
QByteArray SSL::readOutgoing()
{
return QByteArray();
return d->c->readOutgoing();
}
const Cert & SSL::peerCertificate() const
@ -752,5 +777,25 @@ const Cert & SSL::peerCertificate() const
int SSL::certificateValidityResult() const
{
return NoCert;
return d->c->validityResult();
}
void SSL::ctx_handshaken(bool b)
{
if(b) {
// read the cert
QCA_CertContext *cc = d->c->peerCertificate();
d->cert.fromContext(cc);
}
handshaken(b);
}
void SSL::ctx_readyRead()
{
readyRead();
}
void SSL::ctx_readyReadOutgoing()
{
readyReadOutgoing();
}

@ -10,6 +10,7 @@
class QCA_HashContext;
class QCA_CipherContext;
class QCA_CertContext;
namespace QCA
{
@ -254,6 +255,9 @@ namespace QCA
private:
class Private;
Private *d;
friend class SSL;
void fromContext(QCA_CertContext *);
};
class SSL : public QObject
@ -298,6 +302,11 @@ namespace QCA
void readyRead();
void readyReadOutgoing();
private slots:
void ctx_handshaken(bool);
void ctx_readyRead();
void ctx_readyReadOutgoing();
private:
class Private;
Private *d;

@ -4,6 +4,7 @@
#include<qglobal.h>
#include<qstring.h>
#include<qdatetime.h>
#include<qobject.h>
#include"qca.h"
#ifdef Q_WS_WIN
@ -80,7 +81,7 @@ class QCA_CertContext
public:
virtual ~QCA_CertContext() {}
virtual QCA_CertContext *clone()=0;
virtual QCA_CertContext *clone() const=0;
virtual bool isNull() const=0;
virtual bool createFromDER(const char *in, unsigned int len)=0;
virtual bool createFromPEM(const char *in, unsigned int len)=0;
@ -96,12 +97,25 @@ public:
virtual QDateTime notAfter() const=0;
};
class QCA_SSLContext
class QCA_SSLContext : public QObject
{
Q_OBJECT
public:
virtual ~QCA_SSLContext() {}
virtual bool begin(const QString &host, const QPtrList<QCA_CertContext> &store)=0;
virtual void writeIncoming(const QByteArray &a)=0;
virtual QByteArray readOutgoing()=0;
virtual void write(const QByteArray &a)=0;
virtual QByteArray read()=0;
virtual QCA_CertContext *peerCertificate() const=0;
virtual int validityResult() const=0;
signals:
void handshaken(bool);
void readyRead();
void readyReadOutgoing();
};
#endif

246
ssltest.cpp Normal file

@ -0,0 +1,246 @@
#include<qapplication.h>
#include<qdom.h>
#include<qfile.h>
#include<qsocket.h>
#include<qptrlist.h>
#include"base64.h"
#include"qca.h"
QCA::Cert readCertXml(const QDomElement &e)
{
QCA::Cert cert;
// there should be one child data tag
QDomElement data = e.elementsByTagName("data").item(0).toElement();
if(!data.isNull())
cert.fromDER(Base64::stringToArray(data.text()));
return cert;
}
void showCertInfo(const QCA::Cert &cert)
{
printf("-- Cert --\n");
printf(" CN: %s\n", cert.subject()["CN"].latin1());
printf(" Valid from: %s, until %s\n",
cert.notBefore().toString().latin1(),
cert.notAfter().toString().latin1());
printf(" PEM:\n%s\n", cert.toPEM().latin1());
}
QPtrList<QCA::Cert> getRootCerts()
{
QPtrList<QCA::Cert> list;
// open the Psi rootcerts file
QFile f("/usr/local/share/psi/certs/rootcert.xml");
if(!f.open(IO_ReadOnly)) {
printf("unable to open %s\n", f.name().latin1());
return list;
}
QDomDocument doc;
doc.setContent(&f);
f.close();
QDomElement base = doc.documentElement();
if(base.tagName() != "store") {
printf("wrong format of %s\n", f.name().latin1());
return list;
}
QDomNodeList cl = base.elementsByTagName("certificate");
if(cl.count() == 0) {
printf("no certs found in %s\n", f.name().latin1());
return list;
}
int num = 0;
for(int n = 0; n < (int)cl.count(); ++n) {
QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
if(cert->isNull()) {
printf("error reading cert\n");
delete cert;
continue;
}
++num;
list.append(cert);
}
printf("imported %d root certs\n", num);
return list;
}
QString resultToString(int result)
{
QString s;
switch(result) {
case QCA::SSL::NoCert:
s = QObject::tr("The server did not present a certificate.");
break;
case QCA::SSL::Valid:
break;
case QCA::SSL::HostMismatch:
s = QObject::tr("The hostname does not match the one the certificate was issued to.");
break;
case QCA::SSL::Rejected:
s = QObject::tr("Root CA is marked to reject the specified purpose.");
break;
case QCA::SSL::Untrusted:
s = QObject::tr("Certificate not trusted for the required purpose.");
break;
case QCA::SSL::SignatureFailed:
s = QObject::tr("Invalid signature.");
break;
case QCA::SSL::InvalidCA:
s = QObject::tr("Invalid CA certificate.");
break;
case QCA::SSL::InvalidPurpose:
s = QObject::tr("Invalid certificate purpose.");
break;
case QCA::SSL::SelfSigned:
s = QObject::tr("Certificate is self-signed.");
break;
case QCA::SSL::Revoked:
s = QObject::tr("Certificate has been revoked.");
break;
case QCA::SSL::PathLengthExceeded:
s = QObject::tr("Maximum certificate chain length exceeded.");
break;
case QCA::SSL::Expired:
s = QObject::tr("Certificate has expired.");
break;
case QCA::SSL::Unknown:
default:
s = QObject::tr("General certificate validation error.");
break;
}
return s;
}
class SecureTest : public QObject
{
Q_OBJECT
public:
SecureTest()
{
sock = new QSocket;
connect(sock, SIGNAL(connected()), SLOT(sock_connected()));
connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
connect(sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
ssl = new QCA::SSL;
connect(ssl, SIGNAL(handshaken(bool)), SLOT(ssl_handshaken(bool)));
connect(ssl, SIGNAL(readyRead()), SLOT(ssl_readyRead()));
connect(ssl, SIGNAL(readyReadOutgoing()), SLOT(ssl_readyReadOutgoing()));
rootCerts = getRootCerts();
}
~SecureTest()
{
delete ssl;
rootCerts.setAutoDelete(true);
rootCerts.clear();
delete sock;
}
void start(const QString &_host)
{
host = _host;
printf("Trying %s:443...\n", host.latin1());
sock->connectToHost(host, 443);
}
signals:
void quit();
private slots:
void sock_connected()
{
printf("Connected, starting TLS handshake...\n");
ssl->begin(host, rootCerts);
}
void sock_readyRead()
{
QByteArray buf(sock->bytesAvailable());
int num = sock->readBlock(buf.data(), buf.size());
if(num < (int)buf.size())
buf.resize(num);
ssl->writeIncoming(buf);
}
void sock_connectionClosed()
{
printf("\nConnection closed.\n");
quit();
}
void ssl_handshaken(bool b)
{
if(b) {
cert = ssl->peerCertificate();
int vr = ssl->certificateValidityResult();
printf("Successful SSL handshake.\n");
if(!cert.isNull())
showCertInfo(cert);
if(vr == QCA::SSL::Valid)
printf("Valid certificate.\n");
else
printf("Invalid certificate: %s\n", resultToString(vr).latin1());
printf("Let's try a GET request now.\n");
QString req = "GET / HTTP/1.0\nHost: " + host + "\n\n";
QCString cs = req.latin1();
QByteArray buf(cs.length());
memcpy(buf.data(), cs.data(), buf.size());
ssl->write(buf);
}
else {
printf("SSL Handshake Error!\n");
quit();
}
}
void ssl_readyRead()
{
QByteArray a = ssl->read();
QCString cs;
cs.resize(a.size()+1);
memcpy(cs.data(), a.data(), a.size());
printf("%s", cs.data());
}
void ssl_readyReadOutgoing()
{
QByteArray a = ssl->readOutgoing();
sock->writeBlock(a.data(), a.size());
}
private:
QString host;
QSocket *sock;
QCA::SSL *ssl;
QCA::Cert cert;
QPtrList<QCA::Cert> rootCerts;
};
#include"ssltest.moc"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QString host = argc > 1 ? argv[1] : "andbit.net";
if(!QCA::isSupported(QCA::CAP_SSL)) {
printf("SSL not supported!\n");
return 1;
}
SecureTest *s = new SecureTest;
QObject::connect(s, SIGNAL(quit()), &app, SLOT(quit()));
s->start(host);
app.exec();
delete s;
return 0;
}

13
ssltest.pro Normal file

@ -0,0 +1,13 @@
TEMPLATE = app
CONFIG += thread
TARGET = ssltest
MOC_DIR = .moc
OBJECTS_DIR = .obj
UI_DIR = .ui
INCLUDEPATH += src
INCLUDEPATH += plugins
HEADERS += base64.h src/qca.h
SOURCES += base64.cpp ssltest.cpp src/qca.cpp