aboutsummaryrefslogtreecommitdiffstats
path: root/src/security
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2005-10-30 16:02:39 +0100
committerVincent Richard <[email protected]>2005-10-30 16:02:39 +0100
commit4522121196f11de0200ed54ea50830c1baf017a6 (patch)
tree10dc792a93779ac0546291acf8064fd0efa138ed /src/security
parentAdded flush() on 'outputStream' + added unit tests for 'charsetFilteredOutput... (diff)
downloadvmime-4522121196f11de0200ed54ea50830c1baf017a6.tar.gz
vmime-4522121196f11de0200ed54ea50830c1baf017a6.zip
Moved certificate code into 'vmime::net::security::cert' namespace.
Diffstat (limited to 'src/security')
-rw-r--r--src/security/cert/X509Certificate.cpp273
-rw-r--r--src/security/cert/certificateChain.cpp53
-rw-r--r--src/security/cert/defaultCertificateVerifier.cpp164
3 files changed, 490 insertions, 0 deletions
diff --git a/src/security/cert/X509Certificate.cpp b/src/security/cert/X509Certificate.cpp
new file mode 100644
index 00000000..a1310f51
--- /dev/null
+++ b/src/security/cert/X509Certificate.cpp
@@ -0,0 +1,273 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 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 <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include <ctime>
+
+#include "vmime/security/cert/X509Certificate.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace cert {
+
+
+#ifndef VMIME_BUILDING_DOC
+
+struct X509CertificateInternalData
+{
+ X509CertificateInternalData()
+ {
+ gnutls_x509_crt_init(&cert);
+ }
+
+ ~X509CertificateInternalData()
+ {
+ gnutls_x509_crt_deinit(cert);
+ }
+
+
+ gnutls_x509_crt cert;
+};
+
+#endif // VMIME_BUILDING_DOC
+
+
+X509Certificate::X509Certificate()
+ : m_data(new X509CertificateInternalData)
+{
+}
+
+
+X509Certificate::X509Certificate(const X509Certificate&)
+ : certificate(), m_data(NULL)
+{
+ // Not used
+}
+
+
+X509Certificate::~X509Certificate()
+{
+ delete m_data;
+}
+
+
+// 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* data, const unsigned int length)
+{
+ ref <X509Certificate> cert = vmime::create <X509Certificate>();
+
+ gnutls_datum buffer;
+ buffer.data = const_cast <byte*>(data);
+ buffer.size = length;
+
+ // Try DER format
+ if (gnutls_x509_crt_import(cert->m_data->cert, &buffer, GNUTLS_X509_FMT_DER) >= 0)
+ return cert;
+
+ // Try PEM format
+ if (gnutls_x509_crt_import(cert->m_data->cert, &buffer, GNUTLS_X509_FMT_PEM) >= 0)
+ return cert;
+
+ return NULL;
+}
+
+
+void X509Certificate::write
+ (utility::outputStream& os, const Format format) const
+{
+ size_t dataSize = 0;
+ gnutls_x509_crt_fmt fmt = GNUTLS_X509_FMT_DER;
+
+ switch (format)
+ {
+ case FORMAT_DER: fmt = GNUTLS_X509_FMT_DER; break;
+ case FORMAT_PEM: fmt = GNUTLS_X509_FMT_PEM; break;
+ }
+
+ gnutls_x509_crt_export(m_data->cert, fmt, NULL, &dataSize);
+
+ byte* data = new byte[dataSize];
+
+ gnutls_x509_crt_export(m_data->cert, fmt, data, &dataSize);
+
+ try
+ {
+ os.write(reinterpret_cast <utility::stream::value_type*>(data), dataSize);
+ }
+ catch (...)
+ {
+ delete [] data;
+ throw;
+ }
+}
+
+
+const byteArray X509Certificate::getSerialNumber() const
+{
+ char serial[64];
+ size_t serialSize = sizeof(serial);
+
+ gnutls_x509_crt_get_serial(m_data->cert, serial, &serialSize);
+
+ return byteArray(serial, serial + serialSize);
+}
+
+
+const bool X509Certificate::checkIssuer
+ (ref <const X509Certificate> issuer) const
+{
+ return (gnutls_x509_crt_check_issuer
+ (m_data->cert, issuer->m_data->cert) >= 1);
+}
+
+
+const bool X509Certificate::verify(ref <const X509Certificate> caCert) const
+{
+ unsigned int verify = 0;
+
+ const int res = gnutls_x509_crt_verify
+ (m_data->cert, &(caCert->m_data->cert), 1,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+ &verify);
+
+ return (res == 0 && verify == 0);
+}
+
+
+const datetime X509Certificate::getActivationDate() const
+{
+ const time_t t = gnutls_x509_crt_get_activation_time(m_data->cert);
+ return datetime(t);
+}
+
+
+const datetime X509Certificate::getExpirationDate() const
+{
+ const time_t t = gnutls_x509_crt_get_expiration_time(m_data->cert);
+ return datetime(t);
+}
+
+
+const byteArray X509Certificate::getFingerprint(const DigestAlgorithm algo) const
+{
+ gnutls_digest_algorithm galgo;
+
+ switch (algo)
+ {
+ case DIGEST_MD5:
+
+ galgo = GNUTLS_DIG_MD5;
+ break;
+
+ default:
+ case DIGEST_SHA1:
+
+ galgo = GNUTLS_DIG_SHA;
+ break;
+ }
+
+ size_t bufferSize = 0;
+ gnutls_x509_crt_get_fingerprint
+ (m_data->cert, galgo, NULL, &bufferSize);
+
+ byte* buffer = new byte[bufferSize];
+
+ if (gnutls_x509_crt_get_fingerprint
+ (m_data->cert, galgo, buffer, &bufferSize) == 0)
+ {
+ byteArray res;
+ res.insert(res.end(), buffer, buffer + bufferSize);
+
+ delete [] buffer;
+
+ return res;
+ }
+
+ delete [] buffer;
+
+ return byteArray();
+}
+
+
+const byteArray X509Certificate::getEncoded() const
+{
+ byteArray bytes;
+ utility::outputStreamByteArrayAdapter os(bytes);
+
+ write(os, FORMAT_DER);
+
+ return bytes;
+}
+
+
+const string X509Certificate::getType() const
+{
+ return "X.509";
+}
+
+
+const int X509Certificate::getVersion() const
+{
+ return gnutls_x509_crt_get_version(m_data->cert);
+}
+
+
+const bool X509Certificate::equals(ref <const certificate> other) const
+{
+ ref <const X509Certificate> otherX509 =
+ other.dynamicCast <const X509Certificate>();
+
+ if (!otherX509)
+ return false;
+
+ const byteArray fp1 = getFingerprint(DIGEST_MD5);
+ const byteArray fp2 = otherX509->getFingerprint(DIGEST_MD5);
+
+ return fp1 == fp2;
+}
+
+
+} // cert
+} // security
+} // vmime
+
diff --git a/src/security/cert/certificateChain.cpp b/src/security/cert/certificateChain.cpp
new file mode 100644
index 00000000..2ac25258
--- /dev/null
+++ b/src/security/cert/certificateChain.cpp
@@ -0,0 +1,53 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 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/security/cert/certificateChain.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace cert {
+
+
+certificateChain::certificateChain(const std::vector <ref <certificate> >& certs)
+ : m_certs(certs)
+{
+}
+
+
+const unsigned int certificateChain::getCount() const
+{
+ return static_cast <unsigned int>(m_certs.size());
+}
+
+
+ref <certificate> certificateChain::getAt(const unsigned int index)
+{
+ return m_certs[index];
+}
+
+
+} // cert
+} // security
+} // vmime
+
diff --git a/src/security/cert/defaultCertificateVerifier.cpp b/src/security/cert/defaultCertificateVerifier.cpp
new file mode 100644
index 00000000..b125d3e4
--- /dev/null
+++ b/src/security/cert/defaultCertificateVerifier.cpp
@@ -0,0 +1,164 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 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 2 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/security/cert/defaultCertificateVerifier.hpp"
+
+#include "vmime/security/cert/X509Certificate.hpp"
+
+#include "vmime/exception.hpp"
+
+
+namespace vmime {
+namespace security {
+namespace cert {
+
+
+defaultCertificateVerifier::defaultCertificateVerifier()
+{
+}
+
+
+defaultCertificateVerifier::~defaultCertificateVerifier()
+{
+}
+
+
+defaultCertificateVerifier::defaultCertificateVerifier(const defaultCertificateVerifier&)
+ : certificateVerifier()
+{
+ // Not used
+}
+
+
+void defaultCertificateVerifier::verify(ref <certificateChain> chain)
+{
+ if (chain->getCount() == 0)
+ return;
+
+ const string type = chain->getAt(0)->getType();
+
+ if (type == "X.509")
+ verifyX509(chain);
+ else
+ throw exceptions::unsupported_certificate_type(type);
+}
+
+
+void defaultCertificateVerifier::verifyX509(ref <certificateChain> chain)
+{
+ // For every certificate in the chain, verify that the certificate
+ // has been issued by the next certificate in the chain
+ if (chain->getCount() >= 2)
+ {
+ for (unsigned int i = 0 ; i < chain->getCount() - 1 ; ++i)
+ {
+ ref <X509Certificate> cert =
+ chain->getAt(i).dynamicCast <X509Certificate>();
+
+ ref <X509Certificate> next =
+ chain->getAt(i + 1).dynamicCast <X509Certificate>();
+
+ if (!cert->checkIssuer(next))
+ {
+ throw exceptions::certificate_verification_exception
+ ("Subject/issuer verification failed.");
+ }
+ }
+ }
+
+ // For every certificate in the chain, verify that the certificate
+ // is valid at the current time
+ const datetime now = datetime::now();
+
+ for (unsigned int i = 0 ; i < chain->getCount() ; ++i)
+ {
+ ref <X509Certificate> cert =
+ chain->getAt(i).dynamicCast <X509Certificate>();
+
+ const datetime begin = cert->getActivationDate();
+ const datetime end = cert->getExpirationDate();
+
+ if (now < begin || now > end)
+ {
+ throw exceptions::certificate_verification_exception
+ ("Validity date check failed.");
+ }
+ }
+
+ // Check whether the certificate can be trusted
+
+ // -- First, verify that the the last certificate in the chain was
+ // -- issued by a third-party that we trust
+ ref <X509Certificate> lastCert =
+ chain->getAt(chain->getCount() - 1).dynamicCast <X509Certificate>();
+
+ bool trusted = false;
+
+ for (unsigned int i = 0 ; !trusted && i < m_x509RootCAs.size() ; ++i)
+ {
+ ref <X509Certificate> rootCa = m_x509RootCAs[i];
+
+ if (lastCert->verify(rootCa))
+ trusted = true;
+ }
+
+ // -- Next, if the issuer certificate cannot be verified against
+ // -- root CAs, compare the subject's certificate against the
+ // -- trusted certificates
+ ref <X509Certificate> firstCert =
+ chain->getAt(0).dynamicCast <X509Certificate>();
+
+ for (unsigned int i = 0 ; !trusted && i < m_x509TrustedCerts.size() ; ++i)
+ {
+ ref <X509Certificate> cert = m_x509TrustedCerts[i];
+
+ if (firstCert->equals(cert))
+ trusted = true;
+ }
+
+ if (!trusted)
+ {
+ throw exceptions::certificate_verification_exception
+ ("Cannot verify certificate against trusted certificates.");
+ }
+}
+
+
+void defaultCertificateVerifier::setX509RootCAs
+ (const std::vector <ref <X509Certificate> >& caCerts)
+{
+ m_x509RootCAs = caCerts;
+}
+
+
+void defaultCertificateVerifier::setX509TrustedCerts
+ (const std::vector <ref <X509Certificate> >& trustedCerts)
+{
+ m_x509TrustedCerts = trustedCerts;
+}
+
+
+} // cert
+} // security
+} // vmime
+