|
|
|
#include<tqapplication.h>
|
|
|
|
#include<tqdom.h>
|
|
|
|
#include<tqfile.h>
|
|
|
|
#include<tqsocket.h>
|
|
|
|
#include<tqptrlist.h>
|
|
|
|
#include"base64.h"
|
|
|
|
#include"qca.h"
|
|
|
|
|
|
|
|
TQCA::Cert readCertXml(const TQDomElement &e)
|
|
|
|
{
|
|
|
|
TQCA::Cert cert;
|
|
|
|
// there should be one child data tag
|
|
|
|
TQDomElement data = e.elementsByTagName("data").item(0).toElement();
|
|
|
|
if(!data.isNull())
|
|
|
|
cert.fromDER(Base64::stringToArray(data.text()));
|
|
|
|
return cert;
|
|
|
|
}
|
|
|
|
|
|
|
|
void showCertInfo(const TQCA::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());
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPtrList<TQCA::Cert> getRootCerts(const TQString &store)
|
|
|
|
{
|
|
|
|
TQPtrList<TQCA::Cert> list;
|
|
|
|
|
|
|
|
// open the Psi rootcerts file
|
|
|
|
TQFile f(store);
|
|
|
|
if(!f.open(IO_ReadOnly)) {
|
|
|
|
printf("unable to open %s\n", f.name().latin1());
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
TQDomDocument doc;
|
|
|
|
doc.setContent(&f);
|
|
|
|
f.close();
|
|
|
|
|
|
|
|
TQDomElement base = doc.documentElement();
|
|
|
|
if(base.tagName() != "store") {
|
|
|
|
printf("wrong format of %s\n", f.name().latin1());
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
TQDomNodeList 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) {
|
|
|
|
TQCA::Cert *cert = new TQCA::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;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString resultToString(int result)
|
|
|
|
{
|
|
|
|
TQString s;
|
|
|
|
switch(result) {
|
|
|
|
case TQCA::TLS::NoCert:
|
|
|
|
s = TQObject::tr("No certificate presented.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::Valid:
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::HostMismatch:
|
|
|
|
s = TQObject::tr("Hostname mismatch.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::Rejected:
|
|
|
|
s = TQObject::tr("Root CA rejects the specified purpose.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::Untrusted:
|
|
|
|
s = TQObject::tr("Not trusted for the specified purpose.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::SignatureFailed:
|
|
|
|
s = TQObject::tr("Invalid signature.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::InvalidCA:
|
|
|
|
s = TQObject::tr("Invalid CA certificate.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::InvalidPurpose:
|
|
|
|
s = TQObject::tr("Invalid certificate purpose.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::SelfSigned:
|
|
|
|
s = TQObject::tr("Certificate is self-signed.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::Revoked:
|
|
|
|
s = TQObject::tr("Certificate has been revoked.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::PathLengthExceeded:
|
|
|
|
s = TQObject::tr("Maximum cert chain length exceeded.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::Expired:
|
|
|
|
s = TQObject::tr("Certificate has expired.");
|
|
|
|
break;
|
|
|
|
case TQCA::TLS::Unknown:
|
|
|
|
default:
|
|
|
|
s = TQObject::tr("General validation error.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
class SecureTest : public TQObject
|
|
|
|
{
|
|
|
|
TQ_OBJECT
|
|
|
|
public:
|
|
|
|
SecureTest()
|
|
|
|
{
|
|
|
|
sock = new TQSocket;
|
|
|
|
connect(sock, TQ_SIGNAL(connected()), TQ_SLOT(sock_connected()));
|
|
|
|
connect(sock, TQ_SIGNAL(readyRead()), TQ_SLOT(sock_readyRead()));
|
|
|
|
connect(sock, TQ_SIGNAL(connectionClosed()), TQ_SLOT(sock_connectionClosed()));
|
|
|
|
connect(sock, TQ_SIGNAL(error(int)), TQ_SLOT(sock_error(int)));
|
|
|
|
|
|
|
|
ssl = new TQCA::TLS;
|
|
|
|
connect(ssl, TQ_SIGNAL(handshaken()), TQ_SLOT(ssl_handshaken()));
|
|
|
|
connect(ssl, TQ_SIGNAL(readyRead()), TQ_SLOT(ssl_readyRead()));
|
|
|
|
connect(ssl, TQ_SIGNAL(readyReadOutgoing(int)), TQ_SLOT(ssl_readyReadOutgoing(int)));
|
|
|
|
connect(ssl, TQ_SIGNAL(closed()), TQ_SLOT(ssl_closed()));
|
|
|
|
connect(ssl, TQ_SIGNAL(error(int)), TQ_SLOT(ssl_error(int)));
|
|
|
|
|
|
|
|
rootCerts.setAutoDelete(true);
|
|
|
|
rootCerts = getRootCerts("/usr/local/share/psi/certs/rootcert.xml");
|
|
|
|
}
|
|
|
|
|
|
|
|
~SecureTest()
|
|
|
|
{
|
|
|
|
delete ssl;
|
|
|
|
delete sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
void start(const TQString &_host)
|
|
|
|
{
|
|
|
|
int n = _host.find(':');
|
|
|
|
int port;
|
|
|
|
if(n != -1) {
|
|
|
|
host = _host.mid(0, n);
|
|
|
|
port = _host.mid(n+1).toInt();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
host = _host;
|
|
|
|
port = 443;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Trying %s:%d...\n", host.latin1(), port);
|
|
|
|
sock->connectToHost(host, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void quit();
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
void sock_connected()
|
|
|
|
{
|
|
|
|
printf("Connected, starting TLS handshake...\n");
|
|
|
|
ssl->setCertificateStore(rootCerts);
|
|
|
|
ssl->startClient(host);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sock_readyRead()
|
|
|
|
{
|
|
|
|
TQByteArray 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 sock_error(int)
|
|
|
|
{
|
|
|
|
printf("\nSocket error.\n");
|
|
|
|
quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ssl_handshaken()
|
|
|
|
{
|
|
|
|
cert = ssl->peerCertificate();
|
|
|
|
int vr = ssl->certificateValidityResult();
|
|
|
|
|
|
|
|
printf("Successful SSL handshake.\n");
|
|
|
|
if(!cert.isNull())
|
|
|
|
showCertInfo(cert);
|
|
|
|
if(vr == TQCA::TLS::Valid)
|
|
|
|
printf("Valid certificate.\n");
|
|
|
|
else
|
|
|
|
printf("Invalid certificate: %s\n", resultToString(vr).latin1());
|
|
|
|
|
|
|
|
printf("Let's try a GET request now.\n");
|
|
|
|
TQString req = "GET / HTTP/1.0\nHost: " + host + "\n\n";
|
|
|
|
TQCString cs = req.latin1();
|
|
|
|
TQByteArray buf(cs.length());
|
|
|
|
memcpy(buf.data(), cs.data(), buf.size());
|
|
|
|
ssl->write(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ssl_readyRead()
|
|
|
|
{
|
|
|
|
TQByteArray a = ssl->read();
|
|
|
|
TQCString cs;
|
|
|
|
cs.resize(a.size()+1);
|
|
|
|
memcpy(cs.data(), a.data(), a.size());
|
|
|
|
printf("%s", cs.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ssl_readyReadOutgoing(int)
|
|
|
|
{
|
|
|
|
TQByteArray a = ssl->readOutgoing();
|
|
|
|
sock->writeBlock(a.data(), a.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ssl_closed()
|
|
|
|
{
|
|
|
|
printf("SSL session closed\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ssl_error(int x)
|
|
|
|
{
|
|
|
|
if(x == TQCA::TLS::ErrHandshake) {
|
|
|
|
printf("SSL Handshake Error!\n");
|
|
|
|
quit();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
printf("SSL Error!\n");
|
|
|
|
quit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
TQString host;
|
|
|
|
TQSocket *sock;
|
|
|
|
TQCA::TLS *ssl;
|
|
|
|
TQCA::Cert cert;
|
|
|
|
TQPtrList<TQCA::Cert> rootCerts;
|
|
|
|
};
|
|
|
|
|
|
|
|
#include"ssltest.moc"
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
TQApplication app(argc, argv, false);
|
|
|
|
TQString host = argc > 1 ? argv[1] : "andbit.net";
|
|
|
|
|
|
|
|
if(!TQCA::isSupported(TQCA::CAP_TLS)) {
|
|
|
|
printf("TLS not supported!\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
SecureTest *s = new SecureTest;
|
|
|
|
TQObject::connect(s, TQ_SIGNAL(quit()), &app, TQ_SLOT(quit()));
|
|
|
|
s->start(host);
|
|
|
|
app.exec();
|
|
|
|
delete s;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|