46c60d6beb2e27477a774fec265142b16d288b0f
[libdcp.git] / src / certificates.cc
1 /*
2     Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /** @file  src/certificates.cc
21  *  @brief Certificate and CertificateChain classes.
22  */
23
24 #include "KM_util.h"
25 #include "certificates.h"
26 #include "compose.hpp"
27 #include "exceptions.h"
28 #include "util.h"
29 #include <libxml++/nodes/element.h>
30 #include <openssl/x509.h>
31 #include <openssl/ssl.h>
32 #include <openssl/asn1.h>
33 #include <openssl/err.h>
34 #include <boost/algorithm/string.hpp>
35 #include <cerrno>
36 #include <algorithm>
37
38 using std::list;
39 using std::string;
40 using std::cout;
41 using boost::shared_ptr;
42 using namespace dcp;
43
44 /** @param c X509 certificate, which this object will take ownership of */
45 Certificate::Certificate (X509* c)
46         : _certificate (c)
47         , _public_key (0)
48 {
49         
50 }
51
52 /** Load an X509 certificate from a file.
53  *  @param filename File to load.
54  */
55 Certificate::Certificate (boost::filesystem::path filename)
56         : _certificate (0)
57         , _public_key (0)
58 {
59         FILE* f = fopen_boost (filename, "r");
60         if (!f) {
61                 throw FileError ("could not open file", filename, errno);
62         }
63         
64         if (!PEM_read_X509 (f, &_certificate, 0, 0)) {
65                 throw MiscError ("could not read X509 certificate");
66         }
67 }
68
69 /** Load an X509 certificate from a string.
70  *  @param cert String to read from.
71  */
72 Certificate::Certificate (string cert)
73         : _certificate (0)
74         , _public_key (0)
75 {
76         read_string (cert);
77 }
78
79 /** Copy constructor.
80  *  @param other Certificate to copy.
81  */
82 Certificate::Certificate (Certificate const & other)
83         : _certificate (0)
84         , _public_key (0)
85 {
86         read_string (other.certificate (true));
87 }
88
89 /** Read a certificate from a string.
90  *  @param cert String to read.
91  */
92 void
93 Certificate::read_string (string cert)
94 {
95         BIO* bio = BIO_new_mem_buf (const_cast<char *> (cert.c_str ()), -1);
96         if (!bio) {
97                 throw MiscError ("could not create memory BIO");
98         }
99
100         _certificate = PEM_read_bio_X509 (bio, 0, 0, 0);
101         if (!_certificate) {
102                 throw MiscError ("could not read X509 certificate from memory BIO");
103         }
104
105         BIO_free (bio);
106 }
107
108 /** Destructor */
109 Certificate::~Certificate ()
110 {
111         X509_free (_certificate);
112         RSA_free (_public_key);
113 }
114
115 /** operator= for Certificate.
116  *  @param other Certificate to read from.
117  */
118 Certificate &
119 Certificate::operator= (Certificate const & other)
120 {
121         if (this == &other) {
122                 return *this;
123         }
124
125         X509_free (_certificate);
126         _certificate = 0;
127         RSA_free (_public_key);
128         _public_key = 0;
129         
130         read_string (other.certificate ());
131
132         return *this;
133 }
134
135 /** Return the certificate as a string.
136  *  @param with_begin_end true to include the -----BEGIN CERTIFICATE--- / -----END CERTIFICATE----- markers.
137  *  @return Certificate string.
138  */
139 string
140 Certificate::certificate (bool with_begin_end) const
141 {
142         assert (_certificate);
143         
144         BIO* bio = BIO_new (BIO_s_mem ());
145         if (!bio) {
146                 throw MiscError ("could not create memory BIO");
147         }
148         
149         PEM_write_bio_X509 (bio, _certificate);
150
151         string s;
152         char* data;
153         long int const data_length = BIO_get_mem_data (bio, &data);
154         for (long int i = 0; i < data_length; ++i) {
155                 s += data[i];
156         }
157
158         BIO_free (bio);
159
160         if (!with_begin_end) {
161                 boost::replace_all (s, "-----BEGIN CERTIFICATE-----\n", "");
162                 boost::replace_all (s, "\n-----END CERTIFICATE-----\n", "");
163         }
164         
165         return s;
166 }
167
168 /** @return Certificate's issuer, in the form
169  *  dnqualifier=&lt;dnQualififer&gt;,CN=&lt;commonName&gt;,OU=&lt;organizationalUnitName&gt,O=&lt;organizationName&gt;
170  *  and with + signs escaped to \+
171  */
172 string
173 Certificate::issuer () const
174 {
175         assert (_certificate);
176         return name_for_xml (X509_get_issuer_name (_certificate));
177 }
178
179 string
180 Certificate::asn_to_utf8 (ASN1_STRING* s)
181 {
182         unsigned char* buf = 0;
183         ASN1_STRING_to_UTF8 (&buf, s);
184         string const u (reinterpret_cast<char *> (buf));
185         OPENSSL_free (buf);
186         return u;
187 }
188
189 string
190 Certificate::get_name_part (X509_NAME* n, int nid)
191 {
192         int p = -1;
193         p = X509_NAME_get_index_by_NID (n, nid, p);
194         assert (p != -1);
195         return asn_to_utf8 (X509_NAME_ENTRY_get_data (X509_NAME_get_entry (n, p)));
196 }
197         
198
199 string
200 Certificate::name_for_xml (X509_NAME * n)
201 {
202         assert (n);
203
204         string s = String::compose (
205                 "dnQualifier=%1,CN=%2,OU=%3,O=%4",
206                 get_name_part (n, NID_dnQualifier),
207                 get_name_part (n, NID_commonName),
208                 get_name_part (n, NID_organizationalUnitName),
209                 get_name_part (n, NID_organizationName)
210                 );
211         
212         boost::replace_all (s, "+", "\\+");
213         return s;
214 }
215
216 string
217 Certificate::subject () const
218 {
219         assert (_certificate);
220
221         return name_for_xml (X509_get_subject_name (_certificate));
222 }
223
224 string
225 Certificate::common_name () const
226 {
227         assert (_certificate);
228
229         return get_name_part (X509_get_subject_name (_certificate), NID_commonName);
230 }
231
232 string
233 Certificate::serial () const
234 {
235         assert (_certificate);
236
237         ASN1_INTEGER* s = X509_get_serialNumber (_certificate);
238         assert (s);
239         
240         BIGNUM* b = ASN1_INTEGER_to_BN (s, 0);
241         char* c = BN_bn2dec (b);
242         BN_free (b);
243         
244         string st (c);
245         OPENSSL_free (c);
246
247         return st;
248 }
249
250 string
251 Certificate::thumbprint () const
252 {
253         assert (_certificate);
254         
255         uint8_t buffer[8192];
256         uint8_t* p = buffer;
257         i2d_X509_CINF (_certificate->cert_info, &p);
258         int const length = p - buffer;
259         if (length > 8192) {
260                 throw MiscError ("buffer too small to generate thumbprint");
261         }
262
263         SHA_CTX sha;
264         SHA1_Init (&sha);
265         SHA1_Update (&sha, buffer, length);
266         uint8_t digest[20];
267         SHA1_Final (digest, &sha);
268
269         char digest_base64[64];
270         return Kumu::base64encode (digest, 20, digest_base64, 64);
271 }
272
273 /** @return RSA public key from this Certificate.  Caller must not free the returned value. */
274 RSA *
275 Certificate::public_key () const
276 {
277         assert (_certificate);
278
279         if (_public_key) {
280                 return _public_key;
281         }
282
283         EVP_PKEY* key = X509_get_pubkey (_certificate);
284         if (!key) {
285                 throw MiscError ("could not get public key from certificate");
286         }
287
288         _public_key = EVP_PKEY_get1_RSA (key);
289         if (!_public_key) {
290                 throw MiscError (String::compose ("could not get RSA public key (%1)", ERR_error_string (ERR_get_error(), 0)));
291         }
292
293         return _public_key;
294 }
295
296 /** @return Root certificate */
297 shared_ptr<const Certificate>
298 CertificateChain::root () const
299 {
300         assert (!_certificates.empty());
301         return _certificates.front ();
302 }
303
304 /** @return Leaf certificate */
305 shared_ptr<const Certificate>
306 CertificateChain::leaf () const
307 {
308         assert (_certificates.size() >= 2);
309         return _certificates.back ();
310 }
311
312 /** @return Certificates in order from root to leaf */
313 CertificateChain::List
314 CertificateChain::root_to_leaf () const
315 {
316         return _certificates;
317 }
318
319 /** @return Certificates in order from leaf to root */
320 CertificateChain::List
321 CertificateChain::leaf_to_root () const
322 {
323         List c = _certificates;
324         c.reverse ();
325         return c;
326 }
327
328 /** Add a certificate to the end of the chain.
329  *  @param c Certificate to add.
330  */
331 void
332 CertificateChain::add (shared_ptr<const Certificate> c)
333 {
334         _certificates.push_back (c);
335 }
336
337 void
338 CertificateChain::remove (shared_ptr<const Certificate> c)
339 {
340         _certificates.remove (c);
341 }
342
343 /** Remove the i'th certificate in the list, as listed
344  *  from root to leaf.
345  */
346 void
347 CertificateChain::remove (int i)
348 {
349         List::iterator j = _certificates.begin ();
350         while (j != _certificates.end () && i > 0) {
351                 --i;
352                 ++j;
353         }
354
355         if (j != _certificates.end ()) {
356                 _certificates.erase (j);
357         }
358 }
359
360 /** Check to see if the chain is valid (i.e. root signs the intermediate, intermediate
361  *  signs the leaf and so on).
362  *  @return true if it's ok, false if not.
363  */
364 bool
365 CertificateChain::valid () const
366 {
367         X509_STORE* store = X509_STORE_new ();
368         if (!store) {
369                 return false;
370         }
371
372         for (List::const_iterator i = _certificates.begin(); i != _certificates.end(); ++i) {
373
374                 List::const_iterator j = i;
375                 ++j;
376                 if (j ==  _certificates.end ()) {
377                         break;
378                 }
379
380                 if (!X509_STORE_add_cert (store, (*i)->x509 ())) {
381                         X509_STORE_free (store);
382                         return false;
383                 }
384
385                 X509_STORE_CTX* ctx = X509_STORE_CTX_new ();
386                 if (!ctx) {
387                         X509_STORE_free (store);
388                         return false;
389                 }
390
391                 X509_STORE_set_flags (store, 0);
392                 if (!X509_STORE_CTX_init (ctx, store, (*j)->x509 (), 0)) {
393                         X509_STORE_CTX_free (ctx);
394                         X509_STORE_free (store);
395                         return false;
396                 }
397
398                 int v = X509_verify_cert (ctx);
399                 X509_STORE_CTX_free (ctx);
400
401                 if (v == 0) {
402                         X509_STORE_free (store);
403                         return false;
404                 }
405         }
406
407         X509_STORE_free (store);
408         return true;
409 }
410
411 /** @return true if the chain is now in order from root to leaf,
412  *  false if no correct order was found.
413  */
414 bool
415 CertificateChain::attempt_reorder ()
416 {
417         List original = _certificates;
418         _certificates.sort ();
419         do {
420                 if (valid ()) {
421                         return true;
422                 }
423         } while (std::next_permutation (_certificates.begin(), _certificates.end ()));
424
425         _certificates = original;
426         return false;
427 }