File indexing completed on 2024-10-13 07:16:34
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 }