vmime/src/security/cert/openssl/X509Certificate_OpenSSL.cpp
2012-11-03 09:27:12 +01:00

468 lines
9.8 KiB
C++
Executable File

//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2009 Vincent Richard <vincent@vincent-richard.net>
//
// 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