41 #include "compose.hpp"
45 #include <asdcp/KM_util.h>
46 #include <libxml++/nodes/element.h>
47 #include <openssl/x509.h>
48 #include <openssl/ssl.h>
49 #include <openssl/asn1.h>
50 #include <openssl/err.h>
51 #include <boost/algorithm/string.hpp>
64 static string const begin_certificate =
"-----BEGIN CERTIFICATE-----";
65 static string const end_certificate =
"-----END CERTIFICATE-----";
68 Certificate::Certificate (X509* c)
75 Certificate::Certificate (
string cert)
79 throw MiscError (
"unexpected data after certificate");
84 Certificate::Certificate (
Certificate const & other)
86 if (other._certificate) {
102 for (
size_t i = 0; i < cert.length(); ++i) {
104 if (cert[i] ==
'\r' || cert[i] ==
'\n') {
105 boost::algorithm::trim (line);
106 lines.push_back (line);
112 boost::algorithm::trim (line);
113 lines.push_back (line);
116 auto i = lines.begin ();
119 while (i != lines.end() && *i != begin_certificate) {
123 if (i == lines.end()) {
124 throw MiscError (
"missing BEGIN line in certificate");
131 bool got_end =
false;
133 while (i != lines.end()) {
134 if (*i == end_certificate) {
143 throw MiscError (
"missing END line in certificate");
151 string fixed = begin_certificate +
"\n";
152 while (!base64.empty ()) {
153 size_t const t = min (
size_t(64), base64.length());
154 fixed += base64.substr (0, t) +
"\n";
155 base64 = base64.substr (t, base64.length() - t);
158 fixed += end_certificate;
160 auto bio = BIO_new_mem_buf (
const_cast<char *
> (fixed.c_str ()), -1);
162 throw MiscError (
"could not create memory BIO");
165 _certificate = PEM_read_bio_X509 (bio, 0, 0, 0);
167 throw MiscError (
"could not read X509 certificate from memory BIO");
174 while (i != lines.end()) {
185 Certificate::~Certificate ()
187 X509_free (_certificate);
188 RSA_free (_public_key);
195 if (
this == &other) {
199 X509_free (_certificate);
201 RSA_free (_public_key);
213 DCP_ASSERT (_certificate);
215 auto bio = BIO_new (BIO_s_mem());
217 throw MiscError (
"could not create memory BIO");
220 PEM_write_bio_X509 (bio, _certificate);
224 long int const data_length = BIO_get_mem_data (bio, &data);
225 for (
long int i = 0; i < data_length; ++i) {
231 if (!with_begin_end) {
232 boost::replace_all (s, begin_certificate +
"\n",
"");
233 boost::replace_all (s,
"\n" + end_certificate +
"\n",
"");
243 DCP_ASSERT (_certificate);
244 return name_for_xml (X509_get_issuer_name(_certificate));
249 Certificate::issuer_common_name()
const
251 DCP_ASSERT(_certificate);
253 return get_name_part(X509_get_issuer_name(_certificate), NID_commonName);
258 Certificate::issuer_organization_name()
const
260 DCP_ASSERT(_certificate);
262 return get_name_part(X509_get_issuer_name(_certificate), NID_organizationName);
267 Certificate::issuer_organizational_unit_name()
const
269 DCP_ASSERT(_certificate);
271 return get_name_part(X509_get_issuer_name(_certificate), NID_organizationalUnitName);
276 Certificate::asn_to_utf8 (ASN1_STRING* s)
278 unsigned char* buf = 0;
279 ASN1_STRING_to_UTF8 (&buf, s);
280 string const u (
reinterpret_cast<char *
> (buf));
287 Certificate::get_name_part (X509_NAME* n,
int nid)
290 p = X509_NAME_get_index_by_NID (n, nid, p);
294 return asn_to_utf8 (X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p)));
299 Certificate::name_for_xml (X509_NAME* name)
303 auto bio = BIO_new (BIO_s_mem ());
305 throw MiscError (
"could not create memory BIO");
308 X509_NAME_print_ex (bio, name, 0, XN_FLAG_RFC2253);
309 int n = BIO_pending (bio);
310 char* result =
new char[n + 1];
311 n = BIO_read (bio, result, n);
323 Certificate::subject ()
const
325 DCP_ASSERT (_certificate);
327 return name_for_xml (X509_get_subject_name(_certificate));
332 Certificate::subject_common_name ()
const
334 DCP_ASSERT (_certificate);
336 return get_name_part (X509_get_subject_name(_certificate), NID_commonName);
341 Certificate::subject_organization_name ()
const
343 DCP_ASSERT (_certificate);
345 return get_name_part (X509_get_subject_name(_certificate), NID_organizationName);
350 Certificate::subject_organizational_unit_name ()
const
352 DCP_ASSERT (_certificate);
354 return get_name_part (X509_get_subject_name(_certificate), NID_organizationalUnitName);
359 Certificate::subject_dn_qualifier()
const
361 DCP_ASSERT (_certificate);
363 return get_name_part(X509_get_subject_name(_certificate), NID_dnQualifier);
369 convert_time (ASN1_TIME
const * time)
372 char const * s = (
char const *) time->data;
374 if (time->type == V_ASN1_UTCTIME) {
375 return LocalTime::from_asn1_utc_time (s);
376 }
else if (time->type == V_ASN1_GENERALIZEDTIME) {
377 return LocalTime::from_asn1_generalized_time (s);
386 Certificate::not_before ()
const
388 DCP_ASSERT (_certificate);
389 #if OPENSSL_VERSION_NUMBER > 0x10100000L
390 return convert_time(X509_get0_notBefore(_certificate));
392 return convert_time(X509_get_notBefore(_certificate));
398 Certificate::not_after ()
const
400 DCP_ASSERT (_certificate);
401 #if OPENSSL_VERSION_NUMBER > 0x10100000L
402 return convert_time(X509_get0_notAfter(_certificate));
404 return convert_time(X509_get_notAfter(_certificate));
410 Certificate::serial ()
const
412 DCP_ASSERT (_certificate);
414 auto s = X509_get_serialNumber (_certificate);
417 auto b = ASN1_INTEGER_to_BN (s, 0);
418 char* c = BN_bn2dec (b);
431 DCP_ASSERT (_certificate);
433 uint8_t buffer[8192];
436 #if OPENSSL_VERSION_NUMBER > 0x10100000L
437 i2d_re_X509_tbs(_certificate, &p);
439 i2d_X509_CINF (_certificate->cert_info, &p);
441 unsigned int const length = p - buffer;
442 if (length >
sizeof (buffer)) {
443 throw MiscError (
"buffer too small to generate thumbprint");
448 SHA1_Update (&sha, buffer, length);
450 SHA1_Final (digest, &sha);
452 char digest_base64[64];
453 return Kumu::base64encode (digest, 20, digest_base64, 64);
460 DCP_ASSERT (_certificate);
466 auto key = X509_get_pubkey (_certificate);
468 throw MiscError (
"could not get public key from certificate");
471 _public_key = EVP_PKEY_get1_RSA (key);
473 throw MiscError (String::compose (
"could not get RSA public key (%1)", ERR_error_string (ERR_get_error(), 0)));
480 static bool string_is_utf8 (X509_NAME* n,
int nid)
483 p = X509_NAME_get_index_by_NID (n, nid, p);
484 return p != -1 && X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p))->type == V_ASN1_UTF8STRING;
489 Certificate::has_utf8_strings ()
const
491 auto n = X509_get_subject_name (_certificate);
492 return string_is_utf8(n, NID_commonName) ||
493 string_is_utf8(n, NID_organizationName) ||
494 string_is_utf8(n, NID_organizationalUnitName);
513 dcp::operator<< (ostream& s,
Certificate const & c)
A wrapper for an X509 certificate.
std::string read_string(std::string)
std::string certificate(bool with_begin_end=false) const
std::string thumbprint() const
std::string issuer() const
A representation of a local time (down to the second), including its offset from GMT (equivalent to x...
A miscellaneous exception.
Exceptions thrown by libdcp.
Namespace for everything in libdcp.
Utility methods and classes.