X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fcertificate.cc;h=a83d800d3f934e78c70e0f61aadf1e837efb5d0c;hb=c763ce17ea1d2bf621a343c4893f5d0c390ec433;hp=fcc7b0ae5bda8e807fa1fc3f4ad61823302e7568;hpb=0f09e4f7335fb125273aa3d1dc397797a2eba1dd;p=libdcp.git diff --git a/src/certificate.cc b/src/certificate.cc index fcc7b0ae..a83d800d 100644 --- a/src/certificate.cc +++ b/src/certificate.cc @@ -1,32 +1,46 @@ /* - Copyright (C) 2012-2014 Carl Hetherington + Copyright (C) 2012-2016 Carl Hetherington - This program is free software; you can redistribute it and/or modify + This file is part of libdcp. + + libdcp 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, + libdcp 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., 675 Mass Ave, Cambridge, MA 02139, USA. - + along with libdcp. If not, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. */ -/** @file src/certificates.cc - * @brief Certificate and CertificateChain classes. +/** @file src/certificate.cc + * @brief Certificate class. */ -#include "KM_util.h" #include "certificate.h" #include "compose.hpp" #include "exceptions.h" #include "util.h" #include "dcp_assert.h" +#include #include #include #include @@ -34,13 +48,18 @@ #include #include #include +#include #include using std::list; using std::string; using std::ostream; +using std::min; using namespace dcp; +static string const begin_certificate = "-----BEGIN CERTIFICATE-----"; +static string const end_certificate = "-----END CERTIFICATE-----"; + /** @param c X509 certificate, which this object will take ownership of */ Certificate::Certificate (X509* c) : _certificate (c) @@ -56,7 +75,10 @@ Certificate::Certificate (string cert) : _certificate (0) , _public_key (0) { - read_string (cert); + string const s = read_string (cert); + if (!s.empty ()) { + throw MiscError ("unexpected data after certificate"); + } } /** Copy constructor. @@ -66,16 +88,84 @@ Certificate::Certificate (Certificate const & other) : _certificate (0) , _public_key (0) { - read_string (other.certificate (true)); + if (other._certificate) { + read_string (other.certificate (true)); + } } /** Read a certificate from a string. * @param cert String to read. + * @return remaining part of the input string after the certificate which was read. */ -void +string Certificate::read_string (string cert) { - BIO* bio = BIO_new_mem_buf (const_cast (cert.c_str ()), -1); + /* Reformat cert so that it has line breaks every 64 characters. + See http://comments.gmane.org/gmane.comp.encryption.openssl.user/55593 + */ + + list lines; + string line; + + for (size_t i = 0; i < cert.length(); ++i) { + line += cert[i]; + if (cert[i] == '\r' || cert[i] == '\n') { + boost::algorithm::trim (line); + lines.push_back (line); + line = ""; + } + } + + if (!line.empty()) { + boost::algorithm::trim (line); + lines.push_back (line); + } + + list::iterator i = lines.begin (); + + /* BEGIN */ + while (i != lines.end() && *i != begin_certificate) { + ++i; + } + + if (i == lines.end()) { + throw MiscError ("missing BEGIN line in certificate"); + } + + /* Skip over the BEGIN line */ + ++i; + + /* The base64 data */ + bool got_end = false; + string base64 = ""; + while (i != lines.end()) { + if (*i == end_certificate) { + got_end = true; + break; + } + base64 += *i; + ++i; + } + + if (!got_end) { + throw MiscError ("missing END line in certificate"); + } + + /* Skip over the END line */ + ++i; + + /* Make up the fixed version */ + + string fixed = begin_certificate + "\n"; + while (!base64.empty ()) { + size_t const t = min (size_t(64), base64.length()); + fixed += base64.substr (0, t) + "\n"; + base64 = base64.substr (t, base64.length() - t); + } + + fixed += end_certificate; + + BIO* bio = BIO_new_mem_buf (const_cast (fixed.c_str ()), -1); if (!bio) { throw MiscError ("could not create memory BIO"); } @@ -86,6 +176,17 @@ Certificate::read_string (string cert) } BIO_free (bio); + + string extra; + + while (i != lines.end()) { + if (!i->empty()) { + extra += *i + "\n"; + } + ++i; + } + + return extra; } /** Destructor */ @@ -141,8 +242,8 @@ Certificate::certificate (bool with_begin_end) const BIO_free (bio); if (!with_begin_end) { - boost::replace_all (s, "-----BEGIN CERTIFICATE-----\n", ""); - boost::replace_all (s, "\n-----END CERTIFICATE-----\n", ""); + boost::replace_all (s, begin_certificate + "\n", ""); + boost::replace_all (s, "\n" + end_certificate + "\n", ""); } return s; @@ -174,7 +275,9 @@ Certificate::get_name_part (X509_NAME* n, int nid) { int p = -1; p = X509_NAME_get_index_by_NID (n, nid, p); - DCP_ASSERT (p != -1); + if (p == -1) { + return ""; + } return asn_to_utf8 (X509_NAME_ENTRY_get_data (X509_NAME_get_entry (n, p))); } @@ -252,6 +355,7 @@ Certificate::serial () const return st; } +/** @return thumbprint of the to-be-signed portion of this certificate */ string Certificate::thumbprint () const { @@ -259,7 +363,12 @@ Certificate::thumbprint () const uint8_t buffer[8192]; uint8_t* p = buffer; + +#if OPENSSL_VERSION_NUMBER > 0x10100000L + i2d_re_X509_tbs(_certificate, &p); +#else i2d_X509_CINF (_certificate->cert_info, &p); +#endif unsigned int const length = p - buffer; if (length > sizeof (buffer)) { throw MiscError ("buffer too small to generate thumbprint"); @@ -298,6 +407,22 @@ Certificate::public_key () const return _public_key; } +static bool string_is_utf8 (X509_NAME* n, int nid) +{ + int p = -1; + p = X509_NAME_get_index_by_NID (n, nid, p); + return p != -1 && X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p))->type == V_ASN1_UTF8STRING; +} + +bool +Certificate::has_utf8_strings () const +{ + X509_NAME* n = X509_get_subject_name (_certificate); + return string_is_utf8(n, NID_commonName) || + string_is_utf8(n, NID_organizationName) || + string_is_utf8(n, NID_organizationalUnitName); +} + bool dcp::operator== (Certificate const & a, Certificate const & b) {