File indexing completed on 2024-04-28 04:43:40

0001 /*
0002  Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com>
0003 
0004  Permission is hereby granted, free of charge, to any person obtaining a copy
0005  of this software and associated documentation files (the "Software"), to deal
0006  in the Software without restriction, including without limitation the rights
0007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0008  copies of the Software, and to permit persons to whom the Software is
0009  furnished to do so, subject to the following conditions:
0010 
0011  The above copyright notice and this permission notice shall be included in
0012  all copies or substantial portions of the Software.
0013 
0014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
0017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
0018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0020 */
0021 
0022 #include <QtCrypto>
0023 
0024 #include <QCoreApplication>
0025 #include <QTcpSocket>
0026 
0027 #ifdef QT_STATICPLUGIN
0028 #include "import_plugins.h"
0029 #endif
0030 
0031 char exampleCA_cert[] =
0032     "-----BEGIN CERTIFICATE-----\n"
0033     "MIICSzCCAbSgAwIBAgIBADANBgkqhkiG9w0BAQUFADA4MRMwEQYDVQQDEwpFeGFt\n"
0034     "cGxlIENBMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwHhcNMDYw\n"
0035     "MzE1MDY1ODMyWhcNMDYwNDE1MDY1ODMyWjA4MRMwEQYDVQQDEwpFeGFtcGxlIENB\n"
0036     "MQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwgZ8wDQYJKoZIhvcN\n"
0037     "AQEBBQADgY0AMIGJAoGBAL6ULdOxmpeZ+G/ypV12eNO4qnHSVIPTrYPkQuweXqPy\n"
0038     "atwGFheG+hLVsNIh9GGOS0tCe7a3hBBKN0BJg1ppfk2x39cDx7hefYqjBuZvp/0O\n"
0039     "8Ja3qlQiJLezITZKLxMBrsibcvcuH8zpfUdys2yaN+YGeqNfjQuoNN3Byl1TwuGJ\n"
0040     "AgMBAAGjZTBjMB0GA1UdDgQWBBSQKCUCLNM7uKrAt5o7qv/yQm6qEzASBgNVHRMB\n"
0041     "Af8ECDAGAQEBAgEIMB4GA1UdEQQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wDgYD\n"
0042     "VR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4GBAAh+SIeT1Ao5qInw8oMSoTdO\n"
0043     "lQ6h67ec/Jk5KmK4OoskuimmHI0Sp0C5kOCLehXbsVWW8pXsNC2fv0d2HkdaSUcX\n"
0044     "hwLzqgyZXd4mupIYlaOTZhuHDwWPCAOZS4LVsi2tndTRHKCP12441JjNKhmZRhkR\n"
0045     "u5zzD60nWgM9dKTaxuZM\n"
0046     "-----END CERTIFICATE-----\n";
0047 
0048 void showCertInfo(const QCA::Certificate &cert)
0049 {
0050     printf("-- Cert --\n");
0051     printf(" CN: %s\n", qPrintable(cert.commonName()));
0052     printf(" Valid from: %s, until %s\n",
0053            qPrintable(cert.notValidBefore().toString()),
0054            qPrintable(cert.notValidAfter().toString()));
0055     printf(" PEM:\n%s\n", qPrintable(cert.toPEM()));
0056 }
0057 
0058 static QString validityToString(QCA::Validity v)
0059 {
0060     QString s;
0061     switch (v) {
0062     case QCA::ValidityGood:
0063         s = QStringLiteral("Validated");
0064         break;
0065     case QCA::ErrorRejected:
0066         s = QStringLiteral("Root CA is marked to reject the specified purpose");
0067         break;
0068     case QCA::ErrorUntrusted:
0069         s = QStringLiteral("Certificate not trusted for the required purpose");
0070         break;
0071     case QCA::ErrorSignatureFailed:
0072         s = QStringLiteral("Invalid signature");
0073         break;
0074     case QCA::ErrorInvalidCA:
0075         s = QStringLiteral("Invalid CA certificate");
0076         break;
0077     case QCA::ErrorInvalidPurpose:
0078         s = QStringLiteral("Invalid certificate purpose");
0079         break;
0080     case QCA::ErrorSelfSigned:
0081         s = QStringLiteral("Certificate is self-signed");
0082         break;
0083     case QCA::ErrorRevoked:
0084         s = QStringLiteral("Certificate has been revoked");
0085         break;
0086     case QCA::ErrorPathLengthExceeded:
0087         s = QStringLiteral("Maximum certificate chain length exceeded");
0088         break;
0089     case QCA::ErrorExpired:
0090         s = QStringLiteral("Certificate has expired");
0091         break;
0092     case QCA::ErrorExpiredCA:
0093         s = QStringLiteral("CA has expired");
0094         break;
0095     case QCA::ErrorValidityUnknown:
0096     default:
0097         s = QStringLiteral("General certificate validation error");
0098         break;
0099     }
0100     return s;
0101 }
0102 
0103 class SecureTest : public QObject
0104 {
0105     Q_OBJECT
0106 public:
0107     SecureTest()
0108     {
0109         sock_done = false;
0110         ssl_done  = false;
0111 
0112         sock = new QTcpSocket;
0113         connect(sock, &QTcpSocket::connected, this, &SecureTest::sock_connected);
0114         connect(sock, &QTcpSocket::readyRead, this, &SecureTest::sock_readyRead);
0115 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
0116         connect(sock, &QTcpSocket::errorOccurred, this, &SecureTest::sock_error);
0117 #else
0118         connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &SecureTest::sock_error);
0119 #endif
0120 
0121         ssl = new QCA::TLS;
0122         connect(ssl, &QCA::TLS::certificateRequested, this, &SecureTest::ssl_certificateRequested);
0123         connect(ssl, &QCA::TLS::handshaken, this, &SecureTest::ssl_handshaken);
0124         connect(ssl, &QCA::TLS::readyRead, this, &SecureTest::ssl_readyRead);
0125         connect(ssl, &QCA::TLS::readyReadOutgoing, this, &SecureTest::ssl_readyReadOutgoing);
0126         connect(ssl, &QCA::TLS::closed, this, &SecureTest::ssl_closed);
0127         connect(ssl, &QCA::TLS::error, this, &SecureTest::ssl_error);
0128     }
0129 
0130     ~SecureTest() override
0131     {
0132         delete ssl;
0133         delete sock;
0134     }
0135 
0136     void start(const QString &_host)
0137     {
0138         int n = _host.indexOf(QLatin1Char(':'));
0139         int port;
0140         if (n != -1) {
0141             host = _host.mid(0, n);
0142 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
0143             port = QStringView(_host).mid(n + 1).toInt();
0144 #else
0145             port = _host.midRef(n + 1).toInt();
0146 #endif
0147         } else {
0148             host = _host;
0149             port = 443;
0150         }
0151 
0152         printf("Trying %s:%d...\n", qPrintable(host), port);
0153         sock->connectToHost(host, port);
0154     }
0155 
0156 Q_SIGNALS:
0157     void quit();
0158 
0159 private Q_SLOTS:
0160     void sock_connected()
0161     {
0162         // We just do this to help doxygen...
0163         QCA::TLS *ssl = SecureTest::ssl;
0164 
0165         printf("Connected, starting TLS handshake...\n");
0166 
0167         QCA::CertificateCollection rootCerts = QCA::systemStore();
0168 
0169         // We add this one to show how, and to make it work with
0170         // the server example.
0171         rootCerts.addCertificate(QCA::Certificate::fromPEM(QString::fromLatin1(exampleCA_cert)));
0172 
0173         if (!QCA::haveSystemStore())
0174             printf("Warning: no root certs\n");
0175         else
0176             ssl->setTrustedCertificates(rootCerts);
0177 
0178         ssl->startClient(host);
0179     }
0180 
0181     void sock_readyRead()
0182     {
0183         // We just do this to help doxygen...
0184         QCA::TLS *ssl = SecureTest::ssl;
0185 
0186         ssl->writeIncoming(sock->readAll());
0187     }
0188 
0189     void sock_connectionClosed()
0190     {
0191         printf("\nConnection closed.\n");
0192         sock_done = true;
0193 
0194         if (ssl_done && sock_done)
0195             emit quit();
0196     }
0197 
0198     void sock_error(QAbstractSocket::SocketError x)
0199     {
0200         if (x == QAbstractSocket::RemoteHostClosedError) {
0201             sock_connectionClosed();
0202             return;
0203         }
0204 
0205         printf("\nSocket error.\n");
0206         emit quit();
0207     }
0208 
0209     void ssl_handshaken()
0210     {
0211         // We just do this to help doxygen...
0212         QCA::TLS *ssl = SecureTest::ssl;
0213 
0214         QCA::TLS::IdentityResult r = ssl->peerIdentityResult();
0215 
0216         printf("Successful SSL handshake using %s (%i of %i bits)\n",
0217                qPrintable(ssl->cipherSuite()),
0218                ssl->cipherBits(),
0219                ssl->cipherMaxBits());
0220         if (r != QCA::TLS::NoCertificate) {
0221             cert = ssl->peerCertificateChain().primary();
0222             if (!cert.isNull())
0223                 showCertInfo(cert);
0224         }
0225 
0226         QString str = QStringLiteral("Peer Identity: ");
0227         if (r == QCA::TLS::Valid)
0228             str += QStringLiteral("Valid");
0229         else if (r == QCA::TLS::HostMismatch)
0230             str += QStringLiteral("Error: Wrong certificate");
0231         else if (r == QCA::TLS::InvalidCertificate)
0232             str += QStringLiteral("Error: Invalid certificate.\n -> Reason: ") +
0233                 validityToString(ssl->peerCertificateValidity());
0234         else
0235             str += QStringLiteral("Error: No certificate");
0236         printf("%s\n", qPrintable(str));
0237 
0238         ssl->continueAfterStep();
0239 
0240         printf("Let's try a GET request now.\n");
0241         QString req = QStringLiteral("GET / HTTP/1.0\nHost: ") + host + QStringLiteral("\n\n");
0242         ssl->write(req.toLatin1());
0243     }
0244 
0245     void ssl_certificateRequested()
0246     {
0247         // We just do this to help doxygen...
0248         QCA::TLS *ssl = SecureTest::ssl;
0249 
0250         printf("Server requested client certificate.\n");
0251         QList<QCA::CertificateInfoOrdered> issuerList = ssl->issuerList();
0252         if (!issuerList.isEmpty()) {
0253             printf("Allowed issuers:\n");
0254             foreach (QCA::CertificateInfoOrdered i, issuerList)
0255                 printf("  %s\n", qPrintable(i.toString()));
0256         }
0257 
0258         ssl->continueAfterStep();
0259     }
0260 
0261     void ssl_readyRead()
0262     {
0263         // We just do this to help doxygen...
0264         QCA::TLS *ssl = SecureTest::ssl;
0265 
0266         QByteArray a = ssl->read();
0267         printf("%s", a.data());
0268     }
0269 
0270     void ssl_readyReadOutgoing()
0271     {
0272         // We just do this to help doxygen...
0273         QCA::TLS *ssl = SecureTest::ssl;
0274 
0275         sock->write(ssl->readOutgoing());
0276     }
0277 
0278     void ssl_closed()
0279     {
0280         printf("SSL session closed.\n");
0281         ssl_done = true;
0282 
0283         if (ssl_done && sock_done)
0284             emit quit();
0285     }
0286 
0287     void ssl_error()
0288     {
0289         // We just do this to help doxygen...
0290         QCA::TLS *ssl = SecureTest::ssl;
0291 
0292         int x = ssl->errorCode();
0293         if (x == QCA::TLS::ErrorHandshake) {
0294             printf("SSL Handshake Error!\n");
0295             emit quit();
0296         } else {
0297             printf("SSL Error!\n");
0298             emit quit();
0299         }
0300     }
0301 
0302 private:
0303     QString          host;
0304     QTcpSocket      *sock;
0305     QCA::TLS        *ssl;
0306     QCA::Certificate cert;
0307     bool             sock_done, ssl_done;
0308 };
0309 
0310 #include "ssltest.moc"
0311 
0312 int main(int argc, char **argv)
0313 {
0314     QCA::Initializer init;
0315 
0316     QCoreApplication app(argc, argv);
0317     QString          host = argc > 1 ? QString::fromLocal8Bit(argv[1]) : QStringLiteral("andbit.net");
0318 
0319     if (!QCA::isSupported("tls")) {
0320         printf("TLS not supported!\n");
0321         return 1;
0322     }
0323 
0324     SecureTest *s = new SecureTest;
0325     QObject::connect(s, &SecureTest::quit, &app, &QCoreApplication::quit);
0326     s->start(host);
0327     app.exec();
0328     delete s;
0329 
0330     return 0;
0331 }