KIO
ksmimecrypto.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 * 00003 * Copyright (C) 2003 Stefan Rompf <sux@loplof.de> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "ksmimecrypto.h" 00022 00023 #include <QtCore/QList> 00024 #include <QtCore/QString> 00025 #include <kdebug.h> 00026 00027 #include "kopenssl.h" 00028 #include "ksslcertificate.h" 00029 #include "ksslpkcs12.h" 00030 00031 // this hack provided by Malte Starostik to avoid glibc/openssl bug 00032 // on some systems 00033 #ifdef KSSL_HAVE_SSL 00034 #define crypt _openssl_crypt 00035 #include <openssl/err.h> 00036 #undef crypt 00037 #endif 00038 00039 00040 // forward included macros to KOpenSSLProxy 00041 #define sk_new kossl->sk_new 00042 #define sk_free kossl->sk_free 00043 #define sk_push kossl->sk_push 00044 #define sk_value kossl->sk_value 00045 #define sk_num kossl->sk_num 00046 #define BIO_ctrl kossl->BIO_ctrl 00047 00048 00049 #ifdef KSSL_HAVE_SSL 00050 static const char eot = 0; 00051 00052 class KSMIMECryptoPrivate { 00053 KOpenSSLProxy *kossl; 00054 00055 public: 00056 KSMIMECryptoPrivate(KOpenSSLProxy *kossl); 00057 00058 00059 STACK_OF(X509) *certsToX509(const QList<KSSLCertificate *> &certs); 00060 00061 KSMIMECrypto::rc signMessage(BIO *clearText, 00062 BIO *cipherText, 00063 KSSLPKCS12 &privKey, QList<KSSLCertificate *> &certs, 00064 bool detached); 00065 00066 KSMIMECrypto::rc encryptMessage(BIO *clearText, 00067 BIO *cipherText, KSMIMECrypto::algo algorithm, 00068 QList<KSSLCertificate *> &recip); 00069 00070 KSMIMECrypto::rc checkSignature(BIO *clearText, 00071 BIO *signature, bool detached, 00072 QList<KSSLCertificate *> &recip); 00073 00074 KSMIMECrypto::rc decryptMessage(BIO *cipherText, 00075 BIO *clearText, 00076 KSSLPKCS12 &privKey); 00077 00078 void MemBIOToQByteArray(BIO *src, QByteArray &dest); 00079 00080 KSMIMECrypto::rc sslErrToRc(void); 00081 }; 00082 00083 00084 KSMIMECryptoPrivate::KSMIMECryptoPrivate(KOpenSSLProxy *kossl): kossl(kossl) { 00085 } 00086 00087 00088 STACK_OF(X509) *KSMIMECryptoPrivate::certsToX509(const QList<KSSLCertificate *> &certs) { 00089 STACK_OF(X509) *x509 = sk_new(NULL); 00090 foreach(KSSLCertificate *cert, certs) { 00091 sk_X509_push(x509, cert->getCert()); 00092 } 00093 return x509; 00094 } 00095 00096 00097 KSMIMECrypto::rc KSMIMECryptoPrivate::signMessage(BIO *clearText, 00098 BIO *cipherText, 00099 KSSLPKCS12 &privKey, QList<KSSLCertificate *> &certs, 00100 bool detached) { 00101 00102 STACK_OF(X509) *other = NULL; 00103 KSMIMECrypto::rc rc; 00104 int flags = detached?PKCS7_DETACHED:0; 00105 00106 if (certs.count()) other = certsToX509(certs); 00107 00108 PKCS7 *p7 = kossl->PKCS7_sign(privKey.getCertificate()->getCert(), privKey.getPrivateKey(), 00109 other, clearText, flags); 00110 00111 if (other) sk_X509_free(other); 00112 00113 if (!p7) return sslErrToRc(); 00114 00115 if (kossl->i2d_PKCS7_bio(cipherText, p7)) { 00116 rc = KSMIMECrypto::KSC_R_OK; 00117 } else { 00118 rc = sslErrToRc(); 00119 } 00120 00121 kossl->PKCS7_free(p7); 00122 00123 return rc; 00124 } 00125 00126 KSMIMECrypto::rc KSMIMECryptoPrivate::encryptMessage(BIO *clearText, 00127 BIO *cipherText, KSMIMECrypto::algo algorithm, 00128 QList<KSSLCertificate *> &recip) { 00129 EVP_CIPHER *cipher = NULL; 00130 KSMIMECrypto::rc rc; 00131 switch(algorithm) { 00132 case KSMIMECrypto::KSC_C_DES3_CBC: 00133 cipher = kossl->EVP_des_ede3_cbc(); 00134 break; 00135 case KSMIMECrypto::KSC_C_RC2_CBC_128: 00136 cipher = kossl->EVP_rc2_cbc(); 00137 break; 00138 case KSMIMECrypto::KSC_C_RC2_CBC_64: 00139 cipher = kossl->EVP_rc2_64_cbc(); 00140 break; 00141 case KSMIMECrypto::KSC_C_DES_CBC: 00142 cipher = kossl->EVP_des_cbc(); 00143 break; 00144 case KSMIMECrypto::KSC_C_RC2_CBC_40: 00145 cipher = kossl->EVP_rc2_40_cbc(); 00146 break; 00147 } 00148 if (!cipher) return KSMIMECrypto::KSC_R_NOCIPHER; 00149 00150 STACK_OF(X509) *certs = certsToX509(recip); 00151 00152 PKCS7 *p7 = kossl->PKCS7_encrypt(certs, clearText, cipher, 0); 00153 00154 sk_X509_free(certs); 00155 00156 if (!p7) return sslErrToRc(); 00157 00158 if (kossl->i2d_PKCS7_bio(cipherText, p7)) { 00159 rc = KSMIMECrypto::KSC_R_OK; 00160 } else { 00161 rc = sslErrToRc(); 00162 } 00163 00164 kossl->PKCS7_free(p7); 00165 00166 return rc; 00167 } 00168 00169 00170 KSMIMECrypto::rc KSMIMECryptoPrivate::checkSignature(BIO *clearText, 00171 BIO *signature, bool detached, 00172 QList<KSSLCertificate *> &recip) { 00173 00174 PKCS7 *p7 = kossl->d2i_PKCS7_bio(signature, NULL); 00175 KSMIMECrypto::rc rc = KSMIMECrypto::KSC_R_OTHER; 00176 00177 if (!p7) return sslErrToRc(); 00178 00179 BIO *in; 00180 BIO *out; 00181 if (detached) { 00182 in = clearText; 00183 out = NULL; 00184 } else { 00185 in = NULL; 00186 out = clearText; 00187 } 00188 00189 X509_STORE *dummystore = kossl->X509_STORE_new(); 00190 if (kossl->PKCS7_verify(p7, NULL, dummystore, in, out, PKCS7_NOVERIFY)) { 00191 STACK_OF(X509) *signers = kossl->PKCS7_get0_signers(p7, 0, PKCS7_NOVERIFY); 00192 int num = sk_X509_num(signers); 00193 00194 for(int n=0; n<num; n++) { 00195 KSSLCertificate *signer = KSSLCertificate::fromX509(sk_X509_value(signers, n)); 00196 recip.append(signer); 00197 } 00198 00199 sk_X509_free(signers); 00200 rc = KSMIMECrypto::KSC_R_OK; 00201 } else { 00202 rc = sslErrToRc(); 00203 } 00204 00205 kossl->X509_STORE_free(dummystore); 00206 kossl->PKCS7_free(p7); 00207 00208 return rc; 00209 } 00210 00211 00212 KSMIMECrypto::rc KSMIMECryptoPrivate::decryptMessage(BIO *cipherText, 00213 BIO *clearText, 00214 KSSLPKCS12 &privKey) { 00215 00216 PKCS7 *p7 = kossl->d2i_PKCS7_bio(cipherText, NULL); 00217 KSMIMECrypto::rc rc; 00218 00219 if (!p7) return sslErrToRc(); 00220 00221 if (kossl->PKCS7_decrypt(p7, privKey.getPrivateKey(), privKey.getCertificate()->getCert(), 00222 clearText, 0)) { 00223 rc = KSMIMECrypto::KSC_R_OK; 00224 } else { 00225 rc = sslErrToRc(); 00226 } 00227 00228 kossl->PKCS7_free(p7); 00229 00230 return rc; 00231 } 00232 00233 00234 void KSMIMECryptoPrivate::MemBIOToQByteArray(BIO *src, QByteArray &dest) { 00235 char *buf; 00236 long len = BIO_get_mem_data(src, &buf); 00237 dest = QByteArray(buf, len); 00238 /* Now this goes quite a bit into openssl internals. 00239 We assume that openssl uses malloc() (it does in 00240 default config) and rip out the buffer. 00241 */ 00242 reinterpret_cast<BUF_MEM *>(src->ptr)->data = NULL; 00243 } 00244 00245 00246 KSMIMECrypto::rc KSMIMECryptoPrivate::sslErrToRc(void) { 00247 unsigned long cerr = kossl->ERR_get_error(); 00248 00249 // To be completed and possibly fixed 00250 00251 switch(ERR_GET_REASON(cerr)) { 00252 case ERR_R_MALLOC_FAILURE: 00253 return KSMIMECrypto::KSC_R_NOMEM; 00254 } 00255 00256 switch(ERR_GET_LIB(cerr)) { 00257 case ERR_LIB_PKCS7: 00258 switch(ERR_GET_REASON(cerr)) { 00259 case PKCS7_R_WRONG_CONTENT_TYPE: 00260 case PKCS7_R_NO_CONTENT: 00261 case PKCS7_R_NO_SIGNATURES_ON_DATA: 00262 return KSMIMECrypto::KSC_R_FORMAT; 00263 break; 00264 case PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE: 00265 case PKCS7_R_DECRYPT_ERROR: // Hmm? 00266 return KSMIMECrypto::KSC_R_WRONGKEY; 00267 break; 00268 case PKCS7_R_DIGEST_FAILURE: 00269 return KSMIMECrypto::KSC_R_VERIFY; 00270 default: 00271 break; 00272 } 00273 break; 00274 default: 00275 break; 00276 } 00277 00278 kDebug(7029) <<"KSMIMECrypto: uncaught error " <<ERR_GET_LIB(cerr) 00279 <<" " <<ERR_GET_REASON(cerr) <<endl; 00280 return KSMIMECrypto::KSC_R_OTHER; 00281 } 00282 #endif 00283 00284 00285 KSMIMECrypto::KSMIMECrypto() { 00286 #ifdef KSSL_HAVE_SSL 00287 kossl = KOpenSSLProxy::self(); 00288 priv = new KSMIMECryptoPrivate(kossl); 00289 if (!kossl->hasLibCrypto()) kossl = 0L; 00290 #else 00291 kossl = 0L; 00292 priv = 0L; 00293 #endif 00294 } 00295 00296 00297 KSMIMECrypto::~KSMIMECrypto() { 00298 #ifdef KSSL_HAVE_SSL 00299 delete priv; 00300 #endif 00301 } 00302 00303 00304 KSMIMECrypto::rc KSMIMECrypto::signMessage(const QByteArray &clearText, 00305 QByteArray &cipherText, 00306 const KSSLPKCS12 &privKey, 00307 const QList<KSSLCertificate *> &certs, 00308 bool detached) { 00309 #ifdef KSSL_HAVE_SSL 00310 if (!kossl) return KSC_R_NO_SSL; 00311 BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size()); 00312 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00313 00314 rc rc = priv->signMessage(in, out, 00315 const_cast<KSSLPKCS12 &>(privKey), 00316 const_cast<QList<KSSLCertificate *> &>(certs), 00317 detached); 00318 00319 if (!rc) priv->MemBIOToQByteArray(out, cipherText); 00320 00321 kossl->BIO_free(out); 00322 kossl->BIO_free(in); 00323 00324 return rc; 00325 #else 00326 return KSC_R_NO_SSL; 00327 #endif 00328 } 00329 00330 00331 KSMIMECrypto::rc KSMIMECrypto::checkDetachedSignature(const QByteArray &clearText, 00332 const QByteArray &signature, 00333 QList<KSSLCertificate *> &foundCerts) { 00334 #ifdef KSSL_HAVE_SSL 00335 if (!kossl) return KSC_R_NO_SSL; 00336 BIO *txt = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.length()); 00337 BIO *sig = kossl->BIO_new_mem_buf((char *)signature.data(), signature.size()); 00338 00339 rc rc = priv->checkSignature(txt, sig, true, foundCerts); 00340 00341 kossl->BIO_free(sig); 00342 kossl->BIO_free(txt); 00343 00344 return rc; 00345 #else 00346 return KSC_R_NO_SSL; 00347 #endif 00348 } 00349 00350 00351 KSMIMECrypto::rc KSMIMECrypto::checkOpaqueSignature(const QByteArray &signedText, 00352 QByteArray &clearText, 00353 QList<KSSLCertificate *> &foundCerts) { 00354 #ifdef KSSL_HAVE_SSL 00355 if (!kossl) return KSC_R_NO_SSL; 00356 00357 BIO *in = kossl->BIO_new_mem_buf((char *)signedText.data(), signedText.size()); 00358 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00359 00360 rc rc = priv->checkSignature(out, in, false, foundCerts); 00361 00362 kossl->BIO_write(out, &eot, 1); 00363 priv->MemBIOToQByteArray(out, clearText); 00364 00365 kossl->BIO_free(out); 00366 kossl->BIO_free(in); 00367 00368 return rc; 00369 #else 00370 return KSC_R_NO_SSL; 00371 #endif 00372 } 00373 00374 00375 KSMIMECrypto::rc KSMIMECrypto::encryptMessage(const QByteArray &clearText, 00376 QByteArray &cipherText, 00377 algo algorithm, 00378 const QList<KSSLCertificate *> &recip) { 00379 #ifdef KSSL_HAVE_SSL 00380 if (!kossl) return KSC_R_NO_SSL; 00381 00382 BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size()); 00383 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00384 00385 rc rc = priv->encryptMessage(in,out,algorithm, 00386 const_cast< QList<KSSLCertificate *> &>(recip)); 00387 00388 if (!rc) priv->MemBIOToQByteArray(out, cipherText); 00389 00390 kossl->BIO_free(out); 00391 kossl->BIO_free(in); 00392 00393 return rc; 00394 #else 00395 return KSC_R_NO_SSL; 00396 #endif 00397 } 00398 00399 00400 KSMIMECrypto::rc KSMIMECrypto::decryptMessage(const QByteArray &cipherText, 00401 QByteArray &clearText, 00402 const KSSLPKCS12 &privKey) { 00403 #ifdef KSSL_HAVE_SSL 00404 if (!kossl) return KSC_R_NO_SSL; 00405 00406 BIO *in = kossl->BIO_new_mem_buf((char *)cipherText.data(), cipherText.size()); 00407 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00408 00409 rc rc = priv->decryptMessage(in,out, 00410 const_cast<KSSLPKCS12 &>(privKey)); 00411 00412 kossl->BIO_write(out, &eot, 1); 00413 priv->MemBIOToQByteArray(out, clearText); 00414 00415 kossl->BIO_free(out); 00416 kossl->BIO_free(in); 00417 00418 return rc; 00419 #else 00420 return KSC_R_NO_SSL; 00421 #endif 00422 }
KDE 4.6 API Reference