2 Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
34 /** @file src/certificate.cc
35 * @brief Certificate class.
38 #include "certificate.h"
39 #include "compose.hpp"
40 #include "exceptions.h"
42 #include "dcp_assert.h"
43 #include <asdcp/KM_util.h>
44 #include <libxml++/nodes/element.h>
45 #include <openssl/x509.h>
46 #include <openssl/ssl.h>
47 #include <openssl/asn1.h>
48 #include <openssl/err.h>
49 #include <boost/algorithm/string.hpp>
60 static string const begin_certificate = "-----BEGIN CERTIFICATE-----";
61 static string const end_certificate = "-----END CERTIFICATE-----";
63 /** @param c X509 certificate, which this object will take ownership of */
64 Certificate::Certificate (X509* c)
71 /** Load an X509 certificate from a string.
72 * @param cert String to read from.
74 Certificate::Certificate (string cert)
78 string const s = read_string (cert);
80 throw MiscError ("unexpected data after certificate");
85 * @param other Certificate to copy.
87 Certificate::Certificate (Certificate const & other)
91 if (other._certificate) {
92 read_string (other.certificate (true));
96 /** Read a certificate from a string.
97 * @param cert String to read.
98 * @return remaining part of the input string after the certificate which was read.
101 Certificate::read_string (string cert)
103 /* Reformat cert so that it has line breaks every 64 characters.
104 See http://comments.gmane.org/gmane.comp.encryption.openssl.user/55593
110 for (size_t i = 0; i < cert.length(); ++i) {
112 if (cert[i] == '\r' || cert[i] == '\n') {
113 boost::algorithm::trim (line);
114 lines.push_back (line);
120 boost::algorithm::trim (line);
121 lines.push_back (line);
124 list<string>::iterator i = lines.begin ();
127 while (i != lines.end() && *i != begin_certificate) {
131 if (i == lines.end()) {
132 throw MiscError ("missing BEGIN line in certificate");
135 /* Skip over the BEGIN line */
138 /* The base64 data */
139 bool got_end = false;
141 while (i != lines.end()) {
142 if (*i == end_certificate) {
151 throw MiscError ("missing END line in certificate");
154 /* Skip over the END line */
157 /* Make up the fixed version */
159 string fixed = begin_certificate + "\n";
160 while (!base64.empty ()) {
161 size_t const t = min (size_t(64), base64.length());
162 fixed += base64.substr (0, t) + "\n";
163 base64 = base64.substr (t, base64.length() - t);
166 fixed += end_certificate;
168 BIO* bio = BIO_new_mem_buf (const_cast<char *> (fixed.c_str ()), -1);
170 throw MiscError ("could not create memory BIO");
173 _certificate = PEM_read_bio_X509 (bio, 0, 0, 0);
175 throw MiscError ("could not read X509 certificate from memory BIO");
182 while (i != lines.end()) {
193 Certificate::~Certificate ()
195 X509_free (_certificate);
196 RSA_free (_public_key);
199 /** operator= for Certificate.
200 * @param other Certificate to read from.
203 Certificate::operator= (Certificate const & other)
205 if (this == &other) {
209 X509_free (_certificate);
211 RSA_free (_public_key);
214 read_string (other.certificate (true));
219 /** Return the certificate as a string.
220 * @param with_begin_end true to include the -----BEGIN CERTIFICATE--- / -----END CERTIFICATE----- markers.
221 * @return Certificate string.
224 Certificate::certificate (bool with_begin_end) const
226 DCP_ASSERT (_certificate);
228 BIO* bio = BIO_new (BIO_s_mem ());
230 throw MiscError ("could not create memory BIO");
233 PEM_write_bio_X509 (bio, _certificate);
237 long int const data_length = BIO_get_mem_data (bio, &data);
238 for (long int i = 0; i < data_length; ++i) {
244 if (!with_begin_end) {
245 boost::replace_all (s, begin_certificate + "\n", "");
246 boost::replace_all (s, "\n" + end_certificate + "\n", "");
252 /** @return Certificate's issuer, in the form
253 * dnqualifier=<dnQualififer>,CN=<commonName>,OU=<organizationalUnitName>,O=<organizationName>
254 * and with + signs escaped to \+
257 Certificate::issuer () const
259 DCP_ASSERT (_certificate);
260 return name_for_xml (X509_get_issuer_name (_certificate));
264 Certificate::asn_to_utf8 (ASN1_STRING* s)
266 unsigned char* buf = 0;
267 ASN1_STRING_to_UTF8 (&buf, s);
268 string const u (reinterpret_cast<char *> (buf));
274 Certificate::get_name_part (X509_NAME* n, int nid)
277 p = X509_NAME_get_index_by_NID (n, nid, p);
281 return asn_to_utf8 (X509_NAME_ENTRY_get_data (X509_NAME_get_entry (n, p)));
285 Certificate::name_for_xml (X509_NAME* name)
289 BIO* bio = BIO_new (BIO_s_mem ());
291 throw MiscError ("could not create memory BIO");
294 X509_NAME_print_ex (bio, name, 0, XN_FLAG_RFC2253);
295 int n = BIO_pending (bio);
296 char* result = new char[n + 1];
297 n = BIO_read (bio, result, n);
309 Certificate::subject () const
311 DCP_ASSERT (_certificate);
313 return name_for_xml (X509_get_subject_name (_certificate));
317 Certificate::subject_common_name () const
319 DCP_ASSERT (_certificate);
321 return get_name_part (X509_get_subject_name (_certificate), NID_commonName);
325 Certificate::subject_organization_name () const
327 DCP_ASSERT (_certificate);
329 return get_name_part (X509_get_subject_name (_certificate), NID_organizationName);
333 Certificate::subject_organizational_unit_name () const
335 DCP_ASSERT (_certificate);
337 return get_name_part (X509_get_subject_name (_certificate), NID_organizationalUnitName);
342 convert_time (ASN1_TIME const * time)
345 char const * s = (char const *) time->data;
347 if (time->type == V_ASN1_UTCTIME) {
348 sscanf(s, "%2d%2d%2d%2d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
349 if (t.tm_year < 70) {
352 } else if (time->type == V_ASN1_GENERALIZEDTIME) {
353 sscanf(s, "%4d%2d%2d%2d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
363 Certificate::not_before () const
365 DCP_ASSERT (_certificate);
366 #if OPENSSL_VERSION_NUMBER > 0x10100000L
367 return convert_time(X509_get0_notBefore(_certificate));
369 return convert_time(X509_get_notBefore(_certificate));
374 Certificate::not_after () const
376 DCP_ASSERT (_certificate);
377 #if OPENSSL_VERSION_NUMBER > 0x10100000L
378 return convert_time(X509_get0_notAfter(_certificate));
380 return convert_time(X509_get_notAfter(_certificate));
385 Certificate::serial () const
387 DCP_ASSERT (_certificate);
389 ASN1_INTEGER* s = X509_get_serialNumber (_certificate);
392 BIGNUM* b = ASN1_INTEGER_to_BN (s, 0);
393 char* c = BN_bn2dec (b);
402 /** @return thumbprint of the to-be-signed portion of this certificate */
404 Certificate::thumbprint () const
406 DCP_ASSERT (_certificate);
408 uint8_t buffer[8192];
411 #if OPENSSL_VERSION_NUMBER > 0x10100000L
412 i2d_re_X509_tbs(_certificate, &p);
414 i2d_X509_CINF (_certificate->cert_info, &p);
416 unsigned int const length = p - buffer;
417 if (length > sizeof (buffer)) {
418 throw MiscError ("buffer too small to generate thumbprint");
423 SHA1_Update (&sha, buffer, length);
425 SHA1_Final (digest, &sha);
427 char digest_base64[64];
428 return Kumu::base64encode (digest, 20, digest_base64, 64);
431 /** @return RSA public key from this Certificate. Caller must not free the returned value. */
433 Certificate::public_key () const
435 DCP_ASSERT (_certificate);
441 EVP_PKEY* key = X509_get_pubkey (_certificate);
443 throw MiscError ("could not get public key from certificate");
446 _public_key = EVP_PKEY_get1_RSA (key);
448 throw MiscError (String::compose ("could not get RSA public key (%1)", ERR_error_string (ERR_get_error(), 0)));
454 static bool string_is_utf8 (X509_NAME* n, int nid)
457 p = X509_NAME_get_index_by_NID (n, nid, p);
458 return p != -1 && X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p))->type == V_ASN1_UTF8STRING;
462 Certificate::has_utf8_strings () const
464 X509_NAME* n = X509_get_subject_name (_certificate);
465 return string_is_utf8(n, NID_commonName) ||
466 string_is_utf8(n, NID_organizationName) ||
467 string_is_utf8(n, NID_organizationalUnitName);
471 dcp::operator== (Certificate const & a, Certificate const & b)
473 return a.certificate() == b.certificate();
477 dcp::operator< (Certificate const & a, Certificate const & b)
479 return a.certificate() < b.certificate();
483 dcp::operator<< (ostream& s, Certificate const & c)
485 s << c.certificate();