diff options
author | Darrell Anderson <humanreadable@yahoo.com> | 2012-04-17 19:51:44 -0500 |
---|---|---|
committer | Darrell Anderson <humanreadable@yahoo.com> | 2012-04-17 19:51:44 -0500 |
commit | c663e43e7172bb80a8fb779e4dc74f579a60ec45 (patch) | |
tree | fd72db9c80e6c1aacf67e8bd24f75a4da57c4a2e /examples | |
download | tqca-c663e43e7172bb80a8fb779e4dc74f579a60ec45.tar.gz tqca-c663e43e7172bb80a8fb779e4dc74f579a60ec45.zip |
Added tqca package to the sources tree. This module is from
the original qca-1.0 sources with the TQt layer added.
This resolves bug report 817.
Diffstat (limited to 'examples')
-rw-r--r-- | examples/certtest/certtest.cpp | 65 | ||||
-rw-r--r-- | examples/certtest/certtest.pro | 8 | ||||
-rw-r--r-- | examples/ciphertest/ciphertest.cpp | 89 | ||||
-rw-r--r-- | examples/ciphertest/ciphertest.pro | 6 | ||||
-rw-r--r-- | examples/common/base64.cpp | 173 | ||||
-rw-r--r-- | examples/common/base64.h | 36 | ||||
-rw-r--r-- | examples/examples.pri | 7 | ||||
-rw-r--r-- | examples/examples.pro | 11 | ||||
-rw-r--r-- | examples/hashtest/hashtest.cpp | 25 | ||||
-rw-r--r-- | examples/hashtest/hashtest.pro | 6 | ||||
-rw-r--r-- | examples/rsatest/rsatest.cpp | 86 | ||||
-rw-r--r-- | examples/rsatest/rsatest.pro | 6 | ||||
-rw-r--r-- | examples/sasltest/sasltest.cpp | 615 | ||||
-rw-r--r-- | examples/sasltest/sasltest.pro | 8 | ||||
-rw-r--r-- | examples/sslservtest/sslservtest.cpp | 294 | ||||
-rw-r--r-- | examples/sslservtest/sslservtest.pro | 6 | ||||
-rw-r--r-- | examples/ssltest/ssltest.cpp | 276 | ||||
-rw-r--r-- | examples/ssltest/ssltest.pro | 8 |
18 files changed, 1725 insertions, 0 deletions
diff --git a/examples/certtest/certtest.cpp b/examples/certtest/certtest.cpp new file mode 100644 index 0000000..db82993 --- /dev/null +++ b/examples/certtest/certtest.cpp @@ -0,0 +1,65 @@ +#include<tqdom.h> +#include<tqfile.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(" 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()); +} + +int main() +{ + if(!TQCA::isSupported(TQCA::CAP_X509)) { + printf("X509 not supported!\n"); + return 1; + } + + // open the Psi rootcerts file + TQFile f("/usr/local/share/psi/certs/rootcert.xml"); + if(!f.open(IO_ReadOnly)) { + printf("unable to open %s\n", f.name().latin1()); + return 1; + } + 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 1; + } + TQDomNodeList cl = base.elementsByTagName("certificate"); + if(cl.count() == 0) { + printf("no certs found in %s\n", f.name().latin1()); + return 1; + } + + for(int n = 0; n < (int)cl.count(); ++n) { + printf("-- Cert %d --\n", n); + TQCA::Cert cert = readCertXml(cl.item(n).toElement()); + if(cert.isNull()) { + printf("error reading cert\n"); + continue; + } + showCertInfo(cert); + } + + return 0; +} + diff --git a/examples/certtest/certtest.pro b/examples/certtest/certtest.pro new file mode 100644 index 0000000..75d4141 --- /dev/null +++ b/examples/certtest/certtest.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += thread console +TARGET = certtest + +INCLUDEPATH += ../common +HEADERS += ../common/base64.h +SOURCES += ../common/base64.cpp certtest.cpp +include(../examples.pri) diff --git a/examples/ciphertest/ciphertest.cpp b/examples/ciphertest/ciphertest.cpp new file mode 100644 index 0000000..3f2107e --- /dev/null +++ b/examples/ciphertest/ciphertest.cpp @@ -0,0 +1,89 @@ +#include"qca.h" +#include<stdio.h> + +static TQCString arrayToCString(const TQByteArray &); +static TQByteArray cstringToArray(const TQCString &); +static void doDynTest(TQCA::Cipher *c, const TQString &name, const TQCString &cs); + +int main(int argc, char **argv) +{ + TQCA::init(); + TQCString cs = (argc >= 2) ? argv[1] : "hello"; + + // AES128 test + if(!TQCA::isSupported(TQCA::CAP_AES128)) + printf("AES128 not supported!\n"); + else { + // encrypt + TQByteArray key = TQCA::AES128::generateKey(); + TQByteArray iv = TQCA::AES128::generateIV(); + printf("aes128:key:%s\n", TQCA::arrayToHex(key).latin1()); + printf("aes128:iv:%s\n", TQCA::arrayToHex(iv).latin1()); + TQCA::AES128 c(TQCA::Encrypt, TQCA::CBC, key, iv); + c.update(cstringToArray(cs)); + TQByteArray f = c.final(); + TQString result = TQCA::arrayToHex(f); + printf(">aes128(\"%s\") = [%s]\n", cs.data(), result.latin1()); + + // decrypt + TQCA::AES128 d(TQCA::Decrypt, TQCA::CBC, key, iv); + d.update(f); + TQCString dec = arrayToCString(d.final()); + printf("<aes128(\"%s\") = [%s]\n", result.latin1(), dec.data()); + } + + // BlowFish, TripleDES, and AES256 tested dynamically + if(!TQCA::isSupported(TQCA::CAP_BlowFish)) + printf("BlowFish not supported!\n"); + else + doDynTest(new TQCA::BlowFish, "bfish", cs); + + if(!TQCA::isSupported(TQCA::CAP_TripleDES)) + printf("TripleDES not supported!\n"); + else + doDynTest(new TQCA::TripleDES, "3des", cs); + + if(!TQCA::isSupported(TQCA::CAP_AES256)) + printf("AES256 not supported!\n"); + else + doDynTest(new TQCA::AES256, "aes256", cs); + + return 0; +} + +TQCString arrayToCString(const TQByteArray &a) +{ + TQCString cs; + cs.resize(a.size()+1); + memcpy(cs.data(), a.data(), a.size()); + return cs; +} + +TQByteArray cstringToArray(const TQCString &cs) +{ + TQByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + return a; +} + +void doDynTest(TQCA::Cipher *c, const TQString &name, const TQCString &cs) +{ + // encrypt + TQByteArray key = c->dyn_generateKey(); + TQByteArray iv = c->dyn_generateIV(); + printf("%s:key:%s\n", name.latin1(), TQCA::arrayToHex(key).latin1()); + printf("%s:iv:%s\n", name.latin1(), TQCA::arrayToHex(iv).latin1()); + c->reset(TQCA::Encrypt, TQCA::CBC, key, iv); + c->update(cstringToArray(cs)); + TQByteArray f = c->final(); + TQString result = TQCA::arrayToHex(f); + printf(">%s(\"%s\") = [%s]\n", name.latin1(), cs.data(), result.latin1()); + + // decrypt + c->reset(TQCA::Decrypt, TQCA::CBC, key, iv); + c->update(f); + TQCString dec = arrayToCString(c->final()); + printf("<%s(\"%s\") = [%s]\n", name.latin1(), result.latin1(), dec.data()); + delete c; +} + diff --git a/examples/ciphertest/ciphertest.pro b/examples/ciphertest/ciphertest.pro new file mode 100644 index 0000000..c4119c5 --- /dev/null +++ b/examples/ciphertest/ciphertest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += thread console +TARGET = ciphertest + +SOURCES += ciphertest.cpp +include(../examples.pri) diff --git a/examples/common/base64.cpp b/examples/common/base64.cpp new file mode 100644 index 0000000..5938680 --- /dev/null +++ b/examples/common/base64.cpp @@ -0,0 +1,173 @@ +/* + * base64.cpp - Base64 converting functions + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"base64.h" + +//! \class Base64 base64.h +//! \brief Base64 conversion functions. +//! +//! Converts Base64 data between arrays and strings. +//! +//! \code +//! #include "base64.h" +//! +//! ... +//! +//! // encode a block of data into base64 +//! TQByteArray block(1024); +//! TQByteArray enc = Base64::encode(block); +//! +//! \endcode + +//! +//! Encodes array \a s and returns the result. +TQByteArray Base64::encode(const TQByteArray &s) +{ + int i; + int len = s.size(); + char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + int a, b, c; + + TQByteArray p((len+2)/3*4); + int at = 0; + for( i = 0; i < len; i += 3 ) { + a = ((unsigned char)s[i] & 3) << 4; + if(i + 1 < len) { + a += (unsigned char)s[i + 1] >> 4; + b = ((unsigned char)s[i + 1] & 0xF) << 2; + if(i + 2 < len) { + b += (unsigned char)s[i + 2] >> 6; + c = (unsigned char)s[i + 2] & 0x3F; + } + else + c = 64; + } + else + b = c = 64; + + p[at++] = tbl[(unsigned char)s[i] >> 2]; + p[at++] = tbl[a]; + p[at++] = tbl[b]; + p[at++] = tbl[c]; + } + return p; +} + +//! +//! Decodes array \a s and returns the result. +TQByteArray Base64::decode(const TQByteArray &s) +{ + // return value + TQByteArray p; + + // -1 specifies invalid + // 64 specifies eof + // everything else specifies data + + char tbl[] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + }; + + // this should be a multiple of 4 + int len = s.size(); + + if(len % 4) + return p; + + p.resize(len / 4 * 3); + + int i; + int at = 0; + + int a, b, c, d; + c = d = 0; + + for( i = 0; i < len; i += 4 ) { + a = tbl[(int)s[i]]; + b = tbl[(int)s[i + 1]]; + c = tbl[(int)s[i + 2]]; + d = tbl[(int)s[i + 3]]; + if((a == 64 || b == 64) || (a < 0 || b < 0 || c < 0 || d < 0)) { + p.resize(0); + return p; + } + p[at++] = ((a & 0x3F) << 2) | ((b >> 4) & 0x03); + p[at++] = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F); + p[at++] = ((c & 0x03) << 6) | ((d >> 0) & 0x3F); + } + + if(c & 64) + p.resize(at - 2); + else if(d & 64) + p.resize(at - 1); + + return p; +} + +//! +//! Encodes array \a a and returns the result as a string. +TQString Base64::arrayToString(const TQByteArray &a) +{ + TQByteArray b = encode(a); + TQCString c; + c.resize(b.size()+1); + memcpy(c.data(), b.data(), b.size()); + return TQString::fromLatin1(c); +} + +//! +//! Decodes string \a s and returns the result as an array. +TQByteArray Base64::stringToArray(const TQString &s) +{ + if(s.isEmpty()) + return TQByteArray(); + const char *c = s.latin1(); + int len = strlen(c); + TQByteArray b(len); + memcpy(b.data(), c, len); + TQByteArray a = decode(b); + return a; +} + +//! +//! Encodes string \a s and returns the result as a string. +TQString Base64::encodeString(const TQString &s) +{ + TQCString c = s.utf8(); + int len = c.length(); + TQByteArray b(len); + memcpy(b.data(), c.data(), len); + return arrayToString(b); +} diff --git a/examples/common/base64.h b/examples/common/base64.h new file mode 100644 index 0000000..93c6023 --- /dev/null +++ b/examples/common/base64.h @@ -0,0 +1,36 @@ +/* + * base64.h - Base64 converting functions + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_BASE64_H +#define CS_BASE64_H + +#include<qstring.h> + +class Base64 +{ +public: + static TQByteArray encode(const TQByteArray &); + static TQByteArray decode(const TQByteArray &); + static TQString arrayToString(const TQByteArray &); + static TQByteArray stringToArray(const TQString &); + static TQString encodeString(const TQString &); +}; + +#endif diff --git a/examples/examples.pri b/examples/examples.pri new file mode 100644 index 0000000..b1c4f91 --- /dev/null +++ b/examples/examples.pri @@ -0,0 +1,7 @@ +# change/remove these entries, depending on the installation prefix +INCLUDEPATH += /usr/local/include +LIBS += -L/usr/local/lib + +# link +LIBS += -lqca + diff --git a/examples/examples.pro b/examples/examples.pro new file mode 100644 index 0000000..457b282 --- /dev/null +++ b/examples/examples.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + hashtest \ + ciphertest \ + rsatest \ + certtest \ + ssltest \ + sslservtest \ + sasltest + diff --git a/examples/hashtest/hashtest.cpp b/examples/hashtest/hashtest.cpp new file mode 100644 index 0000000..917c396 --- /dev/null +++ b/examples/hashtest/hashtest.cpp @@ -0,0 +1,25 @@ +#include"qca.h" +#include<stdio.h> + +int main(int argc, char **argv) +{ + TQCA::init(); + TQCString cs = (argc >= 2) ? argv[1] : "hello"; + + if(!TQCA::isSupported(TQCA::CAP_SHA1)) + printf("SHA1 not supported!\n"); + else { + TQString result = TQCA::SHA1::hashToString(cs); + printf("sha1(\"%s\") = [%s]\n", cs.data(), result.latin1()); + } + + if(!TQCA::isSupported(TQCA::CAP_MD5)) + printf("MD5 not supported!\n"); + else { + TQString result = TQCA::MD5::hashToString(cs); + printf("md5(\"%s\") = [%s]\n", cs.data(), result.latin1()); + } + + return 0; +} + diff --git a/examples/hashtest/hashtest.pro b/examples/hashtest/hashtest.pro new file mode 100644 index 0000000..d642131 --- /dev/null +++ b/examples/hashtest/hashtest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += thread console +TARGET = hashtest + +SOURCES += hashtest.cpp +include(../examples.pri) diff --git a/examples/rsatest/rsatest.cpp b/examples/rsatest/rsatest.cpp new file mode 100644 index 0000000..bd513fd --- /dev/null +++ b/examples/rsatest/rsatest.cpp @@ -0,0 +1,86 @@ +#include<tqfile.h> +#include<tqfileinfo.h> +#include"qca.h" +#include<stdio.h> + +//#define USE_FILE + +TQCA::RSAKey readKeyFile(const TQString &name) +{ + TQCA::RSAKey k; + TQFile f(name); + if(!f.open(IO_ReadOnly)) { + printf("Unable to open %s\n", name.latin1()); + return k; + } + TQByteArray der = f.readAll(); + f.close(); + printf("Read %s [%d bytes]\n", name.latin1(), der.size()); + + if(!k.fromDER(der)) { + printf("%s: Error importing DER format.\n", name.latin1()); + return k; + } + char *yes = "yes"; + char *no = "no"; + printf("Successfully imported %s (enc=%s, dec=%s)\n", + name.latin1(), + k.havePublic() ? yes : no, + k.havePrivate() ? yes : no); + + printf("Converting to DER: %d bytes\n", k.toDER().size()); + printf("Converting to PEM:\n%s\n", k.toPEM().latin1()); + return k; +} + +int main(int argc, char **argv) +{ + TQCA::init(); + TQCString cs = (argc >= 2) ? argv[1] : "hello"; + + if(!TQCA::isSupported(TQCA::CAP_RSA)) + printf("RSA not supported!\n"); + else { +#ifdef USE_FILE + TQCA::RSAKey pubkey = readKeyFile("keypublic.der"); + if(pubkey.isNull()) + return 1; + TQCA::RSAKey seckey = readKeyFile("keyprivate.der"); + if(seckey.isNull()) + return 1; +#else + TQCA::RSAKey seckey = TQCA::RSA::generateKey(1024); + if(seckey.isNull()) + return 1; + TQCA::RSAKey pubkey = seckey; +#endif + // encrypt some data + TQByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + + TQCA::RSA op; + op.setKey(pubkey); + TQByteArray result; + if(!op.encrypt(a, &result)) { + printf("Error encrypting.\n"); + return 1; + } + TQString rstr = TQCA::arrayToHex(result); + printf(">rsa(\"%s\") = [%s]\n", cs.data(), rstr.latin1()); + + // now decrypt it + op.setKey(seckey); + TQByteArray dec; + if(!op.decrypt(result, &dec)) { + printf("Error decrypting.\n"); + return 1; + } + TQCString dstr; + dstr.resize(dec.size()+1); + memcpy(dstr.data(), dec.data(), dec.size()); + printf("<rsa(\"%s\") = [%s]\n", rstr.latin1(), dstr.data()); + } + + return 0; +} + diff --git a/examples/rsatest/rsatest.pro b/examples/rsatest/rsatest.pro new file mode 100644 index 0000000..fc293ad --- /dev/null +++ b/examples/rsatest/rsatest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += thread console +TARGET = rsatest + +SOURCES += rsatest.cpp +include(../examples.pri) diff --git a/examples/sasltest/sasltest.cpp b/examples/sasltest/sasltest.cpp new file mode 100644 index 0000000..dd4f858 --- /dev/null +++ b/examples/sasltest/sasltest.cpp @@ -0,0 +1,615 @@ +#include<tqapplication.h> +#include<tqtimer.h> +#include<tqsocket.h> +#include<tqserversocket.h> +#include<stdio.h> + +#ifdef Q_OS_UNIX +#include<unistd.h> +#endif + +#include"base64.h" +#include"qca.h" + +#define PROTO_NAME "foo" +#define PROTO_PORT 8001 + +static TQString prompt(const TQString &s) +{ + printf("* %s ", s.latin1()); + fflush(stdout); + char line[256]; + fgets(line, 255, stdin); + TQString result = line; + if(result[result.length()-1] == '\n') + result.truncate(result.length()-1); + return result; +} + +class ClientTest : public TQObject +{ + Q_OBJECT +public: + ClientTest() + { + sock = new TQSocket; + connect(sock, SIGNAL(connected()), SLOT(sock_connected())); + connect(sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(sock, SIGNAL(error(int)), SLOT(sock_error(int))); + + sasl = new QCA::SASL; + connect(sasl, SIGNAL(clientFirstStep(const TQString &, const TQByteArray *)), SLOT(sasl_clientFirstStep(const TQString &, const TQByteArray *))); + connect(sasl, SIGNAL(nextStep(const TQByteArray &)), SLOT(sasl_nextStep(const TQByteArray &))); + connect(sasl, SIGNAL(needParams(bool, bool, bool, bool)), SLOT(sasl_needParams(bool, bool, bool, bool))); + connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); + connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); + connect(sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int))); + connect(sasl, SIGNAL(error(int)), SLOT(sasl_error(int))); + } + + ~ClientTest() + { + delete sock; + delete sasl; + } + + void start(const TQString &_host, int port, const TQString &user="", const TQString &pass="") + { + mode = 0; + host = _host; + sock->connectToHost(host, port); + sasl->setMinimumSSF(0); + sasl->setMaximumSSF(256); + + if(!user.isEmpty()) { + sasl->setUsername(user); + sasl->setAuthzid(user); + } + if(!pass.isEmpty()) + sasl->setPassword(pass); + } + +signals: + void quit(); + +private slots: + void sock_connected() + { + printf("Connected to server. Awaiting mechanism list...\n"); + } + + void sock_connectionClosed() + { + printf("Connection closed by peer.\n"); + quit(); + } + + void sock_error(int x) + { + TQString s; + if(x == TQSocket::ErrConnectionRefused) + s = "connection refused or timed out"; + else if(x == TQSocket::ErrHostNotFound) + s = "host not found"; + else if(x == TQSocket::ErrSocketRead) + s = "read error"; + + printf("Socket error: %s\n", s.latin1()); + quit(); + } + + void sock_readyRead() + { + if(mode == 2) { + int avail = sock->bytesAvailable(); + TQByteArray a(avail); + int n = sock->readBlock(a.data(), a.size()); + a.resize(n); + printf("Read %d bytes\n", a.size()); + sasl->writeIncoming(a); + } + else { + if(sock->canReadLine()) { + TQString line = sock->readLine(); + line.truncate(line.length()-1); // chop the newline + handleLine(line); + } + } + } + + void sasl_clientFirstStep(const TQString &mech, const TQByteArray *clientInit) + { + printf("Choosing mech: %s\n", mech.latin1()); + TQString line = mech; + if(clientInit) { + TQCString cs(clientInit->data(), clientInit->size()+1); + line += ' '; + line += cs; + } + sendLine(line); + } + + void sasl_nextStep(const TQByteArray &stepData) + { + TQCString cs(stepData.data(), stepData.size()+1); + TQString line = "C"; + if(!stepData.isEmpty()) { + line += ','; + line += cs; + } + sendLine(line); + } + + void sasl_needParams(bool user, bool authzid, bool pass, bool realm) + { + TQString username; + if(user || authzid) + username = prompt("Username:"); + if(user) { + sasl->setUsername(username); + } + if(authzid) { + sasl->setAuthzid(username); + } + if(pass) { + sasl->setPassword(prompt("Password (not hidden!) :")); + } + if(realm) { + sasl->setRealm(prompt("Realm:")); + } + sasl->continueAfterParams(); + } + + void sasl_authenticated() + { + printf("SASL success!\n"); + printf("SSF: %d\n", sasl->ssf()); + } + + void sasl_readyRead() + { + TQByteArray a = sasl->read(); + int oldsize = inbuf.size(); + inbuf.resize(oldsize + a.size()); + memcpy(inbuf.data() + oldsize, a.data(), a.size()); + processInbuf(); + } + + void sasl_readyReadOutgoing(int) + { + TQByteArray a = sasl->readOutgoing(); + sock->writeBlock(a.data(), a.size()); + } + + void sasl_error(int) + { + printf("SASL error!\n"); + quit(); + return; + } + +private: + TQSocket *sock; + QCA::SASL *sasl; + int mode; + TQString host; + TQByteArray inbuf; + + void processInbuf() + { + TQStringList list; + for(int n = 0; n < (int)inbuf.size(); ++n) { + if(inbuf[n] == '\n') { + TQCString cs(inbuf.data(), n+1); + char *p = inbuf.data(); + ++n; + int x = inbuf.size() - n; + memmove(p, p + n, x); + inbuf.resize(x); + list += TQString::fromUtf8(cs); + // start over, basically + n = -1; + } + } + + for(TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + handleLine(*it); + } + + void handleLine(const TQString &line) + { + printf("Reading: [%s]\n", line.latin1()); + if(mode == 0) { + // first line is the method list + TQStringList mechlist = TQStringList::split(' ', line); + ++mode; + + // kick off the client + sasl->setAllowAnonymous(false); + if(!sasl->startClient(PROTO_NAME, host, mechlist)) { + printf("Error starting client!\n"); + quit(); + } + } + else if(mode == 1) { + TQString type, rest; + int n = line.find(','); + if(n != -1) { + type = line.mid(0, n); + rest = line.mid(n+1); + } + else { + type = line; + rest = ""; + } + + if(type == "C") { + TQCString cs = rest.latin1(); + TQByteArray buf(cs.length()); + memcpy(buf.data(), cs.data(), buf.size()); + sasl->putStep(buf); + } + else if(type == "E") { + printf("Authentication failed.\n"); + quit(); + return; + } + else if(type == "A") { + printf("Authentication success.\n"); + ++mode; + sock_readyRead(); // any extra data? + return; + } + else { + printf("Bad format from peer, closing.\n"); + quit(); + return; + } + } + else { + } + } + + void sendLine(const TQString &line) + { + printf("Writing: {%s}\n", line.latin1()); + TQString s = line + '\n'; + TQCString cs = s.latin1(); + if(mode == 2) { + TQByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + sasl->write(a); + } + else + sock->writeBlock(cs.data(), cs.length()); + } +}; + +class ServerTest : public QServerSocket +{ + Q_OBJECT +public: + ServerTest(const TQString &_str, int _port) : QServerSocket(_port), port(_port) + { + sock = 0; + sasl = 0; + realm = TQString::null; + str = _str; + } + + ~ServerTest() + { + delete sock; + delete sasl; + } + + void start() + { + if(!ok()) { + printf("Error binding to port %d!\n", port); + TTQTimer::singleShot(0, this, SIGNAL(quit())); + return; + } + char myhostname[256]; + int r = gethostname(myhostname, sizeof(myhostname)-1); + if(r == -1) { + printf("Error getting hostname!\n"); + TTQTimer::singleShot(0, this, SIGNAL(quit())); + return; + } + host = myhostname; + printf("Listening on %s:%d ...\n", host.latin1(), port); + } + + void newConnection(int s) + { + // Note: only 1 connection supported at a time in this example! + if(sock) { + TQSocket tmp; + tmp.setSocket(s); + printf("Connection ignored, already have one active.\n"); + return; + } + + printf("Connection received! Starting SASL handshake...\n"); + + sock = new TQSocket; + connect(sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(sock, SIGNAL(error(int)), SLOT(sock_error(int))); + connect(sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int))); + + sasl = new QCA::SASL; + connect(sasl, SIGNAL(authCheck(const TQString &, const TQString &)), SLOT(sasl_authCheck(const TQString &, const TQString &))); + connect(sasl, SIGNAL(nextStep(const TQByteArray &)), SLOT(sasl_nextStep(const TQByteArray &))); + connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); + connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); + connect(sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int))); + connect(sasl, SIGNAL(error(int)), SLOT(sasl_error(int))); + + sock->setSocket(s); + mode = 0; + inbuf.resize(0); + + sasl->setMinimumSSF(0); + sasl->setMaximumSSF(256); + + TQStringList mechlist; + if(!sasl->startServer(PROTO_NAME, host, realm, &mechlist)) { + printf("Error starting server!\n"); + quit(); + } + TQString str; + bool first = true; + for(TQStringList::ConstIterator it = mechlist.begin(); it != mechlist.end(); ++it) { + if(!first) + str += ' '; + str += *it; + first = false; + } + sendLine(str); + } + +signals: + void quit(); + +private slots: + void sock_connectionClosed() + { + printf("Connection closed by peer.\n"); + close(); + } + + void sock_error(int x) + { + TQString s; + if(x == TQSocket::ErrConnectionRefused) + s = "connection refused or timed out"; + else if(x == TQSocket::ErrHostNotFound) + s = "host not found"; + else if(x == TQSocket::ErrSocketRead) + s = "read error"; + + printf("Socket error: %s\n", s.latin1()); + close(); + } + + void sock_readyRead() + { + if(sock->canReadLine()) { + TQString line = sock->readLine(); + line.truncate(line.length()-1); // chop the newline + handleLine(line); + } + } + + void sock_bytesWritten(int x) + { + if(mode == 2) { + toWrite -= x; + if(toWrite <= 0) { + printf("Sent, closing.\n"); + close(); + } + } + } + + void sasl_nextStep(const TQByteArray &stepData) + { + TQCString cs(stepData.data(), stepData.size()+1); + TQString line = "C"; + if(!stepData.isEmpty()) { + line += ','; + line += cs; + } + sendLine(line); + } + + void sasl_authCheck(const TQString &user, const TQString &authzid) + { + printf("AuthCheck: User: [%s], Authzid: [%s]\n", user.latin1(), authzid.latin1()); + sasl->continueAfterAuthCheck(); + } + + void sasl_authenticated() + { + sendLine("A"); + printf("Authentication success.\n"); + ++mode; + printf("SSF: %d\n", sasl->ssf()); + sendLine(str); + } + + void sasl_readyRead() + { + TQByteArray a = sasl->read(); + int oldsize = inbuf.size(); + inbuf.resize(oldsize + a.size()); + memcpy(inbuf.data() + oldsize, a.data(), a.size()); + processInbuf(); + } + + void sasl_readyReadOutgoing(int) + { + TQByteArray a = sasl->readOutgoing(); + toWrite = a.size(); + sock->writeBlock(a.data(), a.size()); + } + + void sasl_error(int x) + { + if(x == QCA::SASL::ErrAuth) { + sendLine("E"); + printf("Authentication failed.\n"); + close(); + } + else { + printf("SASL security layer error!\n"); + close(); + } + } + +private: + TQSocket *sock; + QCA::SASL *sasl; + TQString host, realm; + int port; + int mode; + TQString str; + TQByteArray inbuf; + int toWrite; + + void processInbuf() + { + } + + void handleLine(const TQString &line) + { + printf("Reading: [%s]\n", line.latin1()); + if(mode == 0) { + int n = line.find(' '); + if(n != -1) { + TQString mech = line.mid(0, n); + TQCString cs = line.mid(n+1).latin1(); + TQByteArray clientInit(cs.length()); + memcpy(clientInit.data(), cs.data(), clientInit.size()); + sasl->putServerFirstStep(mech, clientInit); + } + else + sasl->putServerFirstStep(line); + ++mode; + } + else if(mode == 1) { + TQString type, rest; + int n = line.find(','); + if(n != -1) { + type = line.mid(0, n); + rest = line.mid(n+1); + } + else { + type = line; + rest = ""; + } + + if(type == "C") { + TQCString cs = rest.latin1(); + TQByteArray buf(cs.length()); + memcpy(buf.data(), cs.data(), buf.size()); + sasl->putStep(buf); + } + else { + printf("Bad format from peer, closing.\n"); + close(); + return; + } + } + } + + void sendLine(const TQString &line) + { + printf("Writing: {%s}\n", line.latin1()); + TQString s = line + '\n'; + TQCString cs = s.latin1(); + if(mode == 2) { + TQByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + sasl->write(a); + } + else + sock->writeBlock(cs.data(), cs.length()); + } + + void close() + { + sock->deleteLater(); + sock = 0; + delete sasl; + sasl = 0; + } +}; + +#include"sasltest.moc" + +void usage() +{ + printf("usage: sasltest client [host] [user] [pass]\n"); + printf(" sasltest server [string]\n\n"); +} + +int main(int argc, char **argv) +{ + TQApplication app(argc, argv, false); + + TQString host, user, pass; + TQString str = "Hello, World"; + bool server; + if(argc < 2) { + usage(); + return 0; + } + TQString arg = argv[1]; + if(arg == "client") { + if(argc < 3) { + usage(); + return 0; + } + host = argv[2]; + if(argc >= 4) + user = argv[3]; + if(argc >= 5) + pass = argv[4]; + server = false; + } + else if(arg == "server") { + if(argc >= 3) + str = argv[2]; + server = true; + } + else { + usage(); + return 0; + } + + if(!QCA::isSupported(QCA::CAP_SASL)) { + printf("SASL not supported!\n"); + return 1; + } + + if(server) { + ServerTest *s = new ServerTest(str, PROTO_PORT); + TQObject::connect(s, SIGNAL(quit()), &app, SLOT(quit())); + s->start(); + app.exec(); + delete s; + } + else { + ClientTest *c = new ClientTest; + TQObject::connect(c, SIGNAL(quit()), &app, SLOT(quit())); + c->start(host, PROTO_PORT, user, pass); + app.exec(); + delete c; + } + + return 0; +} diff --git a/examples/sasltest/sasltest.pro b/examples/sasltest/sasltest.pro new file mode 100644 index 0000000..f7d1c98 --- /dev/null +++ b/examples/sasltest/sasltest.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += thread console +TARGET = sasltest + +INCLUDEPATH += ../common +HEADERS += ../common/base64.h +SOURCES += ../common/base64.cpp sasltest.cpp +include(../examples.pri) diff --git a/examples/sslservtest/sslservtest.cpp b/examples/sslservtest/sslservtest.cpp new file mode 100644 index 0000000..384558f --- /dev/null +++ b/examples/sslservtest/sslservtest.cpp @@ -0,0 +1,294 @@ +#include<tqapplication.h> +#include<tqfile.h> +#include<tqsocket.h> +#include<tqserversocket.h> +#include<tqvaluelist.h> +#include<tqtimer.h> +#include"qca.h" + +char pemdata_cert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDbjCCAtegAwIBAgIBADANBgkqhkiG9w0BAQQFADCBhzELMAkGA1UEBhMCVVMx\n" + "EzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNVBAcTBklydmluZTEYMBYGA1UEChMP\n" + "RXhhbXBsZSBDb21wYW55MRQwEgYDVQQDEwtleGFtcGxlLmNvbTEiMCAGCSqGSIb3\n" + "DQEJARYTZXhhbXBsZUBleGFtcGxlLmNvbTAeFw0wMzA3MjQwNzMwMDBaFw0wMzA4\n" + "MjMwNzMwMDBaMIGHMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEP\n" + "MA0GA1UEBxMGSXJ2aW5lMRgwFgYDVQQKEw9FeGFtcGxlIENvbXBhbnkxFDASBgNV\n" + "BAMTC2V4YW1wbGUuY29tMSIwIAYJKoZIhvcNAQkBFhNleGFtcGxlQGV4YW1wbGUu\n" + "Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCobzCF268K2sRp473gvBTT\n" + "4AgSL1kjeF8N57vxS1P8zWrWMXNs4LuH0NRZmKTajeboy0br8xw+smIy3AbaKAwW\n" + "WZToesxebu3m9VeA8dqWyOaUMjoxAcgVYesgVaMpjRe7fcWdJnX1wJoVVPuIcO8m\n" + "a+AAPByfTORbzpSTmXAQAwIDAQABo4HnMIHkMB0GA1UdDgQWBBTvFierzLmmYMq0\n" + "cB/+5rK1bNR56zCBtAYDVR0jBIGsMIGpgBTvFierzLmmYMq0cB/+5rK1bNR566GB\n" + "jaSBijCBhzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNV\n" + "BAcTBklydmluZTEYMBYGA1UEChMPRXhhbXBsZSBDb21wYW55MRQwEgYDVQQDEwtl\n" + "eGFtcGxlLmNvbTEiMCAGCSqGSIb3DQEJARYTZXhhbXBsZUBleGFtcGxlLmNvbYIB\n" + "ADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBAGqGhXf7xNOnYNtFO7gz\n" + "K6RdZGHFI5q1DAEz4hhNBC9uElh32XGX4wN7giz3zLC8v9icL/W4ff/K5NDfv3Gf\n" + "gQe/+Wo9Be3H3ul6uwPPFnx4+PIOF2a5TW99H9smyxWdNjnFtcUte4al3RszcMWG\n" + "x3iqsWosGtj6F+ridmKoqKLu\n" + "-----END CERTIFICATE-----\n"; + +char pemdata_privkey[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXAIBAAKBgQCobzCF268K2sRp473gvBTT4AgSL1kjeF8N57vxS1P8zWrWMXNs\n" + "4LuH0NRZmKTajeboy0br8xw+smIy3AbaKAwWWZToesxebu3m9VeA8dqWyOaUMjox\n" + "AcgVYesgVaMpjRe7fcWdJnX1wJoVVPuIcO8ma+AAPByfTORbzpSTmXAQAwIDAQAB\n" + "AoGAP83u+aYghuIcaWhmM03MLf69z/WztKYSi/fu0BcS977w67bL3MC9CVPoPRB/\n" + "0nLSt/jZIuRzHKUCYfXLerSU7v0oXDTy6GPzWMh/oXIrpF0tYNbwWF7LSq2O2gGZ\n" + "XtA9MSmUNNJaKzQQeXjqdVFOY8A0Pho+k2KByBiCi+ChkcECQQDRUuyX0+PKJtA2\n" + "M36BOTFpy61BAv+JRlXUnHuevOfQWl6NR6YGygqCyH1sWtP1sa9S4wWys3DFH+5A\n" + "DkuAqk7zAkEAzf4eUH2hp5CIMsXH+WpIzKj09oY1it2CAKjVq4rUELf8iXvmGoFl\n" + "000spua4MjHNUYm7LR0QaKesKrMyGZUesQJAL8aLdYPJI+SD9Tr/jqLtIkZ4frQe\n" + "eshw4pvsoyheiHF3zyshO791crAr4EVCx3sMlxB1xnmqLXPCPyCEHxO//QJBAIBY\n" + "IYkjDZJ6ofGIe1UyXJNvfdkPu9J+ut4wU5jjEcgs6mK62J6RGuFxhy2iOQfFMdjo\n" + "yL+OCUg7mDCun7uCxrECQAtSvnLOFMjO5qExRjFtwi+b1rcSekd3Osk/izyRFSzg\n" + "Or+AL56/EKfiogNnFipgaXIbb/xj785Cob6v96XoW1I=\n" + "-----END RSA PRIVATE KEY-----\n"; + +class LayerTracker +{ +public: + struct Item + { + int plain; + int encoded; + }; + + LayerTracker() + { + p = 0; + } + + void reset() + { + p = 0; + list.clear(); + } + + void addPlain(int plain) + { + p += plain; + } + + void specifyEncoded(int encoded, int plain) + { + // can't specify more bytes than we have + if(plain > p) + plain = p; + p -= plain; + Item i; + i.plain = plain; + i.encoded = encoded; + list += i; + } + + int finished(int encoded) + { + int plain = 0; + for(TQValueList<Item>::Iterator it = list.begin(); it != list.end();) { + Item &i = *it; + + // not enough? + if(encoded < i.encoded) { + i.encoded -= encoded; + break; + } + + encoded -= i.encoded; + plain += i.plain; + it = list.remove(it); + } + return plain; + } + + int p; + TQValueList<Item> list; +}; + +class SecureServerTest : public QServerSocket +{ + Q_OBJECT +public: + enum { Idle, Handshaking, Active, Closing }; + + SecureServerTest(int _port) : QServerSocket(_port), port(_port) + { + sock = new TQSocket; + connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(sock, SIGNAL(error(int)), SLOT(sock_error(int))); + connect(sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int))); + + ssl = new QCA::TLS; + connect(ssl, SIGNAL(handshaken()), SLOT(ssl_handshaken())); + connect(ssl, SIGNAL(readyRead()), SLOT(ssl_readyRead())); + connect(ssl, SIGNAL(readyReadOutgoing(int)), SLOT(ssl_readyReadOutgoing(int))); + connect(ssl, SIGNAL(closed()), SLOT(ssl_closed())); + connect(ssl, SIGNAL(error(int)), SLOT(ssl_error(int))); + + cert.fromPEM(pemdata_cert); + privkey.fromPEM(pemdata_privkey); + + mode = Idle; + } + + ~SecureServerTest() + { + delete ssl; + delete sock; + } + + void start() + { + if(cert.isNull() || privkey.isNull()) { + printf("Error loading cert and/or private key!\n"); + TTQTimer::singleShot(0, this, SIGNAL(quit())); + return; + } + if(!ok()) { + printf("Error binding to port %d!\n", port); + TTQTimer::singleShot(0, this, SIGNAL(quit())); + return; + } + printf("Listening on port %d ...\n", port); + } + + void newConnection(int s) + { + // Note: only 1 connection supported at a time in this example! + if(sock->isOpen()) { + TQSocket tmp; + tmp.setSocket(s); + printf("throwing away extra connection\n"); + return; + } + mode = Handshaking; + sock->setSocket(s); + printf("Connection received! Starting TLS handshake...\n"); + ssl->setCertificate(cert, privkey); + ssl->startServer(); + } + +signals: + void quit(); + +private slots: + 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("Connection closed.\n"); + } + + void sock_bytesWritten(int x) + { + if(mode == Active && sent) { + int bytes = layer.finished(x); + bytesLeft -= bytes; + + if(bytesLeft == 0) { + mode = Closing; + printf("SSL shutdown\n"); + ssl->close(); + } + } + } + + void sock_error(int) + { + printf("Socket error.\n"); + } + + void ssl_handshaken() + { + printf("Successful SSL handshake. Waiting for newline.\n"); + layer.reset(); + bytesLeft = 0; + sent = false; + mode = Active; + } + + void ssl_readyRead() + { + TQByteArray a = ssl->read(); + TQString str = + "<html>\n" + "<head><title>Test</title></head>\n" + "<body>this is only a test</body>\n" + "</html>\n"; + TQCString cs = str.latin1(); + TQByteArray b(cs.length()); + memcpy(b.data(), cs.data(), b.size()); + + printf("Sending test response...\n"); + sent = true; + layer.addPlain(b.size()); + ssl->write(b); + } + + void ssl_readyReadOutgoing(int plainBytes) + { + TQByteArray a = ssl->readOutgoing(); + layer.specifyEncoded(a.size(), plainBytes); + sock->writeBlock(a.data(), a.size()); + } + + void ssl_closed() + { + printf("Closing.\n"); + sock->close(); + } + + void ssl_error(int x) + { + if(x == QCA::TLS::ErrHandshake) { + printf("SSL Handshake Error! Closing.\n"); + sock->close(); + } + else { + printf("SSL Error! Closing.\n"); + sock->close(); + } + } + +private: + int port; + TQSocket *sock; + QCA::TLS *ssl; + QCA::Cert cert; + QCA::RSAKey privkey; + + bool sent; + int mode; + int bytesLeft; + LayerTracker layer; +}; + +#include"sslservtest.moc" + +int main(int argc, char **argv) +{ + TQApplication app(argc, argv, false); + int port = argc > 1 ? TQString(argv[1]).toInt() : 8000; + + if(!QCA::isSupported(QCA::CAP_TLS)) { + printf("TLS not supported!\n"); + return 1; + } + + SecureServerTest *s = new SecureServerTest(port); + TQObject::connect(s, SIGNAL(quit()), &app, SLOT(quit())); + s->start(); + app.exec(); + delete s; + + return 0; +} diff --git a/examples/sslservtest/sslservtest.pro b/examples/sslservtest/sslservtest.pro new file mode 100644 index 0000000..028d5ab --- /dev/null +++ b/examples/sslservtest/sslservtest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += thread console +TARGET = sslservtest + +SOURCES += sslservtest.cpp +include(../examples.pri) diff --git a/examples/ssltest/ssltest.cpp b/examples/ssltest/ssltest.cpp new file mode 100644 index 0000000..f26f293 --- /dev/null +++ b/examples/ssltest/ssltest.cpp @@ -0,0 +1,276 @@ +#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 +{ + Q_OBJECT +public: + SecureTest() + { + sock = new TQSocket; + connect(sock, SIGNAL(connected()), SLOT(sock_connected())); + connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(sock, SIGNAL(error(int)), SLOT(sock_error(int))); + + ssl = new TQCA::TLS; + connect(ssl, SIGNAL(handshaken()), SLOT(ssl_handshaken())); + connect(ssl, SIGNAL(readyRead()), SLOT(ssl_readyRead())); + connect(ssl, SIGNAL(readyReadOutgoing(int)), SLOT(ssl_readyReadOutgoing(int))); + connect(ssl, SIGNAL(closed()), SLOT(ssl_closed())); + connect(ssl, SIGNAL(error(int)), 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, SIGNAL(quit()), &app, SLOT(quit())); + s->start(host); + app.exec(); + delete s; + + return 0; +} diff --git a/examples/ssltest/ssltest.pro b/examples/ssltest/ssltest.pro new file mode 100644 index 0000000..44ae325 --- /dev/null +++ b/examples/ssltest/ssltest.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += thread console +TARGET = ssltest + +INCLUDEPATH += ../common +HEADERS += ../common/base64.h +SOURCES += ../common/base64.cpp ssltest.cpp +include(../examples.pri) |