Merge master; at least partially.
[libdcp.git] / src / certificates.cc
index c1e71b143bd7114aa76a95b9329f754e35d0df48..372f8571a2c6a277062b8326c86b60d2f277bd65 100644 (file)
@@ -1,9 +1,30 @@
+/*
+    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
 #include <sstream>
 #include <vector>
 #include <boost/algorithm/string.hpp>
 #include <openssl/x509.h>
 #include <openssl/ssl.h>
 #include <openssl/asn1.h>
+#include <libxml++/nodes/element.h>
+#include "KM_util.h"
 #include "certificates.h"
 #include "exceptions.h"
 
@@ -21,14 +42,55 @@ Certificate::Certificate (X509* c)
        
 }
 
+Certificate::Certificate (string const & filename)
+       : _certificate (0)
+{
+       FILE* f = fopen (filename.c_str(), "r");
+       if (!f) {
+               throw FileError ("could not open file", filename);
+       }
+       
+       if (!PEM_read_X509 (f, &_certificate, 0, 0)) {
+               throw MiscError ("could not read X509 certificate");
+       }
+}
+
 Certificate::~Certificate ()
 {
        X509_free (_certificate);
 }
 
+string
+Certificate::certificate () const
+{
+       assert (_certificate);
+       
+       BIO* bio = BIO_new (BIO_s_mem ());
+       if (!bio) {
+               throw MiscError ("could not create memory BIO");
+       }
+       
+       PEM_write_bio_X509 (bio, _certificate);
+
+       string s;
+       char* data;
+       long int const data_length = BIO_get_mem_data (bio, &data);
+       for (long int i = 0; i < data_length; ++i) {
+               s += data[i];
+       }
+
+       BIO_free (bio);
+
+       boost::replace_all (s, "-----BEGIN CERTIFICATE-----\n", "");
+       boost::replace_all (s, "\n-----END CERTIFICATE-----\n", "");
+       return s;
+}
+
 string
 Certificate::issuer () const
 {
+       assert (_certificate);
+       
        X509_NAME* n = X509_get_issuer_name (_certificate);
        assert (n);
 
@@ -48,12 +110,17 @@ Certificate::name_for_xml (string const & n)
                x << *i << ",";
        }
 
-       return x.str().substr(0, x.str().length() - 2);
+       string s = x.str();
+       boost::replace_all (s, "+", "\\+");
+
+       return s.substr(0, s.length() - 2);
 }
 
 string
 Certificate::subject () const
 {
+       assert (_certificate);
+
        X509_NAME* n = X509_get_subject_name (_certificate);
        assert (n);
 
@@ -65,6 +132,8 @@ Certificate::subject () const
 string
 Certificate::serial () const
 {
+       assert (_certificate);
+
        ASN1_INTEGER* s = X509_get_serialNumber (_certificate);
        assert (s);
        
@@ -77,32 +146,28 @@ Certificate::serial () const
 
        return st;
 }
-               
-
-/** @param filename Text file of PEM-format certificates,
- *  in the order:
- *
- *  1. self-signed root certificate
- *  2. intermediate certificate signed by root certificate
- *  ...
- *  n. leaf certificate signed by previous intermediate.
- */
-
-CertificateChain::CertificateChain (string const & filename)
+
+string
+Certificate::thumbprint () const
 {
-       FILE* f = fopen (filename.c_str(), "r");
-       if (!f) {
-               throw FileError ("could not open file", filename);
-       }
+       assert (_certificate);
        
-       while (1) {
-               X509* c = 0;
-               if (!PEM_read_X509 (f, &c, 0, 0)) {
-                       break;
-               }
-
-               _certificates.push_back (shared_ptr<Certificate> (new Certificate (c)));
+       uint8_t buffer[8192];
+       uint8_t* p = buffer;
+       i2d_X509_CINF (_certificate->cert_info, &p);
+       int const length = p - buffer;
+       if (length > 8192) {
+               throw MiscError ("buffer too small to generate thumbprint");
        }
+
+       SHA_CTX sha;
+       SHA1_Init (&sha);
+       SHA1_Update (&sha, buffer, length);
+       uint8_t digest[20];
+       SHA1_Final (digest, &sha);
+
+       char digest_base64[64];
+       return Kumu::base64encode (digest, 20, digest_base64, 64);
 }
 
 shared_ptr<Certificate>
@@ -118,3 +183,17 @@ CertificateChain::leaf () const
        assert (_certificates.size() >= 2);
        return _certificates.back ();
 }
+
+list<shared_ptr<Certificate> >
+CertificateChain::leaf_to_root () const
+{
+       list<shared_ptr<Certificate> > c = _certificates;
+       c.reverse ();
+       return c;
+}
+
+void
+CertificateChain::add (shared_ptr<Certificate> c)
+{
+       _certificates.push_back (c);
+}