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 }
Q_OBJECTQ_OBJECT
void readyRead()
This signal is emitted when SecureLayer has decrypted (application side) data ready to be read.
Q_SLOTSQ_SLOTS
void newConnection()
void bytesWritten(qint64 bytes)
QCA_EXPORT void init()
Initialise QCA.
Q_SCRIPTABLE Q_NOREPLY void start()
virtual void close() override
static Certificate fromPEM(const QString &s, ConvertResult *result=nullptr, const QString &provider=QString())
Import the certificate from PEM format.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void deleteLater()
void closed()
This signal is emitted when the SecureLayer connection is closed.
void readyRead()
@ ErrorHandshake
problem during the negotiation
int toInt(bool *ok, int base) const const
void errorOccurred(QAbstractSocket::SocketError socketError)
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.
Q_SIGNALSQ_SIGNALS
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.
void error()
This signal is emitted when an error is detected.
void handshaken()
Emitted when the protocol handshake is complete.
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.