QCA

ssltest.cpp
1 /*
2  Copyright (C) 2003-2005 Justin Karneges <[email protected]>
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 
31 char 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 
48 void showCertInfo(const QCA::Certificate &cert)
49 {
50  printf("-- Cert --\n");
51  printf(" CN: %s\n", qPrintable(cert.commonName()));
52  printf(" Valid from: %s, until %s\n",
53  qPrintable(cert.notValidBefore().toString()),
54  qPrintable(cert.notValidAfter().toString()));
55  printf(" PEM:\n%s\n", qPrintable(cert.toPEM()));
56 }
57 
58 static 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;
69  s = QStringLiteral("Certificate not trusted for the required purpose");
70  break;
72  s = QStringLiteral("Invalid signature");
73  break;
75  s = QStringLiteral("Invalid CA certificate");
76  break;
78  s = QStringLiteral("Invalid certificate purpose");
79  break;
81  s = QStringLiteral("Certificate is self-signed");
82  break;
83  case QCA::ErrorRevoked:
84  s = QStringLiteral("Certificate has been revoked");
85  break;
87  s = QStringLiteral("Maximum certificate chain length exceeded");
88  break;
89  case QCA::ErrorExpired:
90  s = QStringLiteral("Certificate has expired");
91  break;
93  s = QStringLiteral("CA has expired");
94  break;
96  default:
97  s = QStringLiteral("General certificate validation error");
98  break;
99  }
100  return s;
101 }
102 
103 class SecureTest : public QObject
104 {
105  Q_OBJECT
106 public:
107  SecureTest()
108  {
109  sock_done = false;
110  ssl_done = false;
111 
112  sock = new QTcpSocket;
113  connect(sock, &QTcpSocket::connected, this, &SecureTest::sock_connected);
114  connect(sock, &QTcpSocket::readyRead, this, &SecureTest::sock_readyRead);
115 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
116  connect(sock, &QTcpSocket::errorOccurred, this, &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(ssl, &QCA::TLS::certificateRequested, this, &SecureTest::ssl_certificateRequested);
123  connect(ssl, &QCA::TLS::handshaken, this, &SecureTest::ssl_handshaken);
124  connect(ssl, &QCA::TLS::readyRead, this, &SecureTest::ssl_readyRead);
125  connect(ssl, &QCA::TLS::readyReadOutgoing, this, &SecureTest::ssl_readyReadOutgoing);
126  connect(ssl, &QCA::TLS::closed, this, &SecureTest::ssl_closed);
127  connect(ssl, &QCA::TLS::error, this, &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(QLatin1Char(':'));
139  int port;
140  if (n != -1) {
141  host = _host.mid(0, n);
142 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
143  port = QStringView(_host).mid(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("Trying %s:%d...\n", qPrintable(host), port);
153  sock->connectToHost(host, port);
154  }
155 
156 Q_SIGNALS:
157  void quit();
158 
159 private Q_SLOTS:
160  void sock_connected()
161  {
162  // We just do this to help doxygen...
163  QCA::TLS *ssl = SecureTest::ssl;
164 
165  printf("Connected, starting TLS handshake...\n");
166 
168 
169  // We add this one to show how, and to make it work with
170  // the server example.
172 
173  if (!QCA::haveSystemStore())
174  printf("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(sock->readAll());
187  }
188 
189  void sock_connectionClosed()
190  {
191  printf("\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  {
201  sock_connectionClosed();
202  return;
203  }
204 
205  printf("\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 
215 
216  printf("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(ssl->peerCertificateValidity());
234  else
235  str += QStringLiteral("Error: No certificate");
236  printf("%s\n", qPrintable(str));
237 
238  ssl->continueAfterStep();
239 
240  printf("Let's try a GET request now.\n");
241  QString req = QStringLiteral("GET / HTTP/1.0\nHost: ") + host + QStringLiteral("\n\n");
242  ssl->write(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("Server requested client certificate.\n");
252  if (!issuerList.isEmpty()) {
253  printf("Allowed issuers:\n");
254  foreach (QCA::CertificateInfoOrdered i, issuerList)
255  printf(" %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("%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(ssl->readOutgoing());
276  }
277 
278  void ssl_closed()
279  {
280  printf("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("SSL Handshake Error!\n");
295  emit quit();
296  } else {
297  printf("SSL Error!\n");
298  emit quit();
299  }
300  }
301 
302 private:
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 
312 int main(int argc, char **argv)
313 {
315 
316  QCoreApplication app(argc, argv);
317  QString host = argc > 1 ? QString::fromLocal8Bit(argv[1]) : QStringLiteral("andbit.net");
318 
319  if (!QCA::isSupported("tls")) {
320  printf("TLS not supported!\n");
321  return 1;
322  }
323 
324  SecureTest *s = new SecureTest;
325  QObject::connect(s, &SecureTest::quit, &app, &QCoreApplication::quit);
326  s->start(host);
327  app.exec();
328  delete s;
329 
330  return 0;
331 }
Q_OBJECTQ_OBJECT
IdentityResult
Type of identity.
QString toPEM() const
Export the Certificate into a PEM format.
int cipherMaxBits() const
The number of bits of security that the cipher could use.
@ ErrorExpiredCA
The Certificate Authority has expired.
Definition: qca_cert.h:509
void write(const QByteArray &a) override
This method writes unencrypted (plain) data to the SecureLayer implementation.
int toInt(bool *ok, int base) const const
@ ErrorInvalidPurpose
The purpose does not match the intended usage.
Definition: qca_cert.h:503
void readyRead()
This signal is emitted when SecureLayer has decrypted (application side) data ready to be read.
QCA_EXPORT CertificateCollection systemStore()
Get system-wide root Certificate Authority (CA) certificates.
Q_SLOTSQ_SLOTS
@ ErrorExpired
The certificate has expired, or is not yet valid (e.g.
Definition: qca_cert.h:507
Error errorCode() const
This method returns the type of error that has occurred.
@ ErrorPathLengthExceeded
The path length from the root CA to this certificate is too long.
Definition: qca_cert.h:506
void continueAfterStep()
Resumes TLS processing.
void setTrustedCertificates(const CertificateCollection &trusted)
Set up the set of trusted certificates that will be used to verify that the certificate provided is v...
QStringRef midRef(int position, int n) const const
QCA_EXPORT void init()
Initialise QCA.
QStringView mid(qsizetype start) const const
Q_SCRIPTABLE Q_NOREPLY void start()
@ InvalidCertificate
invalid cert
static Certificate fromPEM(const QString &s, ConvertResult *result=nullptr, const QString &provider=QString())
Import the certificate from PEM format.
QByteArray toLatin1() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QByteArray read() override
This method reads decrypted (plain) data from the SecureLayer implementation.
@ HostMismatch
valid cert provided, but wrong owner
int cipherBits() const
The number of effective bits of security being used for this connection.
QString commonName() const
The common name of the subject of the certificate.
@ ValidityGood
The certificate is valid.
Definition: qca_cert.h:498
void certificateRequested()
Emitted when the server requests a certificate.
QList< CertificateInfoOrdered > issuerList() const
QString fromLocal8Bit(const char *str, int size)
void startClient(const QString &host=QString())
Start the TLS/SSL connection as a client.
void closed()
This signal is emitted when the SecureLayer connection is closed.
void writeIncoming(const QByteArray &a) override
This method accepts encoded (typically encrypted) data for processing.
@ ErrorRejected
The root CA rejected the certificate purpose.
Definition: qca_cert.h:499
QByteArray readOutgoing(int *plainBytes=nullptr) override
This method provides encoded (typically encrypted) data.
void readyRead()
@ ErrorHandshake
problem during the negotiation
@ ErrorInvalidCA
The Certificate Authority is invalid.
Definition: qca_cert.h:502
void errorOccurred(QAbstractSocket::SocketError socketError)
bool isEmpty() const const
QCA_EXPORT bool haveSystemStore()
Test if QCA can access the root CA certificates.
bool isNull() const
Test if the certificate is empty (null)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
CertificateChain peerCertificateChain() const
The CertificateChain from the peer (other end of the connection to the trusted root certificate).
@ ErrorSelfSigned
The certificate is self-signed, and is not found in the list of trusted certificates.
Definition: qca_cert.h:504
Q_SIGNALSQ_SIGNALS
ScriptableExtension * host() const
const Certificate & primary() const
Return the primary (end-user) Certificate.
Definition: qca_cert.h:1249
void readyReadOutgoing()
This signal is emitted when SecureLayer has encrypted (network side) data ready to be read.
const QList< QKeySequence > & quit()
QAbstractSocket::SocketError error() const const
QString fromLatin1(const char *str, int size)
QCA_EXPORT bool isSupported(const char *features, const QString &provider=QString())
Test if a capability (algorithm) is available.
@ ErrorRevoked
The certificate has been revoked.
Definition: qca_cert.h:505
Validity peerCertificateValidity() const
After the SSL/TLS handshake is valid, this method allows you to check if the received certificate fro...
@ ErrorValidityUnknown
Validity is unknown.
Definition: qca_cert.h:510
@ NoCertificate
identity unknown
IdentityResult peerIdentityResult() const
After the SSL/TLS handshake is complete, this method allows you to determine if the other end of the ...
QString cipherSuite() const
The cipher suite that has been negotiated for this connection.
QString toString(Qt::DateFormat format) const const
void error()
This signal is emitted when an error is detected.
@ Valid
identity is verified
Validity
The validity (or otherwise) of a certificate.
Definition: qca_cert.h:496
void handshaken()
Emitted when the protocol handshake is complete.
QString mid(int position, int n) const const
void addCertificate(const Certificate &cert)
Append a Certificate to this collection.
@ ErrorUntrusted
The certificate is not trusted.
Definition: qca_cert.h:500
@ ErrorSignatureFailed
The signature does not match.
Definition: qca_cert.h:501
char * data()
QDateTime notValidBefore() const
The earliest date that the certificate is valid.
QString toString() const
Convert to RFC 1779 string format.
Definition: qca_cert.h:577
QDateTime notValidAfter() const
The latest date that the certificate is valid.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Sep 26 2022 04:10:45 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.