diff options
Diffstat (limited to 'vmime-master/src/vmime/security/cert/openssl')
| -rw-r--r-- | vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.cpp | 655 | ||||
| -rw-r--r-- | vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp | 119 | 
2 files changed, 774 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 + diff --git a/vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp b/vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp new file mode 100644 index 0000000..c0ebf3c --- /dev/null +++ b/vmime-master/src/vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp @@ -0,0 +1,119 @@ +// +// 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. +// + +#ifndef VMIME_SECURITY_CERT_X509CERTIFICATE_OPENSSL_HPP_INCLUDED +#define VMIME_SECURITY_CERT_X509CERTIFICATE_OPENSSL_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/security/cert/X509Certificate.hpp" + +#include <openssl/x509.h> + + +namespace vmime { +namespace security { +namespace cert { + + +class X509Certificate_OpenSSL : public X509Certificate { + +	friend class X509Certificate; + +	X509Certificate_OpenSSL(const X509Certificate_OpenSSL&); + +public: + +	X509Certificate_OpenSSL(); +	X509Certificate_OpenSSL(X509* cert); + +	~X509Certificate_OpenSSL(); + + +	void write(utility::outputStream& os, const Format format) const; + +	const byteArray getSerialNumber() const; + +	const string getIssuerString() const; +	bool checkIssuer(const shared_ptr <const X509Certificate>& issuer) const; + +	bool verify(const shared_ptr <const X509Certificate>& caCert) const; + +	bool verifyHostName( +		const string& hostname, +		std::vector <std::string>* nonMatchingNames = NULL +	) const; + +	const datetime getExpirationDate() const; +	const datetime getActivationDate() const; + +	const byteArray getFingerprint(const DigestAlgorithm algo) const; + + +	static shared_ptr <X509Certificate> importInternal(X509* cert); + + +	// Implementation of 'certificate' +	const byteArray getEncoded() const; +	const string getType() const; +	int getVersion() const; +	bool equals(const shared_ptr <const certificate>& other) const; +	void* getInternalData(); + +private: + +	/** Internal utility function to test whether a hostname matches +	  * the specified X509 Common Name (wildcard is supported). +	  * +	  * @param cnBuf pointer to buffer holding Common Name +	  * @param host pointer to buffer holding host name +	  * @return true if the hostname matches the Common Name, or +	  * false otherwise +	  */ +	static bool cnMatch(const char* cnBuf, const char* host); + +	/** Internal utility function to convert ASN1_TIME +	 *  structs to vmime::datetime +	 * +	 *  @param pointer to ASN1_TIME struct to convert +	 */ +	const datetime convertX509Date(void* time) const; + +	struct OpenSSLX509CertificateInternalData* m_data; +}; + + +} // cert +} // security +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_SECURITY_CERT_X509CERTIFICATE_OPENSSL_HPP_INCLUDED + | 
