00001 /* 00002 Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com> 00003 00004 Permission is hereby granted, free of charge, to any person obtaining a copy 00005 of this software and associated documentation files (the "Software"), to deal 00006 in the Software without restriction, including without limitation the rights 00007 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00008 copies of the Software, and to permit persons to whom the Software is 00009 furnished to do so, subject to the following conditions: 00010 00011 The above copyright notice and this permission notice shall be included in 00012 all copies or substantial portions of the Software. 00013 00014 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00015 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00016 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00017 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 00018 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00019 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00020 */ 00021 00022 #include <QtCrypto> 00023 00024 #include <QCoreApplication> 00025 #include <QTcpSocket> 00026 00027 char exampleCA_cert[] = 00028 "-----BEGIN CERTIFICATE-----\n" 00029 "MIICSzCCAbSgAwIBAgIBADANBgkqhkiG9w0BAQUFADA4MRMwEQYDVQQDEwpFeGFt\n" 00030 "cGxlIENBMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwHhcNMDYw\n" 00031 "MzE1MDY1ODMyWhcNMDYwNDE1MDY1ODMyWjA4MRMwEQYDVQQDEwpFeGFtcGxlIENB\n" 00032 "MQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwgZ8wDQYJKoZIhvcN\n" 00033 "AQEBBQADgY0AMIGJAoGBAL6ULdOxmpeZ+G/ypV12eNO4qnHSVIPTrYPkQuweXqPy\n" 00034 "atwGFheG+hLVsNIh9GGOS0tCe7a3hBBKN0BJg1ppfk2x39cDx7hefYqjBuZvp/0O\n" 00035 "8Ja3qlQiJLezITZKLxMBrsibcvcuH8zpfUdys2yaN+YGeqNfjQuoNN3Byl1TwuGJ\n" 00036 "AgMBAAGjZTBjMB0GA1UdDgQWBBSQKCUCLNM7uKrAt5o7qv/yQm6qEzASBgNVHRMB\n" 00037 "Af8ECDAGAQEBAgEIMB4GA1UdEQQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wDgYD\n" 00038 "VR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4GBAAh+SIeT1Ao5qInw8oMSoTdO\n" 00039 "lQ6h67ec/Jk5KmK4OoskuimmHI0Sp0C5kOCLehXbsVWW8pXsNC2fv0d2HkdaSUcX\n" 00040 "hwLzqgyZXd4mupIYlaOTZhuHDwWPCAOZS4LVsi2tndTRHKCP12441JjNKhmZRhkR\n" 00041 "u5zzD60nWgM9dKTaxuZM\n" 00042 "-----END CERTIFICATE-----\n"; 00043 00044 void showCertInfo(const QCA::Certificate &cert) 00045 { 00046 printf("-- Cert --\n"); 00047 printf(" CN: %s\n", qPrintable(cert.commonName())); 00048 printf(" Valid from: %s, until %s\n", 00049 qPrintable(cert.notValidBefore().toString()), 00050 qPrintable(cert.notValidAfter().toString())); 00051 printf(" PEM:\n%s\n", qPrintable(cert.toPEM())); 00052 } 00053 00054 static QString validityToString(QCA::Validity v) 00055 { 00056 QString s; 00057 switch(v) 00058 { 00059 case QCA::ValidityGood: 00060 s = "Validated"; 00061 break; 00062 case QCA::ErrorRejected: 00063 s = "Root CA is marked to reject the specified purpose"; 00064 break; 00065 case QCA::ErrorUntrusted: 00066 s = "Certificate not trusted for the required purpose"; 00067 break; 00068 case QCA::ErrorSignatureFailed: 00069 s = "Invalid signature"; 00070 break; 00071 case QCA::ErrorInvalidCA: 00072 s = "Invalid CA certificate"; 00073 break; 00074 case QCA::ErrorInvalidPurpose: 00075 s = "Invalid certificate purpose"; 00076 break; 00077 case QCA::ErrorSelfSigned: 00078 s = "Certificate is self-signed"; 00079 break; 00080 case QCA::ErrorRevoked: 00081 s = "Certificate has been revoked"; 00082 break; 00083 case QCA::ErrorPathLengthExceeded: 00084 s = "Maximum certificate chain length exceeded"; 00085 break; 00086 case QCA::ErrorExpired: 00087 s = "Certificate has expired"; 00088 break; 00089 case QCA::ErrorExpiredCA: 00090 s = "CA has expired"; 00091 break; 00092 case QCA::ErrorValidityUnknown: 00093 default: 00094 s = "General certificate validation error"; 00095 break; 00096 } 00097 return s; 00098 } 00099 00100 class SecureTest : public QObject 00101 { 00102 Q_OBJECT 00103 public: 00104 SecureTest() 00105 { 00106 sock_done = false; 00107 ssl_done = false; 00108 00109 sock = new QTcpSocket; 00110 connect(sock, SIGNAL(connected()), SLOT(sock_connected())); 00111 connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); 00112 connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), 00113 SLOT(sock_error(QAbstractSocket::SocketError))); 00114 00115 ssl = new QCA::TLS; 00116 connect(ssl, SIGNAL(certificateRequested()), SLOT(ssl_certificateRequested())); 00117 connect(ssl, SIGNAL(handshaken()), SLOT(ssl_handshaken())); 00118 connect(ssl, SIGNAL(readyRead()), SLOT(ssl_readyRead())); 00119 connect(ssl, SIGNAL(readyReadOutgoing()), 00120 SLOT(ssl_readyReadOutgoing())); 00121 connect(ssl, SIGNAL(closed()), SLOT(ssl_closed())); 00122 connect(ssl, SIGNAL(error()), SLOT(ssl_error())); 00123 } 00124 00125 ~SecureTest() 00126 { 00127 delete ssl; 00128 delete sock; 00129 } 00130 00131 void start(const QString &_host) 00132 { 00133 int n = _host.indexOf(':'); 00134 int port; 00135 if(n != -1) 00136 { 00137 host = _host.mid(0, n); 00138 port = _host.mid(n+1).toInt(); 00139 } 00140 else 00141 { 00142 host = _host; 00143 port = 443; 00144 } 00145 00146 printf("Trying %s:%d...\n", qPrintable(host), port); 00147 sock->connectToHost(host, port); 00148 } 00149 00150 signals: 00151 void quit(); 00152 00153 private slots: 00154 void sock_connected() 00155 { 00156 // We just do this to help doxygen... 00157 QCA::TLS *ssl = SecureTest::ssl; 00158 00159 printf("Connected, starting TLS handshake...\n"); 00160 00161 QCA::CertificateCollection rootCerts = QCA::systemStore(); 00162 00163 // We add this one to show how, and to make it work with 00164 // the server example. 00165 rootCerts.addCertificate(QCA::Certificate::fromPEM(exampleCA_cert)); 00166 00167 if(!QCA::haveSystemStore()) 00168 printf("Warning: no root certs\n"); 00169 else 00170 ssl->setTrustedCertificates(rootCerts); 00171 00172 ssl->startClient(host); 00173 } 00174 00175 void sock_readyRead() 00176 { 00177 // We just do this to help doxygen... 00178 QCA::TLS *ssl = SecureTest::ssl; 00179 00180 ssl->writeIncoming(sock->readAll()); 00181 } 00182 00183 void sock_connectionClosed() 00184 { 00185 printf("\nConnection closed.\n"); 00186 sock_done = true; 00187 00188 if(ssl_done && sock_done) 00189 emit quit(); 00190 } 00191 00192 void sock_error(QAbstractSocket::SocketError x) 00193 { 00194 if(x == QAbstractSocket::RemoteHostClosedError) 00195 { 00196 sock_connectionClosed(); 00197 return; 00198 } 00199 00200 printf("\nSocket error.\n"); 00201 emit quit(); 00202 } 00203 00204 void ssl_handshaken() 00205 { 00206 // We just do this to help doxygen... 00207 QCA::TLS *ssl = SecureTest::ssl; 00208 00209 QCA::TLS::IdentityResult r = ssl->peerIdentityResult(); 00210 00211 printf("Successful SSL handshake using %s (%i of %i bits)\n", 00212 qPrintable(ssl->cipherSuite()), 00213 ssl->cipherBits(), 00214 ssl->cipherMaxBits() ); 00215 if(r != QCA::TLS::NoCertificate) 00216 { 00217 cert = ssl->peerCertificateChain().primary(); 00218 if(!cert.isNull()) 00219 showCertInfo(cert); 00220 } 00221 00222 QString str = "Peer Identity: "; 00223 if(r == QCA::TLS::Valid) 00224 str += "Valid"; 00225 else if(r == QCA::TLS::HostMismatch) 00226 str += "Error: Wrong certificate"; 00227 else if(r == QCA::TLS::InvalidCertificate) 00228 str += "Error: Invalid certificate.\n -> Reason: " + 00229 validityToString(ssl->peerCertificateValidity()); 00230 else 00231 str += "Error: No certificate"; 00232 printf("%s\n", qPrintable(str)); 00233 00234 ssl->continueAfterStep(); 00235 00236 printf("Let's try a GET request now.\n"); 00237 QString req = "GET / HTTP/1.0\nHost: " + host + "\n\n"; 00238 ssl->write(req.toLatin1()); 00239 } 00240 00241 void ssl_certificateRequested() 00242 { 00243 // We just do this to help doxygen... 00244 QCA::TLS *ssl = SecureTest::ssl; 00245 00246 printf("Server requested client certificate.\n"); 00247 QList<QCA::CertificateInfoOrdered> issuerList = ssl->issuerList(); 00248 if(!issuerList.isEmpty()) 00249 { 00250 printf("Allowed issuers:\n"); 00251 foreach(QCA::CertificateInfoOrdered i, issuerList) 00252 printf(" %s\n", qPrintable(i.toString())); 00253 } 00254 00255 ssl->continueAfterStep(); 00256 } 00257 00258 void ssl_readyRead() 00259 { 00260 // We just do this to help doxygen... 00261 QCA::TLS *ssl = SecureTest::ssl; 00262 00263 QByteArray a = ssl->read(); 00264 printf("%s", a.data()); 00265 } 00266 00267 void ssl_readyReadOutgoing() 00268 { 00269 // We just do this to help doxygen... 00270 QCA::TLS *ssl = SecureTest::ssl; 00271 00272 sock->write(ssl->readOutgoing()); 00273 } 00274 00275 void ssl_closed() 00276 { 00277 printf("SSL session closed.\n"); 00278 ssl_done = true; 00279 00280 if(ssl_done && sock_done) 00281 emit quit(); 00282 } 00283 00284 void ssl_error() 00285 { 00286 // We just do this to help doxygen... 00287 QCA::TLS *ssl = SecureTest::ssl; 00288 00289 int x = ssl->errorCode(); 00290 if(x == QCA::TLS::ErrorHandshake) 00291 { 00292 printf("SSL Handshake Error!\n"); 00293 emit quit(); 00294 } 00295 else 00296 { 00297 printf("SSL Error!\n"); 00298 emit quit(); 00299 } 00300 } 00301 00302 private: 00303 QString host; 00304 QTcpSocket *sock; 00305 QCA::TLS *ssl; 00306 QCA::Certificate cert; 00307 bool sock_done, ssl_done; 00308 }; 00309 00310 #include "ssltest.moc" 00311 00312 int main(int argc, char **argv) 00313 { 00314 QCA::Initializer init; 00315 00316 QCoreApplication app(argc, argv); 00317 QString host = argc > 1 ? argv[1] : "andbit.net"; 00318 00319 if(!QCA::isSupported("tls")) 00320 { 00321 printf("TLS not supported!\n"); 00322 return 1; 00323 } 00324 00325 SecureTest *s = new SecureTest; 00326 QObject::connect(s, SIGNAL(quit()), &app, SLOT(quit())); 00327 s->start(host); 00328 app.exec(); 00329 delete s; 00330 00331 return 0; 00332 }