libdcp
certificate.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3 
4  This file is part of libdcp.
5 
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.
10 
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.
15 
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/>.
18 
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
23  including the two.
24 
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.
32 */
33 
34 
40 #include "certificate.h"
41 #include "compose.hpp"
42 #include "exceptions.h"
43 #include "util.h"
44 #include "dcp_assert.h"
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>
52 #include <cerrno>
53 #include <iostream>
54 #include <algorithm>
55 
56 
57 using std::list;
58 using std::string;
59 using std::ostream;
60 using std::min;
61 using namespace dcp;
62 
63 
64 static string const begin_certificate = "-----BEGIN CERTIFICATE-----";
65 static string const end_certificate = "-----END CERTIFICATE-----";
66 
67 
68 Certificate::Certificate (X509* c)
69  : _certificate (c)
70 {
71 
72 }
73 
74 
75 Certificate::Certificate (string cert)
76 {
77  auto const s = read_string (cert);
78  if (!s.empty()) {
79  throw MiscError ("unexpected data after certificate");
80  }
81 }
82 
83 
84 Certificate::Certificate (Certificate const & other)
85 {
86  if (other._certificate) {
87  read_string (other.certificate (true));
88  }
89 }
90 
91 
92 string
94 {
95  /* Reformat cert so that it has line breaks every 64 characters.
96  See http://comments.gmane.org/gmane.comp.encryption.openssl.user/55593
97  */
98 
99  list<string> lines;
100  string line;
101 
102  for (size_t i = 0; i < cert.length(); ++i) {
103  line += cert[i];
104  if (cert[i] == '\r' || cert[i] == '\n') {
105  boost::algorithm::trim (line);
106  lines.push_back (line);
107  line = "";
108  }
109  }
110 
111  if (!line.empty()) {
112  boost::algorithm::trim (line);
113  lines.push_back (line);
114  }
115 
116  auto i = lines.begin ();
117 
118  /* BEGIN */
119  while (i != lines.end() && *i != begin_certificate) {
120  ++i;
121  }
122 
123  if (i == lines.end()) {
124  throw MiscError ("missing BEGIN line in certificate");
125  }
126 
127  /* Skip over the BEGIN line */
128  ++i;
129 
130  /* The base64 data */
131  bool got_end = false;
132  string base64 = "";
133  while (i != lines.end()) {
134  if (*i == end_certificate) {
135  got_end = true;
136  break;
137  }
138  base64 += *i;
139  ++i;
140  }
141 
142  if (!got_end) {
143  throw MiscError ("missing END line in certificate");
144  }
145 
146  /* Skip over the END line */
147  ++i;
148 
149  /* Make up the fixed version */
150 
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);
156  }
157 
158  fixed += end_certificate;
159 
160  auto bio = BIO_new_mem_buf (const_cast<char *> (fixed.c_str ()), -1);
161  if (!bio) {
162  throw MiscError ("could not create memory BIO");
163  }
164 
165  _certificate = PEM_read_bio_X509 (bio, 0, 0, 0);
166  if (!_certificate) {
167  throw MiscError ("could not read X509 certificate from memory BIO");
168  }
169 
170  BIO_free (bio);
171 
172  string extra;
173 
174  while (i != lines.end()) {
175  if (!i->empty()) {
176  extra += *i + "\n";
177  }
178  ++i;
179  }
180 
181  return extra;
182 }
183 
184 
185 Certificate::~Certificate ()
186 {
187  X509_free (_certificate);
188  RSA_free (_public_key);
189 }
190 
191 
192 Certificate &
193 Certificate::operator= (Certificate const & other)
194 {
195  if (this == &other) {
196  return *this;
197  }
198 
199  X509_free (_certificate);
200  _certificate = 0;
201  RSA_free (_public_key);
202  _public_key = 0;
203 
204  read_string (other.certificate(true));
205 
206  return *this;
207 }
208 
209 
210 string
211 Certificate::certificate (bool with_begin_end) const
212 {
213  DCP_ASSERT (_certificate);
214 
215  auto bio = BIO_new (BIO_s_mem());
216  if (!bio) {
217  throw MiscError ("could not create memory BIO");
218  }
219 
220  PEM_write_bio_X509 (bio, _certificate);
221 
222  string s;
223  char* data;
224  long int const data_length = BIO_get_mem_data (bio, &data);
225  for (long int i = 0; i < data_length; ++i) {
226  s += data[i];
227  }
228 
229  BIO_free (bio);
230 
231  if (!with_begin_end) {
232  boost::replace_all (s, begin_certificate + "\n", "");
233  boost::replace_all (s, "\n" + end_certificate + "\n", "");
234  }
235 
236  return s;
237 }
238 
239 
240 string
242 {
243  DCP_ASSERT (_certificate);
244  return name_for_xml (X509_get_issuer_name(_certificate));
245 }
246 
247 
248 string
249 Certificate::issuer_common_name() const
250 {
251  DCP_ASSERT(_certificate);
252 
253  return get_name_part(X509_get_issuer_name(_certificate), NID_commonName);
254 }
255 
256 
257 string
258 Certificate::issuer_organization_name() const
259 {
260  DCP_ASSERT(_certificate);
261 
262  return get_name_part(X509_get_issuer_name(_certificate), NID_organizationName);
263 }
264 
265 
266 string
267 Certificate::issuer_organizational_unit_name() const
268 {
269  DCP_ASSERT(_certificate);
270 
271  return get_name_part(X509_get_issuer_name(_certificate), NID_organizationalUnitName);
272 }
273 
274 
275 string
276 Certificate::asn_to_utf8 (ASN1_STRING* s)
277 {
278  unsigned char* buf = 0;
279  ASN1_STRING_to_UTF8 (&buf, s);
280  string const u (reinterpret_cast<char *> (buf));
281  OPENSSL_free (buf);
282  return u;
283 }
284 
285 
286 string
287 Certificate::get_name_part (X509_NAME* n, int nid)
288 {
289  int p = -1;
290  p = X509_NAME_get_index_by_NID (n, nid, p);
291  if (p == -1) {
292  return "";
293  }
294  return asn_to_utf8 (X509_NAME_ENTRY_get_data(X509_NAME_get_entry(n, p)));
295 }
296 
297 
298 string
299 Certificate::name_for_xml (X509_NAME* name)
300 {
301  assert (name);
302 
303  auto bio = BIO_new (BIO_s_mem ());
304  if (!bio) {
305  throw MiscError ("could not create memory BIO");
306  }
307 
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);
312  result[n] = '\0';
313 
314  BIO_free (bio);
315 
316  string s = result;
317  delete[] result;
318 
319  return s;
320 }
321 
322 string
323 Certificate::subject () const
324 {
325  DCP_ASSERT (_certificate);
326 
327  return name_for_xml (X509_get_subject_name(_certificate));
328 }
329 
330 
331 string
332 Certificate::subject_common_name () const
333 {
334  DCP_ASSERT (_certificate);
335 
336  return get_name_part (X509_get_subject_name(_certificate), NID_commonName);
337 }
338 
339 
340 string
341 Certificate::subject_organization_name () const
342 {
343  DCP_ASSERT (_certificate);
344 
345  return get_name_part (X509_get_subject_name(_certificate), NID_organizationName);
346 }
347 
348 
349 string
350 Certificate::subject_organizational_unit_name () const
351 {
352  DCP_ASSERT (_certificate);
353 
354  return get_name_part (X509_get_subject_name(_certificate), NID_organizationalUnitName);
355 }
356 
357 
358 string
359 Certificate::subject_dn_qualifier() const
360 {
361  DCP_ASSERT (_certificate);
362 
363  return get_name_part(X509_get_subject_name(_certificate), NID_dnQualifier);
364 }
365 
366 
367 static
368 LocalTime
369 convert_time (ASN1_TIME const * time)
370 {
371  LocalTime t;
372  char const * s = (char const *) time->data;
373 
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);
378  }
379 
380  DCP_ASSERT (false);
381  return {};
382 }
383 
384 
385 LocalTime
386 Certificate::not_before () const
387 {
388  DCP_ASSERT (_certificate);
389 #if OPENSSL_VERSION_NUMBER > 0x10100000L
390  return convert_time(X509_get0_notBefore(_certificate));
391 #else
392  return convert_time(X509_get_notBefore(_certificate));
393 #endif
394 }
395 
396 
397 LocalTime
398 Certificate::not_after () const
399 {
400  DCP_ASSERT (_certificate);
401 #if OPENSSL_VERSION_NUMBER > 0x10100000L
402  return convert_time(X509_get0_notAfter(_certificate));
403 #else
404  return convert_time(X509_get_notAfter(_certificate));
405 #endif
406 }
407 
408 
409 string
410 Certificate::serial () const
411 {
412  DCP_ASSERT (_certificate);
413 
414  auto s = X509_get_serialNumber (_certificate);
415  DCP_ASSERT (s);
416 
417  auto b = ASN1_INTEGER_to_BN (s, 0);
418  char* c = BN_bn2dec (b);
419  BN_free (b);
420 
421  string st (c);
422  OPENSSL_free (c);
423 
424  return st;
425 }
426 
427 
428 string
430 {
431  DCP_ASSERT (_certificate);
432 
433  uint8_t buffer[8192];
434  uint8_t* p = buffer;
435 
436 #if OPENSSL_VERSION_NUMBER > 0x10100000L
437  i2d_re_X509_tbs(_certificate, &p);
438 #else
439  i2d_X509_CINF (_certificate->cert_info, &p);
440 #endif
441  unsigned int const length = p - buffer;
442  if (length > sizeof (buffer)) {
443  throw MiscError ("buffer too small to generate thumbprint");
444  }
445 
446  SHA_CTX sha;
447  SHA1_Init (&sha);
448  SHA1_Update (&sha, buffer, length);
449  uint8_t digest[20];
450  SHA1_Final (digest, &sha);
451 
452  char digest_base64[64];
453  return Kumu::base64encode (digest, 20, digest_base64, 64);
454 }
455 
456 
457 RSA *
459 {
460  DCP_ASSERT (_certificate);
461 
462  if (_public_key) {
463  return _public_key;
464  }
465 
466  auto key = X509_get_pubkey (_certificate);
467  if (!key) {
468  throw MiscError ("could not get public key from certificate");
469  }
470 
471  _public_key = EVP_PKEY_get1_RSA (key);
472  if (!_public_key) {
473  throw MiscError (String::compose ("could not get RSA public key (%1)", ERR_error_string (ERR_get_error(), 0)));
474  }
475 
476  return _public_key;
477 }
478 
479 
480 static bool string_is_utf8 (X509_NAME* n, int nid)
481 {
482  int p = -1;
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;
485 }
486 
487 
488 bool
489 Certificate::has_utf8_strings () const
490 {
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);
495 }
496 
497 
498 bool
499 dcp::operator== (Certificate const & a, Certificate const & b)
500 {
501  return a.certificate() == b.certificate();
502 }
503 
504 
505 bool
506 dcp::operator< (Certificate const & a, Certificate const & b)
507 {
508  return a.certificate() < b.certificate();
509 }
510 
511 
512 ostream&
513 dcp::operator<< (ostream& s, Certificate const & c)
514 {
515  s << c.certificate();
516  return s;
517 }
Certificate class.
A wrapper for an X509 certificate.
Definition: certificate.h:66
std::string read_string(std::string)
Definition: certificate.cc:93
std::string certificate(bool with_begin_end=false) const
Definition: certificate.cc:211
RSA * public_key() const
Definition: certificate.cc:458
std::string thumbprint() const
Definition: certificate.cc:429
std::string issuer() const
Definition: certificate.cc:241
A representation of a local time (down to the second), including its offset from GMT (equivalent to x...
Definition: local_time.h:68
A miscellaneous exception.
Definition: exceptions.h:94
DCP_ASSERT macro.
Exceptions thrown by libdcp.
Namespace for everything in libdcp.
Definition: array_data.h:50
Utility methods and classes.