ksmimecrypto.cc
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 00022 #include <tqptrlist.h> 00023 #include <tqcstring.h> 00024 #include <tqstring.h> 00025 #include <kdebug.h> 00026 00027 #include "kopenssl.h" 00028 #include "ksslcertificate.h" 00029 #include "ksslpkcs12.h" 00030 #include "ksmimecrypto.h" 00031 00032 // this hack provided by Malte Starostik to avoid glibc/openssl bug 00033 // on some systems 00034 #ifdef KSSL_HAVE_SSL 00035 #define crypt _openssl_crypt 00036 #include <openssl/err.h> 00037 #undef crypt 00038 #endif 00039 00040 00041 #ifdef KSSL_HAVE_SSL 00042 static const char eot = 0; 00043 00044 class KSMIMECryptoPrivate { 00045 KOpenSSLProxy *kossl; 00046 00047 public: 00048 KSMIMECryptoPrivate(KOpenSSLProxy *kossl); 00049 00050 00051 STACK_OF(X509) *certsToX509(TQPtrList<KSSLCertificate> &certs); 00052 00053 KSMIMECrypto::rc signMessage(BIO *clearText, 00054 BIO *cipherText, 00055 KSSLPKCS12 &privKey, TQPtrList<KSSLCertificate> &certs, 00056 bool detached); 00057 00058 KSMIMECrypto::rc encryptMessage(BIO *clearText, 00059 BIO *cipherText, KSMIMECrypto::algo algorithm, 00060 TQPtrList<KSSLCertificate> &recip); 00061 00062 KSMIMECrypto::rc checkSignature(BIO *clearText, 00063 BIO *signature, bool detached, 00064 TQPtrList<KSSLCertificate> &recip); 00065 00066 KSMIMECrypto::rc decryptMessage(BIO *cipherText, 00067 BIO *clearText, 00068 KSSLPKCS12 &privKey); 00069 00070 void MemBIOToQByteArray(BIO *src, TQByteArray &dest); 00071 00072 KSMIMECrypto::rc sslErrToRc(void); 00073 }; 00074 00075 00076 KSMIMECryptoPrivate::KSMIMECryptoPrivate(KOpenSSLProxy *kossl): kossl(kossl) { 00077 } 00078 00079 00080 STACK_OF(X509) *KSMIMECryptoPrivate::certsToX509(TQPtrList<KSSLCertificate> &certs) { 00081 STACK_OF(X509) *x509 = reinterpret_cast<STACK_OF(X509)*>(kossl->OPENSSL_sk_new(NULL)); 00082 KSSLCertificate *cert = certs.first(); 00083 while(cert) { 00084 kossl->OPENSSL_sk_push(x509, cert->getCert()); 00085 cert = certs.next(); 00086 } 00087 return x509; 00088 } 00089 00090 00091 KSMIMECrypto::rc KSMIMECryptoPrivate::signMessage(BIO *clearText, 00092 BIO *cipherText, 00093 KSSLPKCS12 &privKey, TQPtrList<KSSLCertificate> &certs, 00094 bool detached) { 00095 00096 STACK_OF(X509) *other = NULL; 00097 KSMIMECrypto::rc rc; 00098 int flags = detached?PKCS7_DETACHED:0; 00099 00100 if (certs.count()) other = certsToX509(certs); 00101 00102 PKCS7 *p7 = kossl->PKCS7_sign(privKey.getCertificate()->getCert(), privKey.getPrivateKey(), 00103 other, clearText, flags); 00104 00105 if (other) kossl->OPENSSL_sk_free(other); 00106 00107 if (!p7) return sslErrToRc(); 00108 00109 if (kossl->i2d_PKCS7_bio(cipherText, p7)) { 00110 rc = KSMIMECrypto::KSC_R_OK; 00111 } else { 00112 rc = sslErrToRc(); 00113 } 00114 00115 kossl->PKCS7_free(p7); 00116 00117 return rc; 00118 } 00119 00120 KSMIMECrypto::rc KSMIMECryptoPrivate::encryptMessage(BIO *clearText, 00121 BIO *cipherText, KSMIMECrypto::algo algorithm, 00122 TQPtrList<KSSLCertificate> &recip) { 00123 EVP_CIPHER *cipher = NULL; 00124 KSMIMECrypto::rc rc; 00125 switch(algorithm) { 00126 case KSMIMECrypto::KSC_C_DES3_CBC: 00127 cipher = kossl->EVP_des_ede3_cbc(); 00128 break; 00129 case KSMIMECrypto::KSC_C_RC2_CBC_128: 00130 cipher = kossl->EVP_rc2_cbc(); 00131 break; 00132 case KSMIMECrypto::KSC_C_RC2_CBC_64: 00133 cipher = kossl->EVP_rc2_64_cbc(); 00134 break; 00135 case KSMIMECrypto::KSC_C_DES_CBC: 00136 cipher = kossl->EVP_des_cbc(); 00137 break; 00138 case KSMIMECrypto::KSC_C_RC2_CBC_40: 00139 cipher = kossl->EVP_rc2_40_cbc(); 00140 break; 00141 } 00142 if (!cipher) return KSMIMECrypto::KSC_R_NOCIPHER; 00143 00144 STACK_OF(X509) *certs = certsToX509(recip); 00145 00146 PKCS7 *p7 = kossl->PKCS7_encrypt(certs, clearText, cipher, 0); 00147 00148 kossl->OPENSSL_sk_free(certs); 00149 00150 if (!p7) return sslErrToRc(); 00151 00152 if (kossl->i2d_PKCS7_bio(cipherText, p7)) { 00153 rc = KSMIMECrypto::KSC_R_OK; 00154 } else { 00155 rc = sslErrToRc(); 00156 } 00157 00158 kossl->PKCS7_free(p7); 00159 00160 return rc; 00161 } 00162 00163 00164 KSMIMECrypto::rc KSMIMECryptoPrivate::checkSignature(BIO *clearText, 00165 BIO *signature, bool detached, 00166 TQPtrList<KSSLCertificate> &recip) { 00167 00168 PKCS7 *p7 = kossl->d2i_PKCS7_bio(signature, NULL); 00169 KSMIMECrypto::rc rc = KSMIMECrypto::KSC_R_OTHER; 00170 00171 if (!p7) return sslErrToRc(); 00172 00173 BIO *in; 00174 BIO *out; 00175 if (detached) { 00176 in = clearText; 00177 out = NULL; 00178 } else { 00179 in = NULL; 00180 out = clearText; 00181 } 00182 00183 X509_STORE *dummystore = kossl->X509_STORE_new(); 00184 if (kossl->PKCS7_verify(p7, NULL, dummystore, in, out, PKCS7_NOVERIFY)) { 00185 STACK_OF(X509) *signers = kossl->PKCS7_get0_signers(p7, 0, PKCS7_NOVERIFY); 00186 int num = kossl->OPENSSL_sk_num(signers); 00187 00188 for(int n=0; n<num; n++) { 00189 KSSLCertificate *signer = KSSLCertificate::fromX509(reinterpret_cast<X509*>(kossl->OPENSSL_sk_value(signers, n))); 00190 recip.append(signer); 00191 } 00192 00193 kossl->OPENSSL_sk_free(signers); 00194 rc = KSMIMECrypto::KSC_R_OK; 00195 } else { 00196 rc = sslErrToRc(); 00197 } 00198 00199 kossl->X509_STORE_free(dummystore); 00200 kossl->PKCS7_free(p7); 00201 00202 return rc; 00203 } 00204 00205 00206 KSMIMECrypto::rc KSMIMECryptoPrivate::decryptMessage(BIO *cipherText, 00207 BIO *clearText, 00208 KSSLPKCS12 &privKey) { 00209 00210 PKCS7 *p7 = kossl->d2i_PKCS7_bio(cipherText, NULL); 00211 KSMIMECrypto::rc rc; 00212 00213 if (!p7) return sslErrToRc(); 00214 00215 if (kossl->PKCS7_decrypt(p7, privKey.getPrivateKey(), privKey.getCertificate()->getCert(), 00216 clearText, 0)) { 00217 rc = KSMIMECrypto::KSC_R_OK; 00218 } else { 00219 rc = sslErrToRc(); 00220 } 00221 00222 kossl->PKCS7_free(p7); 00223 00224 return rc; 00225 } 00226 00227 00228 void KSMIMECryptoPrivate::MemBIOToQByteArray(BIO *src, TQByteArray &dest) { 00229 char *buf; 00230 long len = kossl->BIO_get_mem_data(src, &buf); 00231 dest.assign(buf, len); 00232 /* Now this goes quite a bit into openssl internals. 00233 We assume that openssl uses malloc() (it does in 00234 default config) and rip out the buffer. 00235 */ 00236 void *ptr = kossl->BIO_get_data(src); 00237 reinterpret_cast<BUF_MEM *>(ptr)->data = NULL; 00238 } 00239 00240 00241 KSMIMECrypto::rc KSMIMECryptoPrivate::sslErrToRc(void) { 00242 unsigned long cerr = kossl->ERR_get_error(); 00243 00244 // To be completed and possibly fixed 00245 00246 switch(ERR_GET_REASON(cerr)) { 00247 case ERR_R_MALLOC_FAILURE: 00248 return KSMIMECrypto::KSC_R_NOMEM; 00249 } 00250 00251 switch(ERR_GET_LIB(cerr)) { 00252 case ERR_LIB_PKCS7: 00253 switch(ERR_GET_REASON(cerr)) { 00254 case PKCS7_R_WRONG_CONTENT_TYPE: 00255 case PKCS7_R_NO_CONTENT: 00256 case PKCS7_R_NO_SIGNATURES_ON_DATA: 00257 return KSMIMECrypto::KSC_R_FORMAT; 00258 break; 00259 case PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE: 00260 case PKCS7_R_DECRYPT_ERROR: // Hmm? 00261 return KSMIMECrypto::KSC_R_WRONGKEY; 00262 break; 00263 case PKCS7_R_DIGEST_FAILURE: 00264 return KSMIMECrypto::KSC_R_VERIFY; 00265 default: 00266 break; 00267 } 00268 break; 00269 default: 00270 break; 00271 } 00272 00273 kdDebug(7029) <<"KSMIMECrypto: uncaught error " <<ERR_GET_LIB(cerr) 00274 <<" " <<ERR_GET_REASON(cerr) <<endl; 00275 return KSMIMECrypto::KSC_R_OTHER; 00276 } 00277 #endif 00278 00279 00280 KSMIMECrypto::KSMIMECrypto() { 00281 #ifdef KSSL_HAVE_SSL 00282 kossl = KOpenSSLProxy::self(); 00283 priv = new KSMIMECryptoPrivate(kossl); 00284 if (!kossl->hasLibCrypto()) kossl = 0L; 00285 #else 00286 kossl = 0L; 00287 #endif 00288 } 00289 00290 00291 KSMIMECrypto::~KSMIMECrypto() { 00292 #ifdef KSSL_HAVE_SSL 00293 delete priv; 00294 #endif 00295 } 00296 00297 00298 KSMIMECrypto::rc KSMIMECrypto::signMessage(const TQCString &clearText, 00299 TQByteArray &cipherText, 00300 const KSSLPKCS12 &privKey, 00301 const TQPtrList<KSSLCertificate> &certs, 00302 bool detached) { 00303 #ifdef KSSL_HAVE_SSL 00304 if (!kossl) return KSC_R_NO_SSL; 00305 BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size()); 00306 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00307 00308 rc rc = priv->signMessage(in, out, 00309 const_cast<KSSLPKCS12 &>(privKey), 00310 const_cast<TQPtrList<KSSLCertificate> &>(certs), 00311 detached); 00312 00313 if (!rc) priv->MemBIOToQByteArray(out, cipherText); 00314 00315 kossl->BIO_free(out); 00316 kossl->BIO_free(in); 00317 00318 return rc; 00319 #else 00320 return KSC_R_NO_SSL; 00321 #endif 00322 } 00323 00324 00325 KSMIMECrypto::rc KSMIMECrypto::checkDetachedSignature(const TQCString &clearText, 00326 const TQByteArray &signature, 00327 TQPtrList<KSSLCertificate> &foundCerts) { 00328 #ifdef KSSL_HAVE_SSL 00329 if (!kossl) return KSC_R_NO_SSL; 00330 BIO *txt = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.length()); 00331 BIO *sig = kossl->BIO_new_mem_buf((char *)signature.data(), signature.size()); 00332 00333 rc rc = priv->checkSignature(txt, sig, true, foundCerts); 00334 00335 kossl->BIO_free(sig); 00336 kossl->BIO_free(txt); 00337 00338 return rc; 00339 #else 00340 return KSC_R_NO_SSL; 00341 #endif 00342 } 00343 00344 00345 KSMIMECrypto::rc KSMIMECrypto::checkOpaqueSignature(const TQByteArray &signedText, 00346 TQCString &clearText, 00347 TQPtrList<KSSLCertificate> &foundCerts) { 00348 #ifdef KSSL_HAVE_SSL 00349 if (!kossl) return KSC_R_NO_SSL; 00350 00351 BIO *in = kossl->BIO_new_mem_buf((char *)signedText.data(), signedText.size()); 00352 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00353 00354 rc rc = priv->checkSignature(out, in, false, foundCerts); 00355 00356 kossl->BIO_write(out, &eot, 1); 00357 priv->MemBIOToQByteArray(out, clearText); 00358 00359 kossl->BIO_free(out); 00360 kossl->BIO_free(in); 00361 00362 return rc; 00363 #else 00364 return KSC_R_NO_SSL; 00365 #endif 00366 } 00367 00368 00369 KSMIMECrypto::rc KSMIMECrypto::encryptMessage(const TQCString &clearText, 00370 TQByteArray &cipherText, 00371 algo algorithm, 00372 const TQPtrList<KSSLCertificate> &recip) { 00373 #ifdef KSSL_HAVE_SSL 00374 if (!kossl) return KSC_R_NO_SSL; 00375 00376 BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size()); 00377 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00378 00379 rc rc = priv->encryptMessage(in,out,algorithm, 00380 const_cast< TQPtrList<KSSLCertificate> &>(recip)); 00381 00382 if (!rc) priv->MemBIOToQByteArray(out, cipherText); 00383 00384 kossl->BIO_free(out); 00385 kossl->BIO_free(in); 00386 00387 return rc; 00388 #else 00389 return KSC_R_NO_SSL; 00390 #endif 00391 } 00392 00393 00394 KSMIMECrypto::rc KSMIMECrypto::decryptMessage(const TQByteArray &cipherText, 00395 TQCString &clearText, 00396 const KSSLPKCS12 &privKey) { 00397 #ifdef KSSL_HAVE_SSL 00398 if (!kossl) return KSC_R_NO_SSL; 00399 00400 BIO *in = kossl->BIO_new_mem_buf((char *)cipherText.data(), cipherText.size()); 00401 BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); 00402 00403 rc rc = priv->decryptMessage(in,out, 00404 const_cast<KSSLPKCS12 &>(privKey)); 00405 00406 kossl->BIO_write(out, &eot, 1); 00407 priv->MemBIOToQByteArray(out, clearText); 00408 00409 kossl->BIO_free(out); 00410 kossl->BIO_free(in); 00411 00412 return rc; 00413 #else 00414 return KSC_R_NO_SSL; 00415 #endif 00416 } 00417