diff options
Diffstat (limited to 'src/security/cert/openssl/X509Certificate_OpenSSL.cpp')
-rwxr-xr-x | src/security/cert/openssl/X509Certificate_OpenSSL.cpp | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/src/security/cert/openssl/X509Certificate_OpenSSL.cpp b/src/security/cert/openssl/X509Certificate_OpenSSL.cpp new file mode 100755 index 00000000..dacb006e --- /dev/null +++ b/src/security/cert/openssl/X509Certificate_OpenSSL.cpp @@ -0,0 +1,467 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard <[email protected]> +// +// 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 <cstdio> +#include <ctime> +#include <map> +#include <algorithm> + +#include "vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp" + +#include "vmime/utility/outputStreamByteArrayAdapter.hpp" + +#include "vmime/exception.hpp" + +#include <openssl/x509.h> +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/err.h> + + +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 <vmime::string, vmime::datetime::Months>::const_iterator + c_it = m_monthMap.find(mstr); + + if (c_it != m_monthMap.end()) + return c_it->second; + + return -1; + } + +private: + + std::map<vmime::string, vmime::datetime::Months> 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> X509Certificate_OpenSSL::importInternal(X509* cert) +{ + if (cert) + return vmime::create <X509Certificate_OpenSSL>(reinterpret_cast <X509 *>(cert)); + + return NULL; +} + + +// static +ref <X509Certificate> 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> X509Certificate::import + (const byte_t* data, const unsigned int length) +{ + ref <X509Certificate_OpenSSL> cert = vmime::create <X509Certificate_OpenSSL>(); + + BIO* membio = BIO_new_mem_buf(const_cast <byte_t*>(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 <utility::stream::value_type*>(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 <utility::stream::value_type*>(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 <const X509Certificate> cert_) const +{ + ref <const X509Certificate_OpenSSL> cert = + cert_.dynamicCast <const X509Certificate_OpenSSL>(); + + // 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 <const X509Certificate> caCert_) const +{ + ref <const X509Certificate_OpenSSL> caCert = + caCert_.dynamicCast <const X509Certificate_OpenSSL>(); + + + 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<ASN1_TIME*>(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 <const certificate> other) const +{ + ref <const X509Certificate_OpenSSL> otherX509 = + other.dynamicCast <const X509Certificate_OpenSSL>(); + + 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 + |