QCA

sslservtest.cpp
1/*
2 Copyright (C) 2003 Justin Karneges <justin@affinix.com>
3 Copyright (C) 2006 Brad Hards <bradh@frogmouth.net>
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
36char 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
54char 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
72class SecureServer : public QObject
73{
75
76public:
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
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
131 void quit();
132
133private 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
248private:
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
263int 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}
Public Key (X.509) certificate.
Definition qca_cert.h:857
static Certificate fromPEM(const QString &s, ConvertResult *result=nullptr, const QString &provider=QString())
Import the certificate from PEM format.
bool isNull() const
Test if the certificate is empty (null)
Convenience method for initialising and cleaning up QCA.
Definition qca_core.h:660
bool isNull() const
Test if the key is null (empty)
Generic private key.
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 error()
This signal is emitted when an error is detected.
void readyReadOutgoing()
This signal is emitted when SecureLayer has encrypted (network side) data ready to be read.
void closed()
This signal is emitted when the SecureLayer connection is closed.
void readyRead()
This signal is emitted when SecureLayer has decrypted (application side) data ready to be read.
Transport Layer Security / Secure Socket Layer.
void write(const QByteArray &a) override
This method writes unencrypted (plain) data to the SecureLayer implementation.
void writeIncoming(const QByteArray &a) override
This method accepts encoded (typically encrypted) data for processing.
void continueAfterStep()
Resumes TLS processing.
@ ErrorHandshake
problem during the negotiation
void startServer()
Start the TLS/SSL connection as a server.
QByteArray readOutgoing(int *plainBytes=nullptr) override
This method provides encoded (typically encrypted) data.
void close() override
Close the link.
QByteArray read() override
This method reads decrypted (plain) data from the SecureLayer implementation.
Error errorCode() const
This method returns the type of error that has occurred.
int convertBytesWritten(qint64 encryptedBytes) override
Convert encrypted bytes written to plain text bytes written.
void setCertificate(const CertificateChain &cert, const PrivateKey &key)
The local certificate to use.
void handshaken()
Emitted when the protocol handshake is complete.
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QCA_EXPORT bool isSupported(const char *features, const QString &provider=QString())
Test if a capability (algorithm) is available.
virtual qint64 bytesAvailable() const const override
virtual void close() override
SocketError error() const const
void errorOccurred(QAbstractSocket::SocketError socketError)
void resize(qsizetype newSize, char c)
void bytesWritten(qint64 bytes)
QByteArray read(qint64 maxSize)
void readyRead()
qint64 write(const QByteArray &data)
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QString fromLatin1(QByteArrayView str)
int toInt(bool *ok, int base) const const
bool listen(const QHostAddress &address, quint16 port)
void newConnection()
virtual QTcpSocket * nextPendingConnection()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:26 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.