1/*
2 Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com>
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20*/
21
22#include <QtCrypto>
23
24#include <QCoreApplication>
25#include <QTcpSocket>
26
27#ifdef QT_STATICPLUGIN
28#include "import_plugins.h"
29#endif
30
31char exampleCA_cert[] =
32 "-----BEGIN CERTIFICATE-----\n"
33 "MIICSzCCAbSgAwIBAgIBADANBgkqhkiG9w0BAQUFADA4MRMwEQYDVQQDEwpFeGFt\n"
34 "cGxlIENBMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwHhcNMDYw\n"
35 "MzE1MDY1ODMyWhcNMDYwNDE1MDY1ODMyWjA4MRMwEQYDVQQDEwpFeGFtcGxlIENB\n"
36 "MQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwgZ8wDQYJKoZIhvcN\n"
37 "AQEBBQADgY0AMIGJAoGBAL6ULdOxmpeZ+G/ypV12eNO4qnHSVIPTrYPkQuweXqPy\n"
38 "atwGFheG+hLVsNIh9GGOS0tCe7a3hBBKN0BJg1ppfk2x39cDx7hefYqjBuZvp/0O\n"
39 "8Ja3qlQiJLezITZKLxMBrsibcvcuH8zpfUdys2yaN+YGeqNfjQuoNN3Byl1TwuGJ\n"
40 "AgMBAAGjZTBjMB0GA1UdDgQWBBSQKCUCLNM7uKrAt5o7qv/yQm6qEzASBgNVHRMB\n"
41 "Af8ECDAGAQEBAgEIMB4GA1UdEQQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wDgYD\n"
42 "VR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4GBAAh+SIeT1Ao5qInw8oMSoTdO\n"
43 "lQ6h67ec/Jk5KmK4OoskuimmHI0Sp0C5kOCLehXbsVWW8pXsNC2fv0d2HkdaSUcX\n"
44 "hwLzqgyZXd4mupIYlaOTZhuHDwWPCAOZS4LVsi2tndTRHKCP12441JjNKhmZRhkR\n"
45 "u5zzD60nWgM9dKTaxuZM\n"
46 "-----END CERTIFICATE-----\n";
47
48void showCertInfo(const QCA::Certificate &cert)
49{
50 printf(format: "-- Cert --\n");
51 printf(format: " CN: %s\n", qPrintable(cert.commonName()));
52 printf(format: " Valid from: %s, until %s\n",
53 qPrintable(cert.notValidBefore().toString()),
54 qPrintable(cert.notValidAfter().toString()));
55 printf(format: " PEM:\n%s\n", qPrintable(cert.toPEM()));
56}
57
58static QString validityToString(QCA::Validity v)
59{
60 QString s;
61 switch (v) {
62 case QCA::ValidityGood:
63 s = QStringLiteral("Validated");
64 break;
65 case QCA::ErrorRejected:
66 s = QStringLiteral("Root CA is marked to reject the specified purpose");
67 break;
68 case QCA::ErrorUntrusted:
69 s = QStringLiteral("Certificate not trusted for the required purpose");
70 break;
71 case QCA::ErrorSignatureFailed:
72 s = QStringLiteral("Invalid signature");
73 break;
74 case QCA::ErrorInvalidCA:
75 s = QStringLiteral("Invalid CA certificate");
76 break;
77 case QCA::ErrorInvalidPurpose:
78 s = QStringLiteral("Invalid certificate purpose");
79 break;
80 case QCA::ErrorSelfSigned:
81 s = QStringLiteral("Certificate is self-signed");
82 break;
83 case QCA::ErrorRevoked:
84 s = QStringLiteral("Certificate has been revoked");
85 break;
86 case QCA::ErrorPathLengthExceeded:
87 s = QStringLiteral("Maximum certificate chain length exceeded");
88 break;
89 case QCA::ErrorExpired:
90 s = QStringLiteral("Certificate has expired");
91 break;
92 case QCA::ErrorExpiredCA:
93 s = QStringLiteral("CA has expired");
94 break;
95 case QCA::ErrorValidityUnknown:
96 default:
97 s = QStringLiteral("General certificate validation error");
98 break;
99 }
100 return s;
101}
102
103class SecureTest : public QObject
104{
105 Q_OBJECT
106public:
107 SecureTest()
108 {
109 sock_done = false;
110 ssl_done = false;
111
112 sock = new QTcpSocket;
113 connect(sender: sock, signal: &QTcpSocket::connected, context: this, slot: &SecureTest::sock_connected);
114 connect(sender: sock, signal: &QTcpSocket::readyRead, context: this, slot: &SecureTest::sock_readyRead);
115#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
116 connect(sender: sock, signal: &QTcpSocket::errorOccurred, context: this, slot: &SecureTest::sock_error);
117#else
118 connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &SecureTest::sock_error);
119#endif
120
121 ssl = new QCA::TLS;
122 connect(sender: ssl, signal: &QCA::TLS::certificateRequested, context: this, slot: &SecureTest::ssl_certificateRequested);
123 connect(sender: ssl, signal: &QCA::TLS::handshaken, context: this, slot: &SecureTest::ssl_handshaken);
124 connect(sender: ssl, signal: &QCA::TLS::readyRead, context: this, slot: &SecureTest::ssl_readyRead);
125 connect(sender: ssl, signal: &QCA::TLS::readyReadOutgoing, context: this, slot: &SecureTest::ssl_readyReadOutgoing);
126 connect(sender: ssl, signal: &QCA::TLS::closed, context: this, slot: &SecureTest::ssl_closed);
127 connect(sender: ssl, signal: &QCA::TLS::error, context: this, slot: &SecureTest::ssl_error);
128 }
129
130 ~SecureTest() override
131 {
132 delete ssl;
133 delete sock;
134 }
135
136 void start(const QString &_host)
137 {
138 int n = _host.indexOf(c: QLatin1Char(':'));
139 int port;
140 if (n != -1) {
141 host = _host.mid(position: 0, n);
142#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
143 port = QStringView(_host).mid(pos: n + 1).toInt();
144#else
145 port = _host.midRef(n + 1).toInt();
146#endif
147 } else {
148 host = _host;
149 port = 443;
150 }
151
152 printf(format: "Trying %s:%d...\n", qPrintable(host), port);
153 sock->connectToHost(hostName: host, port);
154 }
155
156Q_SIGNALS:
157 void quit();
158
159private Q_SLOTS:
160 void sock_connected()
161 {
162 // We just do this to help doxygen...
163 QCA::TLS *ssl = SecureTest::ssl;
164
165 printf(format: "Connected, starting TLS handshake...\n");
166
167 QCA::CertificateCollection rootCerts = QCA::systemStore();
168
169 // We add this one to show how, and to make it work with
170 // the server example.
171 rootCerts.addCertificate(cert: QCA::Certificate::fromPEM(s: QString::fromLatin1(ba: exampleCA_cert)));
172
173 if (!QCA::haveSystemStore())
174 printf(format: "Warning: no root certs\n");
175 else
176 ssl->setTrustedCertificates(rootCerts);
177
178 ssl->startClient(host);
179 }
180
181 void sock_readyRead()
182 {
183 // We just do this to help doxygen...
184 QCA::TLS *ssl = SecureTest::ssl;
185
186 ssl->writeIncoming(a: sock->readAll());
187 }
188
189 void sock_connectionClosed()
190 {
191 printf(format: "\nConnection closed.\n");
192 sock_done = true;
193
194 if (ssl_done && sock_done)
195 emit quit();
196 }
197
198 void sock_error(QAbstractSocket::SocketError x)
199 {
200 if (x == QAbstractSocket::RemoteHostClosedError) {
201 sock_connectionClosed();
202 return;
203 }
204
205 printf(format: "\nSocket error.\n");
206 emit quit();
207 }
208
209 void ssl_handshaken()
210 {
211 // We just do this to help doxygen...
212 QCA::TLS *ssl = SecureTest::ssl;
213
214 QCA::TLS::IdentityResult r = ssl->peerIdentityResult();
215
216 printf(format: "Successful SSL handshake using %s (%i of %i bits)\n",
217 qPrintable(ssl->cipherSuite()),
218 ssl->cipherBits(),
219 ssl->cipherMaxBits());
220 if (r != QCA::TLS::NoCertificate) {
221 cert = ssl->peerCertificateChain().primary();
222 if (!cert.isNull())
223 showCertInfo(cert);
224 }
225
226 QString str = QStringLiteral("Peer Identity: ");
227 if (r == QCA::TLS::Valid)
228 str += QStringLiteral("Valid");
229 else if (r == QCA::TLS::HostMismatch)
230 str += QStringLiteral("Error: Wrong certificate");
231 else if (r == QCA::TLS::InvalidCertificate)
232 str += QStringLiteral("Error: Invalid certificate.\n -> Reason: ") +
233 validityToString(v: ssl->peerCertificateValidity());
234 else
235 str += QStringLiteral("Error: No certificate");
236 printf(format: "%s\n", qPrintable(str));
237
238 ssl->continueAfterStep();
239
240 printf(format: "Let's try a GET request now.\n");
241 QString req = QStringLiteral("GET / HTTP/1.0\nHost: ") + host + QStringLiteral("\n\n");
242 ssl->write(a: req.toLatin1());
243 }
244
245 void ssl_certificateRequested()
246 {
247 // We just do this to help doxygen...
248 QCA::TLS *ssl = SecureTest::ssl;
249
250 printf(format: "Server requested client certificate.\n");
251 QList<QCA::CertificateInfoOrdered> issuerList = ssl->issuerList();
252 if (!issuerList.isEmpty()) {
253 printf(format: "Allowed issuers:\n");
254 foreach (QCA::CertificateInfoOrdered i, issuerList)
255 printf(format: " %s\n", qPrintable(i.toString()));
256 }
257
258 ssl->continueAfterStep();
259 }
260
261 void ssl_readyRead()
262 {
263 // We just do this to help doxygen...
264 QCA::TLS *ssl = SecureTest::ssl;
265
266 QByteArray a = ssl->read();
267 printf(format: "%s", a.data());
268 }
269
270 void ssl_readyReadOutgoing()
271 {
272 // We just do this to help doxygen...
273 QCA::TLS *ssl = SecureTest::ssl;
274
275 sock->write(data: ssl->readOutgoing());
276 }
277
278 void ssl_closed()
279 {
280 printf(format: "SSL session closed.\n");
281 ssl_done = true;
282
283 if (ssl_done && sock_done)
284 emit quit();
285 }
286
287 void ssl_error()
288 {
289 // We just do this to help doxygen...
290 QCA::TLS *ssl = SecureTest::ssl;
291
292 int x = ssl->errorCode();
293 if (x == QCA::TLS::ErrorHandshake) {
294 printf(format: "SSL Handshake Error!\n");
295 emit quit();
296 } else {
297 printf(format: "SSL Error!\n");
298 emit quit();
299 }
300 }
301
302private:
303 QString host;
304 QTcpSocket *sock;
305 QCA::TLS *ssl;
306 QCA::Certificate cert;
307 bool sock_done, ssl_done;
308};
309
310#include "ssltest.moc"
311
312int main(int argc, char **argv)
313{
314 QCA::Initializer init;
315
316 QCoreApplication app(argc, argv);
317 QString host = argc > 1 ? QString::fromLocal8Bit(ba: argv[1]) : QStringLiteral("andbit.net");
318
319 if (!QCA::isSupported(features: "tls")) {
320 printf(format: "TLS not supported!\n");
321 return 1;
322 }
323
324 SecureTest *s = new SecureTest;
325 QObject::connect(sender: s, signal: &SecureTest::quit, context: &app, slot: &QCoreApplication::quit);
326 s->start(host: host);
327 app.exec();
328 delete s;
329
330 return 0;
331}
332

source code of qca/examples/ssltest/ssltest.cpp