Write encryption test all to the right place. Some more XML writes unformatted to...
[libdcp.git] / src / certificates.cc
1 /*
2     Copyright (C) 2012 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 #include <sstream>
21 #include <vector>
22 #include <boost/algorithm/string.hpp>
23 #include <openssl/x509.h>
24 #include <openssl/ssl.h>
25 #include <openssl/asn1.h>
26 #include <openssl/err.h>
27 #include <libxml++/nodes/element.h>
28 #include "KM_util.h"
29 #include "certificates.h"
30 #include "compose.hpp"
31 #include "exceptions.h"
32
33 using std::list;
34 using std::string;
35 using std::stringstream;
36 using std::vector;
37 using boost::shared_ptr;
38 using namespace libdcp;
39
40 /** @param c X509 certificate, which this object will take ownership of */
41 Certificate::Certificate (X509* c)
42         : _certificate (c)
43         , _public_key (0)
44 {
45         
46 }
47
48 Certificate::Certificate (boost::filesystem::path filename)
49         : _certificate (0)
50         , _public_key (0)
51 {
52         FILE* f = fopen (filename.string().c_str(), "r");
53         if (!f) {
54                 throw FileError ("could not open file", filename);
55         }
56         
57         if (!PEM_read_X509 (f, &_certificate, 0, 0)) {
58                 throw MiscError ("could not read X509 certificate");
59         }
60 }
61
62 Certificate::Certificate (string cert)
63         : _certificate (0)
64         , _public_key (0)
65 {
66         read_string (cert);
67 }
68
69 Certificate::Certificate (Certificate const & other)
70         : _certificate (0)
71         , _public_key (0)
72 {
73         read_string (other.certificate (true));
74 }
75
76 void
77 Certificate::read_string (string cert)
78 {
79         BIO* bio = BIO_new_mem_buf (const_cast<char *> (cert.c_str ()), -1);
80         if (!bio) {
81                 throw MiscError ("could not create memory BIO");
82         }
83
84         _certificate = PEM_read_bio_X509 (bio, 0, 0, 0);
85         if (!_certificate) {
86                 throw MiscError ("could not read X509 certificate from memory BIO");
87         }
88
89         BIO_free (bio);
90 }
91
92 Certificate::~Certificate ()
93 {
94         X509_free (_certificate);
95         RSA_free (_public_key);
96 }
97
98 Certificate &
99 Certificate::operator= (Certificate const & other)
100 {
101         if (this == &other) {
102                 return *this;
103         }
104
105         X509_free (_certificate);
106         _certificate = 0;
107         RSA_free (_public_key);
108         _public_key = 0;
109         
110         read_string (other.certificate ());
111
112         return *this;
113 }
114
115 string
116 Certificate::certificate (bool with_begin_end) const
117 {
118         assert (_certificate);
119         
120         BIO* bio = BIO_new (BIO_s_mem ());
121         if (!bio) {
122                 throw MiscError ("could not create memory BIO");
123         }
124         
125         PEM_write_bio_X509 (bio, _certificate);
126
127         string s;
128         char* data;
129         long int const data_length = BIO_get_mem_data (bio, &data);
130         for (long int i = 0; i < data_length; ++i) {
131                 s += data[i];
132         }
133
134         BIO_free (bio);
135
136         if (!with_begin_end) {
137                 boost::replace_all (s, "-----BEGIN CERTIFICATE-----\n", "");
138                 boost::replace_all (s, "\n-----END CERTIFICATE-----\n", "");
139         }
140         
141         return s;
142 }
143
144 string
145 Certificate::issuer () const
146 {
147         assert (_certificate);
148         return name_for_xml (X509_get_issuer_name (_certificate));
149 }
150
151 string
152 Certificate::asn_to_utf8 (ASN1_STRING* s)
153 {
154         unsigned char* buf = 0;
155         ASN1_STRING_to_UTF8 (&buf, s);
156         string const u (reinterpret_cast<char *> (buf));
157         OPENSSL_free (buf);
158         return u;
159 }
160
161 string
162 Certificate::get_name_part (X509_NAME* n, int nid)
163 {
164         int p = -1;
165         p = X509_NAME_get_index_by_NID (n, nid, p);
166         assert (p != -1);
167         return asn_to_utf8 (X509_NAME_ENTRY_get_data (X509_NAME_get_entry (n, p)));
168 }
169         
170
171 string
172 Certificate::name_for_xml (X509_NAME * n)
173 {
174         assert (n);
175
176         string s = String::compose (
177                 "dnQualifier=%1,CN=%2,OU=%3,O=%4",
178                 get_name_part (n, NID_dnQualifier),
179                 get_name_part (n, NID_commonName),
180                 get_name_part (n, NID_organizationalUnitName),
181                 get_name_part (n, NID_organizationName)
182                 );
183         
184         boost::replace_all (s, "+", "\\+");
185         return s;
186 }
187
188 string
189 Certificate::subject () const
190 {
191         assert (_certificate);
192
193         return name_for_xml (X509_get_subject_name (_certificate));
194 }
195
196 string
197 Certificate::common_name () const
198 {
199         assert (_certificate);
200
201         return get_name_part (X509_get_subject_name (_certificate), NID_commonName);
202 }
203
204 string
205 Certificate::serial () const
206 {
207         assert (_certificate);
208
209         ASN1_INTEGER* s = X509_get_serialNumber (_certificate);
210         assert (s);
211         
212         BIGNUM* b = ASN1_INTEGER_to_BN (s, 0);
213         char* c = BN_bn2dec (b);
214         BN_free (b);
215         
216         string st (c);
217         OPENSSL_free (c);
218
219         return st;
220 }
221
222 string
223 Certificate::thumbprint () const
224 {
225         assert (_certificate);
226         
227         uint8_t buffer[8192];
228         uint8_t* p = buffer;
229         i2d_X509_CINF (_certificate->cert_info, &p);
230         int const length = p - buffer;
231         if (length > 8192) {
232                 throw MiscError ("buffer too small to generate thumbprint");
233         }
234
235         SHA_CTX sha;
236         SHA1_Init (&sha);
237         SHA1_Update (&sha, buffer, length);
238         uint8_t digest[20];
239         SHA1_Final (digest, &sha);
240
241         char digest_base64[64];
242         return Kumu::base64encode (digest, 20, digest_base64, 64);
243 }
244
245 RSA *
246 Certificate::public_key () const
247 {
248         assert (_certificate);
249
250         if (_public_key) {
251                 return _public_key;
252         }
253
254         EVP_PKEY* key = X509_get_pubkey (_certificate);
255         if (!key) {
256                 throw MiscError ("could not get public key from certificate");
257         }
258
259         _public_key = EVP_PKEY_get1_RSA (key);
260         if (!_public_key) {
261                 throw MiscError (String::compose ("could not get RSA public key (%1)", ERR_error_string (ERR_get_error(), 0)));
262         }
263
264         return _public_key;
265 }
266
267 shared_ptr<Certificate>
268 CertificateChain::root () const
269 {
270         assert (!_certificates.empty());
271         return _certificates.front ();
272 }
273
274 shared_ptr<Certificate>
275 CertificateChain::leaf () const
276 {
277         assert (_certificates.size() >= 2);
278         return _certificates.back ();
279 }
280
281 list<shared_ptr<Certificate> >
282 CertificateChain::leaf_to_root () const
283 {
284         list<shared_ptr<Certificate> > c = _certificates;
285         c.reverse ();
286         return c;
287 }
288
289 void
290 CertificateChain::add (shared_ptr<Certificate> c)
291 {
292         _certificates.push_back (c);
293 }