aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.cpp')
-rw-r--r--vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.cpp655
1 files changed, 655 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.cpp b/vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.cpp
new file mode 100644
index 0000000..5488801
--- /dev/null
+++ b/vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.cpp
@@ -0,0 +1,655 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002 Vincent Richard <vincent@vmime.org>
+//
+// 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/net/tls/openssl/OpenSSLInitializer.hpp"
+
+#include "vmime/utility/outputStreamByteArrayAdapter.hpp"
+
+#include "vmime/security/cert/certificateException.hpp"
+#include "vmime/security/cert/unsupportedCertificateTypeException.hpp"
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/conf.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+
+#ifdef _WIN32
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+#endif
+
+
+namespace vmime {
+namespace security {
+namespace cert {
+
+
+static net::tls::OpenSSLInitializer::autoInitializer openSSLInitializer;
+
+
+#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;
+};
+
+
+// Workaround for i2v() taking either a const or a non-const 'method' on some platforms
+STACK_OF(CONF_VALUE)* call_i2v(const X509V3_EXT_METHOD* m, void* p1, STACK_OF(CONF_VALUE)* p2) {
+
+ return m->i2v(m, p1, p2);
+}
+
+
+STACK_OF(CONF_VALUE)* call_i2v(X509V3_EXT_METHOD* m, void* p1, STACK_OF(CONF_VALUE)* p2) {
+
+ return m->i2v(m, p1, p2);
+}
+
+
+#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;
+}
+
+
+void* X509Certificate_OpenSSL::getInternalData() {
+
+ return &m_data->cert;
+}
+
+
+// static
+shared_ptr <X509Certificate> X509Certificate_OpenSSL::importInternal(X509* cert) {
+
+ if (cert) {
+ return make_shared <X509Certificate_OpenSSL>(reinterpret_cast <X509 *>(cert));
+ }
+
+ return null;
+}
+
+
+// static
+shared_ptr <X509Certificate> X509Certificate::import(utility::inputStream& is) {
+
+ byteArray bytes;
+ byte_t chunk[4096];
+
+ while (!is.eof()) {
+ const size_t len = is.read(chunk, sizeof(chunk));
+ bytes.insert(bytes.end(), chunk, chunk + len);
+ }
+
+ return import(&bytes[0], bytes.size());
+}
+
+
+// static
+shared_ptr <X509Certificate> X509Certificate::import(
+ const byte_t* data,
+ const size_t length
+) {
+
+ shared_ptr <X509Certificate_OpenSSL> cert = make_shared <X509Certificate_OpenSSL>();
+
+ BIO* membio = BIO_new_mem_buf(const_cast <byte_t*>(data), static_cast <int>(length));
+
+ if (!(cert->m_data->cert = d2i_X509_bio(membio, NULL))
+ /*!PEM_read_bio_X509(membio, &(cert->m_data->cert), 0, 0)*/) {
+ BIO_vfree(membio);
+ return null;
+ }
+
+ BIO_vfree(membio);
+
+ return cert;
+}
+
+
+// static
+void X509Certificate::import(
+ utility::inputStream& is,
+ std::vector <shared_ptr <X509Certificate> >& certs
+) {
+
+ byteArray bytes;
+ byte_t chunk[4096];
+
+ while (!is.eof()) {
+ const size_t len = is.read(chunk, sizeof(chunk));
+ bytes.insert(bytes.end(), chunk, chunk + len);
+ }
+
+ import(&bytes[0], bytes.size(), certs);
+}
+
+
+// static
+void X509Certificate::import(
+ const byte_t* data,
+ const size_t length,
+ std::vector <shared_ptr <X509Certificate> >& certs
+) {
+
+ BIO* membio = BIO_new_mem_buf(const_cast <byte_t*>(data), static_cast <int>(length));
+ shared_ptr <X509Certificate_OpenSSL> cert = null;
+
+ while (true) {
+
+ cert = make_shared <X509Certificate_OpenSSL>();
+
+ if (!PEM_read_bio_X509(membio, &(cert->m_data->cert), 0, 0)) {
+ break;
+ }
+
+ certs.push_back(cert);
+ }
+
+ BIO_vfree(membio);
+}
+
+
+void X509Certificate_OpenSSL::write(
+ utility::outputStream& os,
+ const Format format
+) const {
+
+ BIO* membio = 0;
+ long 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 <byte_t*>(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 <byte_t*>(out), dataSize);
+ os.flush();
+ BIO_vfree(membio);
+
+ } else {
+
+ throw unsupportedCertificateTypeException("Unknown format");
+ }
+
+ 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 certificateException("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(const shared_ptr <const X509Certificate>& cert_) const {
+
+ shared_ptr <const X509Certificate_OpenSSL> cert =
+ dynamicCast <const X509Certificate_OpenSSL>(cert_);
+
+ // 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);
+ long 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(const shared_ptr <const X509Certificate>& caCert_) const {
+
+ shared_ptr <const X509Certificate_OpenSSL> caCert =
+ dynamicCast <const X509Certificate_OpenSSL>(caCert_);
+
+// printf("ptr before cast is %p\n", caCert_.get());
+// printf("ptr is %p\n", caCert.get());
+
+ 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;
+}
+
+
+// static
+bool X509Certificate_OpenSSL::cnMatch(const char* cnBuf, const char* host) {
+
+ // Right-to-left match, looking for a '*' wildcard
+ const bool hasWildcard = (strlen(cnBuf) > 1 && cnBuf[0] == '*' && cnBuf[1] == '.');
+ const char* cnBufReverseEndPtr = (cnBuf + (hasWildcard ? 2 : 0));
+ const char* hostPtr = host + strlen(host);
+ const char* cnPtr = cnBuf + strlen(cnBuf);
+
+ bool matches = true;
+
+ while (matches && --hostPtr >= host && --cnPtr >= cnBufReverseEndPtr) {
+ matches = (toupper(*hostPtr) == toupper(*cnPtr));
+ }
+
+ return matches;
+}
+
+
+bool X509Certificate_OpenSSL::verifyHostName(
+ const string& hostname,
+ std::vector <std::string>* nonMatchingNames
+) const {
+
+ // First, check subject common name against hostname
+ char CNBuffer[1024];
+ CNBuffer[sizeof(CNBuffer) - 1] = '\0';
+
+ X509_NAME* xname = X509_get_subject_name(m_data->cert);
+
+ if (X509_NAME_get_text_by_NID(xname, NID_commonName, CNBuffer, sizeof(CNBuffer)) != -1) {
+
+ if (cnMatch(CNBuffer, hostname.c_str())) {
+ return true;
+ }
+
+ if (nonMatchingNames) {
+ nonMatchingNames->push_back(CNBuffer);
+ }
+ }
+
+ // Now, look in subject alternative names
+ bool verify = false;
+
+ STACK_OF(GENERAL_NAME)* altNames = static_cast <GENERAL_NAMES*>
+ (X509_get_ext_d2i(m_data->cert, NID_subject_alt_name, NULL, NULL));
+
+ if (altNames == NULL) {
+ return false;
+ }
+
+ // Check each name within the extension
+ for (int i = 0, n = sk_GENERAL_NAME_num(altNames) ; i < n ; ++i) {
+
+ const GENERAL_NAME* currentName = sk_GENERAL_NAME_value(altNames, i);
+
+ if (currentName->type == GEN_DNS) {
+
+ // Current name is a DNS name, let's check it
+ char *DNSName = (char *) ASN1_STRING_data(currentName->d.dNSName);
+
+ // Make sure there isn't an embedded NUL character in the DNS name
+ if (ASN1_STRING_length(currentName->d.dNSName) != strlen(DNSName)) {
+ // Malformed certificate
+ break;
+ }
+
+ if (cnMatch(DNSName, hostname.c_str())) {
+ verify = true;
+ break;
+ }
+
+ if (nonMatchingNames) {
+ nonMatchingNames->push_back(DNSName);
+ }
+ }
+ }
+
+ sk_GENERAL_NAME_pop_free(altNames, GENERAL_NAME_free);
+
+ return verify;
+}
+
+
+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);
+
+ const long 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;
+ 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, ":");
+ }
+ }
+
+ const long resultLen = BIO_get_mem_data(out, &fingerprint);
+ unsigned char* result = new unsigned char[resultLen];
+ memcpy(result, fingerprint, resultLen);
+ BIO_free(out);
+
+ byteArray res;
+ res.insert(res.end(), &result[0], &result[0] + resultLen);
+
+ 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::getIssuerString() const {
+
+ // Get issuer for this cert
+ BIO* out = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(out, X509_get_issuer_name(m_data->cert), 0, XN_FLAG_RFC2253);
+
+ unsigned char* issuer;
+ const long n = BIO_get_mem_data(out, &issuer);
+
+ vmime::string name(reinterpret_cast <char*>(issuer), n);
+ BIO_free(out);
+
+ return name;
+}
+
+
+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(const shared_ptr <const certificate>& other) const {
+
+ shared_ptr <const X509Certificate_OpenSSL> otherX509 =
+ dynamicCast <const X509Certificate_OpenSSL>(other);
+
+ 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
+