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::asn_to_utf8 (ASN1_STRING* s)
251 unsigned char* buf = 0;
252 ASN1_STRING_to_UTF8 (&buf, s);
253 string const u (
reinterpret_cast<char *
> (buf));
260 Certificate::get_name_part (X509_NAME* n,
int nid)
263 p = X509_NAME_get_index_by_NID (n, nid, p);
267 return asn_to_utf8 (X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p)));
272 Certificate::name_for_xml (X509_NAME* name)
276 auto bio = BIO_new (BIO_s_mem ());
278 throw MiscError (
"could not create memory BIO");
281 X509_NAME_print_ex (bio, name, 0, XN_FLAG_RFC2253);
282 int n = BIO_pending (bio);
283 char* result =
new char[n + 1];
284 n = BIO_read (bio, result, n);
296 Certificate::subject ()
const
298 DCP_ASSERT (_certificate);
300 return name_for_xml (X509_get_subject_name(_certificate));
305 Certificate::subject_common_name ()
const
307 DCP_ASSERT (_certificate);
309 return get_name_part (X509_get_subject_name(_certificate), NID_commonName);
314 Certificate::subject_organization_name ()
const
316 DCP_ASSERT (_certificate);
318 return get_name_part (X509_get_subject_name(_certificate), NID_organizationName);
323 Certificate::subject_organizational_unit_name ()
const
325 DCP_ASSERT (_certificate);
327 return get_name_part (X509_get_subject_name(_certificate), NID_organizationalUnitName);
333 convert_time (ASN1_TIME
const * time)
336 char const * s = (
char const *) time->data;
338 if (time->type == V_ASN1_UTCTIME) {
339 return LocalTime::from_asn1_utc_time (s);
340 }
else if (time->type == V_ASN1_GENERALIZEDTIME) {
341 return LocalTime::from_asn1_generalized_time (s);
350 Certificate::not_before ()
const
352 DCP_ASSERT (_certificate);
353 #if OPENSSL_VERSION_NUMBER > 0x10100000L
354 return convert_time(X509_get0_notBefore(_certificate));
356 return convert_time(X509_get_notBefore(_certificate));
362 Certificate::not_after ()
const
364 DCP_ASSERT (_certificate);
365 #if OPENSSL_VERSION_NUMBER > 0x10100000L
366 return convert_time(X509_get0_notAfter(_certificate));
368 return convert_time(X509_get_notAfter(_certificate));
374 Certificate::serial ()
const
376 DCP_ASSERT (_certificate);
378 auto s = X509_get_serialNumber (_certificate);
381 auto b = ASN1_INTEGER_to_BN (s, 0);
382 char* c = BN_bn2dec (b);
395 DCP_ASSERT (_certificate);
397 uint8_t buffer[8192];
400 #if OPENSSL_VERSION_NUMBER > 0x10100000L
401 i2d_re_X509_tbs(_certificate, &p);
403 i2d_X509_CINF (_certificate->cert_info, &p);
405 unsigned int const length = p - buffer;
406 if (length >
sizeof (buffer)) {
407 throw MiscError (
"buffer too small to generate thumbprint");
412 SHA1_Update (&sha, buffer, length);
414 SHA1_Final (digest, &sha);
416 char digest_base64[64];
417 return Kumu::base64encode (digest, 20, digest_base64, 64);
424 DCP_ASSERT (_certificate);
430 auto key = X509_get_pubkey (_certificate);
432 throw MiscError (
"could not get public key from certificate");
435 _public_key = EVP_PKEY_get1_RSA (key);
437 throw MiscError (String::compose (
"could not get RSA public key (%1)", ERR_error_string (ERR_get_error(), 0)));
444 static bool string_is_utf8 (X509_NAME* n,
int nid)
447 p = X509_NAME_get_index_by_NID (n, nid, p);
448 return p != -1 && X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p))->type == V_ASN1_UTF8STRING;
453 Certificate::has_utf8_strings ()
const
455 auto n = X509_get_subject_name (_certificate);
456 return string_is_utf8(n, NID_commonName) ||
457 string_is_utf8(n, NID_organizationName) ||
458 string_is_utf8(n, NID_organizationalUnitName);
477 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.