summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2016-08-25 14:00:33 +0100
committerCarl Hetherington <cth@carlh.net>2016-08-25 14:00:33 +0100
commit14ce6b8765f47db4f2cc4ab4d2eff64486792e62 (patch)
tree7274c9d4b633964c4f54bbe67ba6bd2ca0dfc0a8
parent04def4c193777d7a6cbd306d0a3ba3944335e444 (diff)
Allow reading of certificate chains from strings.
This also makes the Certificate constructor throw if it finds extra stuff after a certificate it is loading.
-rw-r--r--src/certificate.cc25
-rw-r--r--src/certificate.h14
-rw-r--r--src/certificate_chain.cc19
-rw-r--r--src/certificate_chain.h2
-rw-r--r--test/certificates_test.cc24
-rw-r--r--test/round_trip_test.cc2
6 files changed, 53 insertions, 33 deletions
diff --git a/src/certificate.cc b/src/certificate.cc
index a30b77cd..fbe3a80d 100644
--- a/src/certificate.cc
+++ b/src/certificate.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
This file is part of libdcp.
@@ -64,7 +64,6 @@ static string const end_certificate = "-----END CERTIFICATE-----";
Certificate::Certificate (X509* c)
: _certificate (c)
, _public_key (0)
- , _extra_data (false)
{
}
@@ -76,7 +75,10 @@ Certificate::Certificate (string cert)
: _certificate (0)
, _public_key (0)
{
- _extra_data = read_string (cert);
+ string const s = read_string (cert);
+ if (!s.empty ()) {
+ throw MiscError ("unexpected data after certificate");
+ }
}
/** Copy constructor.
@@ -85,7 +87,6 @@ Certificate::Certificate (string cert)
Certificate::Certificate (Certificate const & other)
: _certificate (0)
, _public_key (0)
- , _extra_data (other._extra_data)
{
if (other._certificate) {
read_string (other.certificate (true));
@@ -94,9 +95,9 @@ Certificate::Certificate (Certificate const & other)
/** Read a certificate from a string.
* @param cert String to read.
- * @return true if there is extra stuff after the end of the certificate, false if not.
+ * @return remaining part of the input string after the certificate which was read.
*/
-bool
+string
Certificate::read_string (string cert)
{
/* Reformat cert so that it has line breaks every 64 characters.
@@ -176,11 +177,16 @@ Certificate::read_string (string cert)
BIO_free (bio);
- /* See if there are any non-blank lines after the certificate that we read */
- while (i != lines.end() && i->empty()) {
+ string extra;
+
+ while (i != lines.end()) {
+ if (!i->empty()) {
+ extra += *i + "\n";
+ }
++i;
}
- return i != lines.end();
+
+ return extra;
}
/** Destructor */
@@ -204,7 +210,6 @@ Certificate::operator= (Certificate const & other)
_certificate = 0;
RSA_free (_public_key);
_public_key = 0;
- _extra_data = other._extra_data;
read_string (other.certificate (true));
diff --git a/src/certificate.h b/src/certificate.h
index 6193af8f..8237c739 100644
--- a/src/certificate.h
+++ b/src/certificate.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
This file is part of libdcp.
@@ -63,7 +63,6 @@ public:
Certificate ()
: _certificate (0)
, _public_key (0)
- , _extra_data (false)
{}
explicit Certificate (std::string);
@@ -73,6 +72,8 @@ public:
Certificate& operator= (Certificate const &);
+ std::string read_string (std::string);
+
std::string certificate (bool with_begin_end = false) const;
std::string serial () const;
@@ -91,12 +92,7 @@ public:
std::string thumbprint () const;
- bool extra_data () const {
- return _extra_data;
- }
-
private:
- bool read_string (std::string);
static std::string name_for_xml (X509_NAME *);
static std::string asn_to_utf8 (ASN1_STRING *);
@@ -104,10 +100,6 @@ private:
X509* _certificate;
mutable RSA* _public_key;
- /** true if extra data was found when this certificate was read
- from a string.
- */
- bool _extra_data;
};
bool operator== (Certificate const & a, Certificate const & b);
diff --git a/src/certificate_chain.cc b/src/certificate_chain.cc
index df68cb9f..ac01d860 100644
--- a/src/certificate_chain.cc
+++ b/src/certificate_chain.cc
@@ -55,6 +55,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <fstream>
+#include <iostream>
using std::string;
using std::ofstream;
@@ -312,6 +313,24 @@ CertificateChain::CertificateChain (
boost::filesystem::remove_all (directory);
}
+CertificateChain::CertificateChain (string s)
+{
+ while (true) {
+ try {
+ Certificate c;
+ s = c.read_string (s);
+ _certificates.push_back (c);
+ } catch (MiscError& e) {
+ /* Failed to read a certificate, just stop */
+ break;
+ }
+ }
+
+ if (!attempt_reorder ()) {
+ throw MiscError ("could not find certificate chain order");
+ }
+}
+
/** @return Root certificate */
Certificate
CertificateChain::root () const
diff --git a/src/certificate_chain.h b/src/certificate_chain.h
index ca259c08..9d7ab47c 100644
--- a/src/certificate_chain.h
+++ b/src/certificate_chain.h
@@ -74,6 +74,8 @@ public:
std::string leaf_common_name = "CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION"
);
+ explicit CertificateChain (std::string);
+
void add (Certificate c);
void remove (Certificate c);
void remove (int);
diff --git a/test/certificates_test.cc b/test/certificates_test.cc
index 879a89dd..8ae01eed 100644
--- a/test/certificates_test.cc
+++ b/test/certificates_test.cc
@@ -55,8 +55,6 @@ BOOST_AUTO_TEST_CASE (certificates1)
"dnQualifier=QFVlym7fuql6bPOnY38aaO1ZPW4=,CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
);
- BOOST_CHECK (!c.leaf().extra_data ());
-
++i;
/* Intermediate */
@@ -70,8 +68,6 @@ BOOST_AUTO_TEST_CASE (certificates1)
"dnQualifier=6eat8r33US71avuQEojmH\\+bjk84=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
);
- BOOST_CHECK (!i->extra_data ());
-
++i;
/* Root */
@@ -88,8 +84,6 @@ BOOST_AUTO_TEST_CASE (certificates1)
"dnQualifier=DCnRdHFbcv4ANVUq2\\+wMVALFSec=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
);
- BOOST_CHECK (!c.root().extra_data ());
-
/* Check that reconstruction from a string works */
dcp::Certificate test (c.root().certificate (true));
BOOST_CHECK_EQUAL (test.certificate(), c.root().certificate());
@@ -101,18 +95,16 @@ BOOST_AUTO_TEST_CASE (certificates2)
{
dcp::Certificate c (dcp::file_to_string (private_test / "CA.GDC-TECH.COM_SA2100_A14903.crt.crt"));
BOOST_CHECK_EQUAL (c.certificate(true), dcp::file_to_string (private_test / "CA.GDC-TECH.COM_SA2100_A14903.crt.crt.reformatted"));
- BOOST_CHECK (!c.extra_data ());
}
{
dcp::Certificate c (dcp::file_to_string (private_test / "usl-cert.pem"));
BOOST_CHECK_EQUAL (c.certificate(true), dcp::file_to_string (private_test / "usl-cert.pem.trimmed"));
- BOOST_CHECK (!c.extra_data ());
}
{
- dcp::Certificate c (dcp::file_to_string (private_test / "chain.pem"));
- BOOST_CHECK (c.extra_data ());
+ /* This is a chain, not an individual certificate, so it should throw an exception */
+ BOOST_CHECK_THROW (dcp::Certificate (dcp::file_to_string (private_test / "chain.pem")), dcp::MiscError);
}
BOOST_CHECK_THROW (dcp::Certificate (dcp::file_to_string (private_test / "no-begin.pem")), dcp::MiscError);
@@ -178,7 +170,17 @@ BOOST_AUTO_TEST_CASE (signer_validation)
BOOST_CHECK (chain.valid ());
/* Put in an unrelated key and the signer should no longer be valid */
- dcp::CertificateChain another_chain ("openssl");
+ dcp::CertificateChain another_chain (boost::filesystem::path ("openssl"));
chain.set_key (another_chain.key().get ());
BOOST_CHECK (!chain.valid ());
}
+
+/** Check reading of a certificate chain from a string */
+BOOST_AUTO_TEST_CASE (certificate_chain_from_string)
+{
+ dcp::CertificateChain a (dcp::file_to_string (private_test / "chain.pem"));
+ BOOST_CHECK_EQUAL (a.root_to_leaf().size(), 3);
+
+ dcp::CertificateChain b (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem"));
+ BOOST_CHECK_EQUAL (b.root_to_leaf().size(), 1);
+}
diff --git a/test/round_trip_test.cc b/test/round_trip_test.cc
index f149c5f1..e2b90244 100644
--- a/test/round_trip_test.cc
+++ b/test/round_trip_test.cc
@@ -48,7 +48,7 @@ using boost::scoped_array;
/** Build an encrypted picture asset and a KDM for it and check that the KDM can be decrypted */
BOOST_AUTO_TEST_CASE (round_trip_test)
{
- shared_ptr<dcp::CertificateChain> signer (new dcp::CertificateChain ("openssl"));
+ shared_ptr<dcp::CertificateChain> signer (new dcp::CertificateChain (boost::filesystem::path ("openssl")));
boost::filesystem::path work_dir = "build/test/round_trip_test";
boost::filesystem::create_directory (work_dir);