| 1 | #include<qapplication.h>
|
|---|
| 2 | #include<qdom.h>
|
|---|
| 3 | #include<qfile.h>
|
|---|
| 4 | #include<qsocket.h>
|
|---|
| 5 | #include<qptrlist.h>
|
|---|
| 6 | #include"base64.h"
|
|---|
| 7 | #include"qca.h"
|
|---|
| 8 |
|
|---|
| 9 | QCA::Cert readCertXml(const QDomElement &e)
|
|---|
| 10 | {
|
|---|
| 11 | QCA::Cert cert;
|
|---|
| 12 | // there should be one child data tag
|
|---|
| 13 | QDomElement data = e.elementsByTagName("data").item(0).toElement();
|
|---|
| 14 | if(!data.isNull())
|
|---|
| 15 | cert.fromDER(Base64::stringToArray(data.text()));
|
|---|
| 16 | return cert;
|
|---|
| 17 | }
|
|---|
| 18 |
|
|---|
| 19 | void showCertInfo(const QCA::Cert &cert)
|
|---|
| 20 | {
|
|---|
| 21 | printf("-- Cert --\n");
|
|---|
| 22 | printf(" CN: %s\n", cert.subject()["CN"].latin1());
|
|---|
| 23 | printf(" Valid from: %s, until %s\n",
|
|---|
| 24 | cert.notBefore().toString().latin1(),
|
|---|
| 25 | cert.notAfter().toString().latin1());
|
|---|
| 26 | printf(" PEM:\n%s\n", cert.toPEM().latin1());
|
|---|
| 27 | }
|
|---|
| 28 |
|
|---|
| 29 | QPtrList<QCA::Cert> getRootCerts(const QString &store)
|
|---|
| 30 | {
|
|---|
| 31 | QPtrList<QCA::Cert> list;
|
|---|
| 32 |
|
|---|
| 33 | // open the Psi rootcerts file
|
|---|
| 34 | QFile f(store);
|
|---|
| 35 | if(!f.open(IO_ReadOnly)) {
|
|---|
| 36 | printf("unable to open %s\n", f.name().latin1());
|
|---|
| 37 | return list;
|
|---|
| 38 | }
|
|---|
| 39 | QDomDocument doc;
|
|---|
| 40 | doc.setContent(&f);
|
|---|
| 41 | f.close();
|
|---|
| 42 |
|
|---|
| 43 | QDomElement base = doc.documentElement();
|
|---|
| 44 | if(base.tagName() != "store") {
|
|---|
| 45 | printf("wrong format of %s\n", f.name().latin1());
|
|---|
| 46 | return list;
|
|---|
| 47 | }
|
|---|
| 48 | QDomNodeList cl = base.elementsByTagName("certificate");
|
|---|
| 49 | if(cl.count() == 0) {
|
|---|
| 50 | printf("no certs found in %s\n", f.name().latin1());
|
|---|
| 51 | return list;
|
|---|
| 52 | }
|
|---|
| 53 |
|
|---|
| 54 | int num = 0;
|
|---|
| 55 | for(int n = 0; n < (int)cl.count(); ++n) {
|
|---|
| 56 | QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
|
|---|
| 57 | if(cert->isNull()) {
|
|---|
| 58 | printf("error reading cert\n");
|
|---|
| 59 | delete cert;
|
|---|
| 60 | continue;
|
|---|
| 61 | }
|
|---|
| 62 |
|
|---|
| 63 | ++num;
|
|---|
| 64 | list.append(cert);
|
|---|
| 65 | }
|
|---|
| 66 | printf("imported %d root certs\n", num);
|
|---|
| 67 |
|
|---|
| 68 | return list;
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | QString resultToString(int result)
|
|---|
| 72 | {
|
|---|
| 73 | QString s;
|
|---|
| 74 | switch(result) {
|
|---|
| 75 | case QCA::TLS::NoCert:
|
|---|
| 76 | s = QObject::tr("No certificate presented.");
|
|---|
| 77 | break;
|
|---|
| 78 | case QCA::TLS::Valid:
|
|---|
| 79 | break;
|
|---|
| 80 | case QCA::TLS::HostMismatch:
|
|---|
| 81 | s = QObject::tr("Hostname mismatch.");
|
|---|
| 82 | break;
|
|---|
| 83 | case QCA::TLS::Rejected:
|
|---|
| 84 | s = QObject::tr("Root CA rejects the specified purpose.");
|
|---|
| 85 | break;
|
|---|
| 86 | case QCA::TLS::Untrusted:
|
|---|
| 87 | s = QObject::tr("Not trusted for the specified purpose.");
|
|---|
| 88 | break;
|
|---|
| 89 | case QCA::TLS::SignatureFailed:
|
|---|
| 90 | s = QObject::tr("Invalid signature.");
|
|---|
| 91 | break;
|
|---|
| 92 | case QCA::TLS::InvalidCA:
|
|---|
| 93 | s = QObject::tr("Invalid CA certificate.");
|
|---|
| 94 | break;
|
|---|
| 95 | case QCA::TLS::InvalidPurpose:
|
|---|
| 96 | s = QObject::tr("Invalid certificate purpose.");
|
|---|
| 97 | break;
|
|---|
| 98 | case QCA::TLS::SelfSigned:
|
|---|
| 99 | s = QObject::tr("Certificate is self-signed.");
|
|---|
| 100 | break;
|
|---|
| 101 | case QCA::TLS::Revoked:
|
|---|
| 102 | s = QObject::tr("Certificate has been revoked.");
|
|---|
| 103 | break;
|
|---|
| 104 | case QCA::TLS::PathLengthExceeded:
|
|---|
| 105 | s = QObject::tr("Maximum cert chain length exceeded.");
|
|---|
| 106 | break;
|
|---|
| 107 | case QCA::TLS::Expired:
|
|---|
| 108 | s = QObject::tr("Certificate has expired.");
|
|---|
| 109 | break;
|
|---|
| 110 | case QCA::TLS::Unknown:
|
|---|
| 111 | default:
|
|---|
| 112 | s = QObject::tr("General validation error.");
|
|---|
| 113 | break;
|
|---|
| 114 | }
|
|---|
| 115 | return s;
|
|---|
| 116 | }
|
|---|
| 117 |
|
|---|
| 118 | class SecureTest : public QObject
|
|---|
| 119 | {
|
|---|
| 120 | Q_OBJECT
|
|---|
| 121 | public:
|
|---|
| 122 | SecureTest()
|
|---|
| 123 | {
|
|---|
| 124 | sock = new QSocket;
|
|---|
| 125 | connect(sock, SIGNAL(connected()), SLOT(sock_connected()));
|
|---|
| 126 | connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
|
|---|
| 127 | connect(sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
|
|---|
| 128 | connect(sock, SIGNAL(error(int)), SLOT(sock_error(int)));
|
|---|
| 129 |
|
|---|
| 130 | ssl = new QCA::TLS;
|
|---|
| 131 | connect(ssl, SIGNAL(handshaken()), SLOT(ssl_handshaken()));
|
|---|
| 132 | connect(ssl, SIGNAL(readyRead()), SLOT(ssl_readyRead()));
|
|---|
| 133 | connect(ssl, SIGNAL(readyReadOutgoing(int)), SLOT(ssl_readyReadOutgoing(int)));
|
|---|
| 134 | connect(ssl, SIGNAL(closed()), SLOT(ssl_closed()));
|
|---|
| 135 | connect(ssl, SIGNAL(error(int)), SLOT(ssl_error(int)));
|
|---|
| 136 |
|
|---|
| 137 | rootCerts.setAutoDelete(true);
|
|---|
| 138 | rootCerts = getRootCerts("/usr/local/share/psi/certs/rootcert.xml");
|
|---|
| 139 | }
|
|---|
| 140 |
|
|---|
| 141 | ~SecureTest()
|
|---|
| 142 | {
|
|---|
| 143 | delete ssl;
|
|---|
| 144 | delete sock;
|
|---|
| 145 | }
|
|---|
| 146 |
|
|---|
| 147 | void start(const QString &_host)
|
|---|
| 148 | {
|
|---|
| 149 | int n = _host.find(':');
|
|---|
| 150 | int port;
|
|---|
| 151 | if(n != -1) {
|
|---|
| 152 | host = _host.mid(0, n);
|
|---|
| 153 | port = _host.mid(n+1).toInt();
|
|---|
| 154 | }
|
|---|
| 155 | else {
|
|---|
| 156 | host = _host;
|
|---|
| 157 | port = 443;
|
|---|
| 158 | }
|
|---|
| 159 |
|
|---|
| 160 | printf("Trying %s:%d...\n", host.latin1(), port);
|
|---|
| 161 | sock->connectToHost(host, port);
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | signals:
|
|---|
| 165 | void quit();
|
|---|
| 166 |
|
|---|
| 167 | private slots:
|
|---|
| 168 | void sock_connected()
|
|---|
| 169 | {
|
|---|
| 170 | printf("Connected, starting TLS handshake...\n");
|
|---|
| 171 | ssl->setCertificateStore(rootCerts);
|
|---|
| 172 | ssl->startClient(host);
|
|---|
| 173 | }
|
|---|
| 174 |
|
|---|
| 175 | void sock_readyRead()
|
|---|
| 176 | {
|
|---|
| 177 | QByteArray buf(sock->bytesAvailable());
|
|---|
| 178 | int num = sock->readBlock(buf.data(), buf.size());
|
|---|
| 179 | if(num < (int)buf.size())
|
|---|
| 180 | buf.resize(num);
|
|---|
| 181 | ssl->writeIncoming(buf);
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| 184 | void sock_connectionClosed()
|
|---|
| 185 | {
|
|---|
| 186 | printf("\nConnection closed.\n");
|
|---|
| 187 | quit();
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | void sock_error(int)
|
|---|
| 191 | {
|
|---|
| 192 | printf("\nSocket error.\n");
|
|---|
| 193 | quit();
|
|---|
| 194 | }
|
|---|
| 195 |
|
|---|
| 196 | void ssl_handshaken()
|
|---|
| 197 | {
|
|---|
| 198 | cert = ssl->peerCertificate();
|
|---|
| 199 | int vr = ssl->certificateValidityResult();
|
|---|
| 200 |
|
|---|
| 201 | printf("Successful SSL handshake.\n");
|
|---|
| 202 | if(!cert.isNull())
|
|---|
| 203 | showCertInfo(cert);
|
|---|
| 204 | if(vr == QCA::TLS::Valid)
|
|---|
| 205 | printf("Valid certificate.\n");
|
|---|
| 206 | else
|
|---|
| 207 | printf("Invalid certificate: %s\n", resultToString(vr).latin1());
|
|---|
| 208 |
|
|---|
| 209 | printf("Let's try a GET request now.\n");
|
|---|
| 210 | QString req = "GET / HTTP/1.0\nHost: " + host + "\n\n";
|
|---|
| 211 | QCString cs = req.latin1();
|
|---|
| 212 | QByteArray buf(cs.length());
|
|---|
| 213 | memcpy(buf.data(), cs.data(), buf.size());
|
|---|
| 214 | ssl->write(buf);
|
|---|
| 215 | }
|
|---|
| 216 |
|
|---|
| 217 | void ssl_readyRead()
|
|---|
| 218 | {
|
|---|
| 219 | QByteArray a = ssl->read();
|
|---|
| 220 | QCString cs;
|
|---|
| 221 | cs.resize(a.size()+1);
|
|---|
| 222 | memcpy(cs.data(), a.data(), a.size());
|
|---|
| 223 | printf("%s", cs.data());
|
|---|
| 224 | }
|
|---|
| 225 |
|
|---|
| 226 | void ssl_readyReadOutgoing(int)
|
|---|
| 227 | {
|
|---|
| 228 | QByteArray a = ssl->readOutgoing();
|
|---|
| 229 | sock->writeBlock(a.data(), a.size());
|
|---|
| 230 | }
|
|---|
| 231 |
|
|---|
| 232 | void ssl_closed()
|
|---|
| 233 | {
|
|---|
| 234 | printf("SSL session closed\n");
|
|---|
| 235 | }
|
|---|
| 236 |
|
|---|
| 237 | void ssl_error(int x)
|
|---|
| 238 | {
|
|---|
| 239 | if(x == QCA::TLS::ErrHandshake) {
|
|---|
| 240 | printf("SSL Handshake Error!\n");
|
|---|
| 241 | quit();
|
|---|
| 242 | }
|
|---|
| 243 | else {
|
|---|
| 244 | printf("SSL Error!\n");
|
|---|
| 245 | quit();
|
|---|
| 246 | }
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | private:
|
|---|
| 250 | QString host;
|
|---|
| 251 | QSocket *sock;
|
|---|
| 252 | QCA::TLS *ssl;
|
|---|
| 253 | QCA::Cert cert;
|
|---|
| 254 | QPtrList<QCA::Cert> rootCerts;
|
|---|
| 255 | };
|
|---|
| 256 |
|
|---|
| 257 | #include"ssltest.moc"
|
|---|
| 258 |
|
|---|
| 259 | int main(int argc, char **argv)
|
|---|
| 260 | {
|
|---|
| 261 | QApplication app(argc, argv, false);
|
|---|
| 262 | QString host = argc > 1 ? argv[1] : "andbit.net";
|
|---|
| 263 |
|
|---|
| 264 | if(!QCA::isSupported(QCA::CAP_TLS)) {
|
|---|
| 265 | printf("TLS not supported!\n");
|
|---|
| 266 | return 1;
|
|---|
| 267 | }
|
|---|
| 268 |
|
|---|
| 269 | SecureTest *s = new SecureTest;
|
|---|
| 270 | QObject::connect(s, SIGNAL(quit()), &app, SLOT(quit()));
|
|---|
| 271 | s->start(host);
|
|---|
| 272 | app.exec();
|
|---|
| 273 | delete s;
|
|---|
| 274 |
|
|---|
| 275 | return 0;
|
|---|
| 276 | }
|
|---|