// // VMime library (http://www.vmime.org) // Copyright (C) 2002-2009 Vincent Richard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // Linking this library statically or dynamically with other modules is making // a combined work based on this library. Thus, the terms and conditions of // the GNU General Public License cover the whole combination. // #include "vmime/config.hpp" #if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL #include #include #include #include #include "vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp" #include "vmime/utility/outputStreamByteArrayAdapter.hpp" #include "vmime/exception.hpp" #include #include #include #include namespace vmime { namespace security { namespace cert { #ifndef VMIME_BUILDING_DOC class monthMap { public: monthMap() { m_monthMap["jan"] = vmime::datetime::JAN; m_monthMap["feb"] = vmime::datetime::FEB; m_monthMap["mar"] = vmime::datetime::MAR; m_monthMap["apr"] = vmime::datetime::APR; m_monthMap["may"] = vmime::datetime::MAY; m_monthMap["jun"] = vmime::datetime::JUN; m_monthMap["jul"] = vmime::datetime::JUL; m_monthMap["aug"] = vmime::datetime::AUG; m_monthMap["sep"] = vmime::datetime::SEP; m_monthMap["oct"] = vmime::datetime::OCT; m_monthMap["nov"] = vmime::datetime::NOV; m_monthMap["dec"] = vmime::datetime::DEC; } int getMonth(vmime::string mstr) { std::transform(mstr.begin(), mstr.end(), mstr.begin(), ::tolower); std::map ::const_iterator c_it = m_monthMap.find(mstr); if (c_it != m_monthMap.end()) return c_it->second; return -1; } private: std::map m_monthMap; }; static monthMap sg_monthMap; struct OpenSSLX509CertificateInternalData { OpenSSLX509CertificateInternalData() { cert = 0; } ~OpenSSLX509CertificateInternalData() { if (cert) X509_free(cert); } X509* cert; }; #endif // VMIME_BUILDING_DOC X509Certificate_OpenSSL::X509Certificate_OpenSSL() : m_data(new OpenSSLX509CertificateInternalData) { } X509Certificate_OpenSSL::X509Certificate_OpenSSL(X509* cert) : m_data(new OpenSSLX509CertificateInternalData) { m_data->cert = X509_dup(cert); } X509Certificate_OpenSSL::X509Certificate_OpenSSL(const X509Certificate_OpenSSL&) : X509Certificate(), m_data(NULL) { // Not used } X509Certificate_OpenSSL::~X509Certificate_OpenSSL() { delete m_data; } // static ref X509Certificate_OpenSSL::importInternal(X509* cert) { if (cert) return vmime::create (reinterpret_cast (cert)); return NULL; } // static ref X509Certificate::import(utility::inputStream& is) { byteArray bytes; utility::stream::value_type chunk[4096]; while (!is.eof()) { const int len = is.read(chunk, sizeof(chunk)); bytes.insert(bytes.end(), chunk, chunk + len); } return import(&bytes[0], bytes.size()); } // static ref X509Certificate::import (const byte_t* data, const unsigned int length) { ref cert = vmime::create (); BIO* membio = BIO_new_mem_buf(const_cast (data), length); if (!PEM_read_bio_X509(membio, &(cert->m_data->cert), 0, 0)) { BIO_vfree(membio); return NULL; } BIO_vfree(membio); return cert; } void X509Certificate_OpenSSL::write (utility::outputStream& os, const Format format) const { BIO* membio = 0; int dataSize = 0; unsigned char* out = 0; if (format == FORMAT_DER) { if ((dataSize = i2d_X509(m_data->cert, &out)) < 0) goto err; os.write(reinterpret_cast (out), dataSize); os.flush(); OPENSSL_free(out); } else if (format == FORMAT_PEM) { membio = BIO_new(BIO_s_mem()); BIO_set_close(membio, BIO_CLOSE); if (!PEM_write_bio_X509(membio, m_data->cert)) goto pem_err; dataSize = BIO_get_mem_data(membio, &out); os.write(reinterpret_cast (out), dataSize); os.flush(); BIO_vfree(membio); } else { throw vmime::exceptions::unsupported_certificate_type("Unknown cert type"); } return; // #### Early Return #### pem_err: { if (membio) BIO_vfree(membio); } err: { char errstr[256]; long ec = ERR_get_error(); ERR_error_string(ec, errstr); throw vmime::exceptions::certificate_exception( "OpenSSLX509Certificate_OpenSSL::write exception - " + string(errstr)); } } const byteArray X509Certificate_OpenSSL::getSerialNumber() const { ASN1_INTEGER *serial = X509_get_serialNumber(m_data->cert); BIGNUM *bnser = ASN1_INTEGER_to_BN(serial, NULL); int n = BN_num_bytes(bnser); byte_t* outbuf = new byte_t[n]; BN_bn2bin(bnser, outbuf); byteArray ser(outbuf, outbuf + n); delete [] outbuf; BN_free(bnser); return ser; } bool X509Certificate_OpenSSL::checkIssuer(ref cert_) const { ref cert = cert_.dynamicCast (); // Get issuer for this cert BIO *out; unsigned char *issuer; out = BIO_new(BIO_s_mem()); X509_NAME_print_ex(out, X509_get_issuer_name(m_data->cert), 0, XN_FLAG_RFC2253); int n = BIO_get_mem_data(out, &issuer); vmime::string thisIssuerName((char*)issuer, n); BIO_free(out); // Get subject of issuer unsigned char *subject; out = BIO_new(BIO_s_mem()); X509_NAME_print_ex(out, X509_get_subject_name(cert->m_data->cert), 0, XN_FLAG_RFC2253); n = BIO_get_mem_data(out, &subject); vmime::string subjOfIssuer((char*)subject, n); BIO_free(out); return subjOfIssuer == thisIssuerName; } bool X509Certificate_OpenSSL::verify(ref caCert_) const { ref caCert = caCert_.dynamicCast (); bool verified = false; bool error = true; X509_STORE *store = X509_STORE_new(); if (store) { X509_STORE_CTX *verifyCtx = X509_STORE_CTX_new(); if (verifyCtx) { if (X509_STORE_add_cert(store, caCert->m_data->cert)) { X509_STORE_CTX_init(verifyCtx, store, m_data->cert, NULL); int ret = X509_verify_cert(verifyCtx); if (ret == 1) { verified = true; error = false; } else if (ret == 0) { verified = false; error = false; } //X509_verify_cert_error_string(vrfy_ctx->error) X509_STORE_CTX_free(verifyCtx); } } X509_STORE_free(store); } return verified && !error; } const datetime X509Certificate_OpenSSL::convertX509Date(void* time) const { char* buffer; BIO* out = BIO_new(BIO_s_mem()); BIO_set_close(out, BIO_CLOSE); ASN1_TIME* asn1_time = reinterpret_cast(time); ASN1_TIME_print(out, asn1_time); int sz = BIO_get_mem_data(out, &buffer); char* dest = new char[sz + 1]; dest[sz] = 0; memcpy(dest, buffer, sz); vmime::string t(dest); BIO_free(out); delete dest; if (t.size() > 0) { char month[4] = {0}; char zone[4] = {0}; int day, hour, minute, second, year; int nrconv = sscanf(t.c_str(), "%s %2d %02d:%02d:%02d %d%s", month, &day, &hour, &minute, &second,&year,zone); if (nrconv >= 6) return datetime(year, sg_monthMap.getMonth(vmime::string(month)), day, hour, minute, second); } // let datetime try and parse it return datetime(t); } const datetime X509Certificate_OpenSSL::getActivationDate() const { return convertX509Date(X509_get_notBefore(m_data->cert)); } const datetime X509Certificate_OpenSSL::getExpirationDate() const { return convertX509Date(X509_get_notAfter(m_data->cert)); } const byteArray X509Certificate_OpenSSL::getFingerprint(const DigestAlgorithm algo) const { BIO *out; int j; unsigned int n; const EVP_MD *digest; unsigned char * fingerprint, *result; unsigned char md[EVP_MAX_MD_SIZE]; switch (algo) { case DIGEST_MD5: digest = EVP_md5(); break; default: case DIGEST_SHA1: digest = EVP_sha1(); break; } out = BIO_new(BIO_s_mem()); BIO_set_close(out, BIO_CLOSE); if (X509_digest(m_data->cert, digest, md, &n)) { for (j=0; j<(int)n; j++) { BIO_printf (out, "%02X",md[j]); if (j+1 != (int)n) BIO_printf(out, ":"); } } n = BIO_get_mem_data(out, &fingerprint); result = new unsigned char[n]; memcpy (result, fingerprint, n); BIO_free(out); byteArray res; res.insert(res.end(), &result[0], &result[0] + n); delete [] result; return res; } const byteArray X509Certificate_OpenSSL::getEncoded() const { byteArray bytes; utility::outputStreamByteArrayAdapter os(bytes); write(os, FORMAT_DER); return bytes; } const string X509Certificate_OpenSSL::getType() const { return "X.509"; } int X509Certificate_OpenSSL::getVersion() const { return (int)X509_get_version(m_data->cert); } bool X509Certificate_OpenSSL::equals(ref other) const { ref otherX509 = other.dynamicCast (); if (!otherX509) return false; const byteArray fp1 = getFingerprint(DIGEST_MD5); const byteArray fp2 = otherX509->getFingerprint(DIGEST_MD5); return fp1 == fp2; } } // cert } // security } // vmime #endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL