QCA

sslservtest.cpp
1 /*
2  Copyright (C) 2003 Justin Karneges <[email protected]>
3  Copyright (C) 2006 Brad Hards <[email protected]>
4 
5  Permission is hereby granted, free of charge, to any person obtaining a copy
6  of this software and associated documentation files (the "Software"), to deal
7  in the Software without restriction, including without limitation the rights
8  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  copies of the Software, and to permit persons to whom the Software is
10  furnished to do so, subject to the following conditions:
11 
12  The above copyright notice and this permission notice shall be included in
13  all copies or substantial portions of the Software.
14 
15  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22 
23 #include <QtCrypto>
24 
25 #include <QCoreApplication>
26 #include <QDebug>
27 #include <QHostAddress>
28 #include <QTcpServer>
29 #include <QTcpSocket>
30 #include <QTimer>
31 
32 #ifdef QT_STATICPLUGIN
33 #include "import_plugins.h"
34 #endif
35 
36 char pemdata_cert[] =
37  "-----BEGIN CERTIFICATE-----\n"
38  "MIICeTCCAeKgAwIBAgIRAKKKnOj6Aarmwf0phApitVAwDQYJKoZIhvcNAQEFBQAw\n"
39  "ODELMAkGA1UEBhMCVVMxFDASBgNVBAoTC0V4YW1wbGUgT3JnMRMwEQYDVQQDEwpF\n"
40  "eGFtcGxlIENBMB4XDTA2MDMxNTA3MDU1MloXDTA3MDMxNTA3MDU1MlowOjEVMBMG\n"
41  "A1UEAxMMRXhhbXBsZSBVc2VyMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBs\n"
42  "ZSBPcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPkKn0FfHMvRZv+3uFcw\n"
43  "VrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9sEAY\n"
44  "YNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6YjR2\n"
45  "NEvIKt1P4mHzYXLmwoF24C1bAgMBAAGjgYAwfjAdBgNVHQ4EFgQUmQIdzyDaPYWF\n"
46  "fPJ8PPOOm1eSsucwHwYDVR0jBBgwFoAUkCglAizTO7iqwLeaO6r/8kJuqhMwDAYD\n"
47  "VR0TAQH/BAIwADAeBgNVHREEFzAVgRNleGFtcGxlQGV4YW1wbGUuY29tMA4GA1Ud\n"
48  "DwEB/wQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQAuhbiUgy2a++EUccaonID7eTJZ\n"
49  "F3D5qXMqUpQxlYxU8du+9AxDD7nFxTMkQC2pzfmEc1znRNmJ1ZeLRL72VYsVndcT\n"
50  "psyM8ABkvPp1d2jWIyccVjGpt+/RN5IPKm/YIbtIZcywvWuXrOp1lanVmppLfPnO\n"
51  "6yneBkC9iqjOv/+Q+A==\n"
52  "-----END CERTIFICATE-----\n";
53 
54 char pemdata_privkey[] =
55  "-----BEGIN PRIVATE KEY-----\n"
56  "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPkKn0FfHMvRZv+3\n"
57  "uFcwVrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9\n"
58  "sEAYYNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6\n"
59  "YjR2NEvIKt1P4mHzYXLmwoF24C1bAgMBAAECgYEAyIjJHDaeVXDU42zovyxpZE4n\n"
60  "PcOEryY+gdFJE8DFgUD4f1huFsj4iCuNg+PaG42p+hf9IARNvSho/RcEaVg4AJrV\n"
61  "jRP8r7fSqcIGr6lGuvDFFv3SU5ddy84g5oqLYGKvuPSHMGfVsZSxAwOrzD4bH19L\n"
62  "SNqtNcpdBsBd7ZiEE4ECQQD/oJGui9D5Dx3QVcS+QV4F8wuyN9jYIANmX/17o0fl\n"
63  "BL0bwRU4RICwadrcybi5N0JQLIYSUm2HGqNvAJbtnuQxAkEA+WeYLLYPeawcy+WU\n"
64  "kGcOR7BUjHiG71+6cvU4XIDW2bezA04fqWXkZRFAwHTMpQb785/XalFftgS21kql\n"
65  "8yLDSwJAHkeT2hwftdDPlEUEmBDAJW5DvWmWGwu3u2G1cfbGZl9oUyhM7ixXHg57\n"
66  "6VlPs0jTZxHPE86FwNIr99MXDbCbkQJBAMDFOJK+ecGirXNP1P+0GA6DFSap9inJ\n"
67  "BRTbwx+EmgwX966DUOefEOSpbDIVVSPs/Qr2LgtIMEFA7Y0+j3wZD3cCQBsTwccd\n"
68  "ASQx59xakpq11eOlTYz14rjwodr4QMyj26WxEPJtz7hKokx/+EH6fWuPIUSrROM5\n"
69  "07y2gaVbYxtis0s=\n"
70  "-----END PRIVATE KEY-----\n";
71 
72 class SecureServer : public QObject
73 {
74  Q_OBJECT
75 
76 public:
77  enum
78  {
79  Idle,
80  Handshaking,
81  Active,
82  Closing
83  };
84 
85  SecureServer(quint16 _port)
86  : port(_port)
87  {
88  server = new QTcpServer;
89  connect(server, &QTcpServer::newConnection, this, &SecureServer::server_handleConnection);
90 
91  ssl = new QCA::TLS;
92  connect(ssl, &QCA::TLS::handshaken, this, &SecureServer::ssl_handshaken);
93  connect(ssl, &QCA::TLS::readyRead, this, &SecureServer::ssl_readyRead);
94  connect(ssl, &QCA::TLS::readyReadOutgoing, this, &SecureServer::ssl_readyReadOutgoing);
95  connect(ssl, &QCA::TLS::closed, this, &SecureServer::ssl_closed);
96  connect(ssl, &QCA::TLS::error, this, &SecureServer::ssl_error);
97 
98  cert = QCA::Certificate::fromPEM(QString::fromLatin1(pemdata_cert));
99  privkey = QCA::PrivateKey::fromPEM(QString::fromLatin1(pemdata_privkey));
100 
101  mode = Idle;
102  }
103 
104  ~SecureServer() override
105  {
106  delete ssl;
107  delete server;
108  }
109 
110  void start()
111  {
112  if (cert.isNull()) {
113  qDebug() << "Error loading cert!";
114  QTimer::singleShot(0, this, &SecureServer::quit);
115  return;
116  }
117  if (privkey.isNull()) {
118  qDebug() << "Error loading private key!";
119  QTimer::singleShot(0, this, &SecureServer::quit);
120  return;
121  }
122  if (false == server->listen(QHostAddress::Any, port)) {
123  qDebug() << "Error binding to port " << port;
124  QTimer::singleShot(0, this, &SecureServer::quit);
125  return;
126  }
127  qDebug() << "Listening on port" << port;
128  }
129 
130 Q_SIGNALS:
131  void quit();
132 
133 private Q_SLOTS:
134  void sock_readyRead()
135  {
136  QByteArray buf(sock->bytesAvailable(), 0x00);
137 
138  int num = sock->read(buf.data(), buf.size());
139 
140  if (-1 == num)
141  qDebug() << "Error reading data from socket";
142 
143  if (num < buf.size())
144  buf.resize(num);
145 
146  ssl->writeIncoming(buf);
147  }
148 
149  void server_handleConnection()
150  {
151  // Note: only 1 connection supported at a time in this example!
152  if (mode != Idle) {
153  QTcpSocket *tmp = server->nextPendingConnection();
154  tmp->close();
156  qDebug() << "throwing away extra connection";
157  return;
158  }
159  mode = Handshaking;
160  sock = server->nextPendingConnection();
161  connect(sock, &QTcpSocket::readyRead, this, &SecureServer::sock_readyRead);
162  connect(sock, &QTcpSocket::disconnected, this, &SecureServer::sock_disconnected);
163 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
164  connect(sock, &QTcpSocket::errorOccurred, this, &SecureServer::sock_error);
165 #else
166  connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &SecureServer::sock_error);
167 #endif
168  connect(sock, &QTcpSocket::bytesWritten, this, &SecureServer::sock_bytesWritten);
169 
170  qDebug() << "Connection received! Starting TLS handshake.";
171  ssl->setCertificate(cert, privkey);
172  ssl->startServer();
173  }
174 
175  void sock_disconnected()
176  {
177  qDebug() << "Connection closed.";
178  }
179 
180  void sock_bytesWritten(qint64 x)
181  {
182  if (mode == Active && sent) {
183  qint64 bytes = ssl->convertBytesWritten(x);
184  bytesLeft -= bytes;
185 
186  if (bytesLeft == 0) {
187  mode = Closing;
188  qDebug() << "Data transfer complete - SSL shutting down";
189  ssl->close();
190  }
191  }
192  }
193 
194  void sock_error(QAbstractSocket::SocketError error)
195  {
196  qDebug() << "Socket error: " << (unsigned)error;
197  }
198 
199  void ssl_handshaken()
200  {
201  qDebug() << "Successful SSL handshake. Waiting for newline.";
202  bytesLeft = 0;
203  sent = false;
204  mode = Active;
205  ssl->continueAfterStep();
206  }
207 
208  void ssl_readyRead()
209  {
210  ssl->read();
211  QByteArray b =
212  "<html>\n"
213  "<head><title>Test</title></head>\n"
214  "<body>this is only a test</body>\n"
215  "</html>\n";
216 
217  qDebug() << "Sending test response.";
218  sent = true;
219  ssl->write(b);
220  }
221 
222  void ssl_readyReadOutgoing()
223  {
224  int plainBytes;
225  QByteArray outgoingData = ssl->readOutgoing(&plainBytes);
226  sock->write(outgoingData);
227  }
228 
229  void ssl_closed()
230  {
231  qDebug() << "Closing socket.";
232  sock->close();
233  mode = Idle;
234  }
235 
236  void ssl_error()
237  {
238  if (ssl->errorCode() == QCA::TLS::ErrorHandshake) {
239  qDebug() << "SSL Handshake Error! Closing.";
240  sock->close();
241  } else {
242  qDebug() << "SSL Error! Closing.";
243  sock->close();
244  }
245  mode = Idle;
246  }
247 
248 private:
249  quint16 port;
250  QTcpServer * server;
251  QTcpSocket * sock;
252  QCA::TLS * ssl;
253  QCA::Certificate cert;
254  QCA::PrivateKey privkey;
255 
256  bool sent;
257  int mode;
258  qint64 bytesLeft;
259 };
260 
261 #include "sslservtest.moc"
262 
263 int main(int argc, char **argv)
264 {
266 
267  QCoreApplication app(argc, argv);
268  int port = argc > 1 ? QString::fromLatin1(argv[1]).toInt() : 8000;
269 
270  if (!QCA::isSupported("tls")) {
271  qDebug() << "TLS not supported!";
272  return 1;
273  }
274 
275  SecureServer *server = new SecureServer(port);
276  QObject::connect(server, &SecureServer::quit, &app, &QCoreApplication::quit);
277  server->start();
278  app.exec();
279  delete server;
280 
281  return 0;
282 }
void handshaken()
Emitted when the protocol handshake is complete.
Generic private key.
void readyRead()
This signal is emitted when SecureLayer has decrypted (application side) data ready to be read...
void newConnection()
QAbstractSocket::SocketError error() const const
Q_SIGNALSQ_SIGNALS
void closed()
This signal is emitted when the SecureLayer connection is closed.
static Certificate fromPEM(const QString &s, ConvertResult *result=nullptr, const QString &provider=QString())
Import the certificate from PEM format.
void resize(int size)
Transport Layer Security / Secure Socket Layer.
virtual void close() override
Q_OBJECTQ_OBJECT
QCA_EXPORT bool isSupported(const char *features, const QString &provider=QString())
Test if a capability (algorithm) is available.
int toInt(bool *ok, int base) const const
void readyReadOutgoing()
This signal is emitted when SecureLayer has encrypted (network side) data ready to be read...
void deleteLater()
void bytesWritten(qint64 bytes)
QCA_EXPORT void init()
Initialise QCA.
static PrivateKey fromPEM(const QString &s, const SecureArray &passphrase=SecureArray(), ConvertResult *result=nullptr, const QString &provider=QString())
Import the key from Privacy Enhanced Mail (PEM) format.
void errorOccurred(QAbstractSocket::SocketError socketError)
void error()
This signal is emitted when an error is detected.
Convenience method for initialising and cleaning up QCA.
Definition: qca_core.h:659
QString fromLatin1(const char *str, int size)
Public Key (X.509) certificate.
Definition: qca_cert.h:856
problem during the negotiation
void readyRead()
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
const QList< QKeySequence > & quit()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Sep 25 2021 23:05:35 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.