ac7e20f7384ab2193e68975a7d44b8fb9158e024
[libdcp.git] / src / certificates.cc
1 #include <sstream>
2 #include <vector>
3 #include <boost/algorithm/string.hpp>
4 #include <openssl/x509.h>
5 #include <openssl/ssl.h>
6 #include <openssl/asn1.h>
7 #include <libxml++/nodes/element.h>
8 #include "certificates.h"
9 #include "exceptions.h"
10
11 using std::list;
12 using std::string;
13 using std::stringstream;
14 using std::vector;
15 using boost::shared_ptr;
16 using namespace libdcp;
17
18 /** @param c X509 certificate, which this object will take ownership of */
19 Certificate::Certificate (X509* c)
20         : _certificate (c)
21 {
22         
23 }
24
25 Certificate::~Certificate ()
26 {
27         X509_free (_certificate);
28 }
29
30 string
31 Certificate::certificate () const
32 {
33         BIO* bio = BIO_new (BIO_s_mem ());
34         if (!bio) {
35                 throw MiscError ("could not create memory BIO");
36         }
37         
38         PEM_write_bio_X509 (bio, _certificate);
39
40         string s;
41         char* data;
42         long int const data_length = BIO_get_mem_data (bio, &data);
43         for (long int i = 0; i < data_length; ++i) {
44                 s += data[i];
45         }
46
47         BIO_free (bio);
48
49         boost::replace_all (s, "-----BEGIN CERTIFICATE-----\n", "");
50         boost::replace_all (s, "\n-----END CERTIFICATE-----\n", "");
51         return s;
52 }
53
54 string
55 Certificate::issuer () const
56 {
57         X509_NAME* n = X509_get_issuer_name (_certificate);
58         assert (n);
59
60         char b[256];
61         X509_NAME_oneline (n, b, 256);
62         return b;
63 }
64
65 string
66 Certificate::name_for_xml (string const & n)
67 {
68         stringstream x;
69         
70         vector<string> p;
71         boost::split (p, n, boost::is_any_of ("/"));
72         for (vector<string>::const_reverse_iterator i = p.rbegin(); i != p.rend(); ++i) {
73                 x << *i << ",";
74         }
75
76         string s = x.str();
77         boost::replace_all (s, "+", "\\+");
78
79         return s.substr(0, s.length() - 2);
80 }
81
82 string
83 Certificate::subject () const
84 {
85         X509_NAME* n = X509_get_subject_name (_certificate);
86         assert (n);
87
88         char b[256];
89         X509_NAME_oneline (n, b, 256);
90         return b;
91 }
92
93 string
94 Certificate::serial () const
95 {
96         ASN1_INTEGER* s = X509_get_serialNumber (_certificate);
97         assert (s);
98         
99         BIGNUM* b = ASN1_INTEGER_to_BN (s, 0);
100         char* c = BN_bn2dec (b);
101         BN_free (b);
102         
103         string st (c);
104         OPENSSL_free (c);
105
106         return st;
107 }
108
109 /** @param filename Text file of PEM-format certificates,
110  *  in the order:
111  *
112  *  1. self-signed root certificate
113  *  2. intermediate certificate signed by root certificate
114  *  ...
115  *  n. leaf certificate signed by previous intermediate.
116  */
117
118 CertificateChain::CertificateChain (string const & filename)
119 {
120         FILE* f = fopen (filename.c_str(), "r");
121         if (!f) {
122                 throw FileError ("could not open file", filename);
123         }
124         
125         while (1) {
126                 X509* c = 0;
127                 if (!PEM_read_X509 (f, &c, 0, 0)) {
128                         break;
129                 }
130
131                 _certificates.push_back (shared_ptr<Certificate> (new Certificate (c)));
132         }
133 }
134
135 shared_ptr<Certificate>
136 CertificateChain::root () const
137 {
138         assert (!_certificates.empty());
139         return _certificates.front ();
140 }
141
142 shared_ptr<Certificate>
143 CertificateChain::leaf () const
144 {
145         assert (_certificates.size() >= 2);
146         return _certificates.back ();
147 }
148
149 list<shared_ptr<Certificate> >
150 CertificateChain::leaf_to_root () const
151 {
152         list<shared_ptr<Certificate> > c = _certificates;
153         c.reverse ();
154         return c;
155 }