--- /dev/null
+/*
+ Copyright (C) 2013-2014 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.
+
+*/
+
+/** @file src/signer_chain.cc
+ * @brief Functions to make signer chains.
+ */
+
+#include "certificate_chain.h"
+#include "exceptions.h"
+#include "util.h"
+#include "KM_util.h"
+#include <openssl/sha.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <fstream>
+#include <sstream>
+
+using std::string;
+using std::ofstream;
+using std::ifstream;
+using std::stringstream;
+using std::cout;
+
+/** Run a shell command.
+ * @param cmd Command to run (UTF8-encoded).
+ */
+static void
+command (string cmd)
+{
+#ifdef LIBDCP_WINDOWS
+ /* We need to use CreateProcessW on Windows so that the UTF-8/16 mess
+ is handled correctly.
+ */
+ int const wn = MultiByteToWideChar (CP_UTF8, 0, cmd.c_str(), -1, 0, 0);
+ wchar_t* buffer = new wchar_t[wn];
+ if (MultiByteToWideChar (CP_UTF8, 0, cmd.c_str(), -1, buffer, wn) == 0) {
+ delete[] buffer;
+ return;
+ }
+
+ int code = 1;
+
+ STARTUPINFOW startup_info;
+ memset (&startup_info, 0, sizeof (startup_info));
+ startup_info.cb = sizeof (startup_info);
+ PROCESS_INFORMATION process_info;
+
+ /* XXX: this doesn't actually seem to work; failing commands end up with
+ a return code of 0
+ */
+ if (CreateProcessW (0, buffer, 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &startup_info, &process_info)) {
+ WaitForSingleObject (process_info.hProcess, INFINITE);
+ DWORD c;
+ if (GetExitCodeProcess (process_info.hProcess, &c)) {
+ code = c;
+ }
+ CloseHandle (process_info.hProcess);
+ CloseHandle (process_info.hThread);
+ }
+
+ delete[] buffer;
+#else
+ cmd += " 2> /dev/null";
+ int const r = system (cmd.c_str ());
+ int const code = WEXITSTATUS (r);
+#endif
+ if (code) {
+ stringstream s;
+ s << "error " << code << " in " << cmd << " within " << boost::filesystem::current_path();
+ throw dcp::MiscError (s.str());
+ }
+}
+
+/** Extract a public key from a private key and create a SHA1 digest of it.
+ * @param private_key Private key
+ * @param openssl openssl binary name (or full path if openssl is not on the system path).
+ * @return SHA1 digest of corresponding public key, with escaped / characters.
+ */
+static string
+public_key_digest (boost::filesystem::path private_key, boost::filesystem::path openssl)
+{
+ boost::filesystem::path public_name = private_key.string() + ".public";
+
+ /* Create the public key from the private key */
+ stringstream s;
+ s << "\"" << openssl.string() << "\" rsa -outform PEM -pubout -in " << private_key.string() << " -out " << public_name.string ();
+ command (s.str().c_str ());
+
+ /* Read in the public key from the file */
+
+ string pub;
+ ifstream f (public_name.string().c_str ());
+ if (!f.good ()) {
+ throw dcp::MiscError ("public key not found");
+ }
+
+ bool read = false;
+ while (f.good ()) {
+ string line;
+ getline (f, line);
+ if (line.length() >= 10 && line.substr(0, 10) == "-----BEGIN") {
+ read = true;
+ } else if (line.length() >= 8 && line.substr(0, 8) == "-----END") {
+ break;
+ } else if (read) {
+ pub += line;
+ }
+ }
+
+ /* Decode the base64 of the public key */
+
+ unsigned char buffer[512];
+ int const N = dcp::base64_decode (pub, buffer, 1024);
+
+ /* Hash it with SHA1 (without the first 24 bytes, for reasons that are not entirely clear) */
+
+ SHA_CTX context;
+ if (!SHA1_Init (&context)) {
+ throw dcp::MiscError ("could not init SHA1 context");
+ }
+
+ if (!SHA1_Update (&context, buffer + 24, N - 24)) {
+ throw dcp::MiscError ("could not update SHA1 digest");
+ }
+
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ if (!SHA1_Final (digest, &context)) {
+ throw dcp::MiscError ("could not finish SHA1 digest");
+ }
+
+ char digest_base64[64];
+ string dig = Kumu::base64encode (digest, SHA_DIGEST_LENGTH, digest_base64, 64);
+#ifdef LIBDCP_WINDOWS
+ boost::replace_all (dig, "/", "\\/");
+#else
+ boost::replace_all (dig, "/", "\\\\/");
+#endif
+ return dig;
+}
+
+boost::filesystem::path
+dcp::make_certificate_chain (boost::filesystem::path openssl)
+{
+ boost::filesystem::path directory = boost::filesystem::unique_path ();
+ boost::filesystem::create_directories (directory);
+
+ boost::filesystem::path const cwd = boost::filesystem::current_path ();
+ boost::filesystem::current_path (directory);
+
+ string quoted_openssl = "\"" + openssl.string() + "\"";
+
+ command (quoted_openssl + " genrsa -out ca.key 2048");
+
+ {
+ ofstream f ("ca.cnf");
+ f << "[ req ]\n"
+ << "distinguished_name = req_distinguished_name\n"
+ << "x509_extensions = v3_ca\n"
+ << "[ v3_ca ]\n"
+ << "basicConstraints = critical,CA:true,pathlen:3\n"
+ << "keyUsage = keyCertSign,cRLSign\n"
+ << "subjectKeyIdentifier = hash\n"
+ << "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ << "[ req_distinguished_name ]\n"
+ << "O = Unique organization name\n"
+ << "OU = Organization unit\n"
+ << "CN = Entity and dnQualifier\n";
+ }
+
+ string const ca_subject = "/O=example.org/OU=example.org/CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION/dnQualifier=" + public_key_digest ("ca.key", openssl);
+
+ {
+ stringstream c;
+ c << quoted_openssl
+ << " req -new -x509 -sha256 -config ca.cnf -days 3650 -set_serial 5"
+ << " -subj " << ca_subject << " -key ca.key -outform PEM -out ca.self-signed.pem";
+ command (c.str().c_str());
+ }
+
+ command (quoted_openssl + " genrsa -out intermediate.key 2048");
+
+ {
+ ofstream f ("intermediate.cnf");
+ f << "[ default ]\n"
+ << "distinguished_name = req_distinguished_name\n"
+ << "x509_extensions = v3_ca\n"
+ << "[ v3_ca ]\n"
+ << "basicConstraints = critical,CA:true,pathlen:2\n"
+ << "keyUsage = keyCertSign,cRLSign\n"
+ << "subjectKeyIdentifier = hash\n"
+ << "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ << "[ req_distinguished_name ]\n"
+ << "O = Unique organization name\n"
+ << "OU = Organization unit\n"
+ << "CN = Entity and dnQualifier\n";
+ }
+
+ string const inter_subject = "/O=example.org/OU=example.org/CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION/dnQualifier="
+ + public_key_digest ("intermediate.key", openssl);
+
+ {
+ stringstream s;
+ s << quoted_openssl
+ << " req -new -config intermediate.cnf -days 3649 -subj " << inter_subject << " -key intermediate.key -out intermediate.csr";
+ command (s.str().c_str());
+ }
+
+
+ command (
+ quoted_openssl +
+ " x509 -req -sha256 -days 3649 -CA ca.self-signed.pem -CAkey ca.key -set_serial 6"
+ " -in intermediate.csr -extfile intermediate.cnf -extensions v3_ca -out intermediate.signed.pem"
+ );
+
+ command (quoted_openssl + " genrsa -out leaf.key 2048");
+
+ {
+ ofstream f ("leaf.cnf");
+ f << "[ default ]\n"
+ << "distinguished_name = req_distinguished_name\n"
+ << "x509_extensions = v3_ca\n"
+ << "[ v3_ca ]\n"
+ << "basicConstraints = critical,CA:false\n"
+ << "keyUsage = digitalSignature,keyEncipherment\n"
+ << "subjectKeyIdentifier = hash\n"
+ << "authorityKeyIdentifier = keyid,issuer:always\n"
+ << "[ req_distinguished_name ]\n"
+ << "O = Unique organization name\n"
+ << "OU = Organization unit\n"
+ << "CN = Entity and dnQualifier\n";
+ }
+
+ string const leaf_subject = "/O=example.org/OU=example.org/CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION/dnQualifier="
+ + public_key_digest ("leaf.key", openssl);
+
+ {
+ stringstream s;
+ s << quoted_openssl << " req -new -config leaf.cnf -days 3648 -subj " << leaf_subject << " -key leaf.key -outform PEM -out leaf.csr";
+ command (s.str().c_str());
+ }
+
+ command (
+ quoted_openssl +
+ " x509 -req -sha256 -days 3648 -CA intermediate.signed.pem -CAkey intermediate.key"
+ " -set_serial 7 -in leaf.csr -extfile leaf.cnf -extensions v3_ca -out leaf.signed.pem"
+ );
+
+ boost::filesystem::current_path (cwd);
+
+ return directory;
+}
--- /dev/null
+/*
+ Copyright (C) 2013-2014 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.
+
+*/
+
+/** @file src/signer_chain.h
+ * @brief Functions to make signer chains.
+ */
+
+#include <boost/filesystem.hpp>
+
+namespace dcp {
+
+/** Create a chain of certificates for signing things.
+ * @param openssl Name of openssl binary (if it is on the path) or full path.
+ * @return Directory (which should be deleted by the caller) containing:
+ * - ca.self-signed.pem self-signed root certificate
+ * - intermediate.signed.pem intermediate certificate
+ * - leaf.key leaf certificate private key
+ * - leaf.signed.pem leaf certificate
+ */
+boost::filesystem::path make_certificate_chain (boost::filesystem::path openssl);
+
+}
}
/** @return Root certificate */
-shared_ptr<Certificate>
+shared_ptr<const Certificate>
CertificateChain::root () const
{
assert (!_certificates.empty());
}
/** @return Leaf certificate */
-shared_ptr<Certificate>
+shared_ptr<const Certificate>
CertificateChain::leaf () const
{
assert (_certificates.size() >= 2);
* @param c Certificate to add.
*/
void
-CertificateChain::add (shared_ptr<Certificate> c)
+CertificateChain::add (shared_ptr<const Certificate> c)
{
_certificates.push_back (c);
}
void
-CertificateChain::remove (shared_ptr<Certificate> c)
+CertificateChain::remove (shared_ptr<const Certificate> c)
{
_certificates.remove (c);
}
}
}
-/** Verify the chain.
+/** Check to see if the chain is valid (i.e. root signs the intermediate, intermediate
+ * signs the leaf and so on).
* @return true if it's ok, false if not.
*/
bool
-CertificateChain::verify () const
+CertificateChain::valid () const
{
X509_STORE* store = X509_STORE_new ();
if (!store) {
List original = _certificates;
_certificates.sort ();
do {
- if (verify ()) {
+ if (valid ()) {
return true;
}
} while (std::next_permutation (_certificates.begin(), _certificates.end ()));
public:
CertificateChain () {}
- void add (boost::shared_ptr<Certificate> c);
- void remove (boost::shared_ptr<Certificate> c);
+ void add (boost::shared_ptr<const Certificate> c);
+ void remove (boost::shared_ptr<const Certificate> c);
void remove (int);
- boost::shared_ptr<Certificate> root () const;
- boost::shared_ptr<Certificate> leaf () const;
+ boost::shared_ptr<const Certificate> root () const;
+ boost::shared_ptr<const Certificate> leaf () const;
- typedef std::list<boost::shared_ptr<Certificate> > List;
+ typedef std::list<boost::shared_ptr<const Certificate> > List;
List leaf_to_root () const;
List root_to_leaf () const;
- bool verify () const;
+ bool valid () const;
bool attempt_reorder ();
private:
return g;
}
-DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path private_key)
+DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key)
{
/* Read the private key */
-
- FILE* private_key_file = fopen_boost (private_key, "r");
- if (!private_key_file) {
- throw FileError ("could not find RSA private key file", private_key, errno);
+
+ BIO* bio = BIO_new_mem_buf (const_cast<char *> (private_key.c_str ()), -1);
+ if (!bio) {
+ throw MiscError ("could not create memory BIO");
}
- RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
- fclose (private_key_file);
+ RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
if (!rsa) {
throw FileError ("could not read RSA private key file", private_key, errno);
}
}
RSA_free (rsa);
+ BIO_free (bio);
}
DecryptedKDM::DecryptedKDM (
{
public:
/** @param kdm Encrypted KDM.
- * @param private_key Private key file name.
+ * @param private_key Private key as a PEM-format string.
*/
- DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path private_key);
+ DecryptedKDM (EncryptedKDM const & kdm, std::string private_key);
/** Construct a DecryptedKDM.
* @param cpl CPL that the keys are for.
#include "signer.h"
#include "exceptions.h"
+#include "certificate_chain.h"
+#include "util.h"
#include <libcxml/cxml.h>
#include <libxml++/libxml++.h>
#include <xmlsec/xmldsig.h>
using boost::shared_ptr;
using namespace dcp;
+Signer::Signer (boost::filesystem::path openssl)
+{
+ boost::filesystem::path directory = make_certificate_chain (openssl);
+
+ _certificates.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (directory / "ca.self-signed.pem")));
+ _certificates.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (directory / "intermediate.signed.pem")));
+ _certificates.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (directory / "leaf.signed.pem")));
+
+ _key = dcp::file_to_string (directory / "leaf.key");
+
+ boost::filesystem::remove_all (directory);
+}
+
+
/** Add a <Signer> and <ds:Signature> nodes to an XML node.
* @param parent XML node to add to.
* @param standard INTEROP or SMPTE.
xmlpp::Node* key_info = cp.node_child("KeyInfo")->node ();
/* Add the certificate chain to the KeyInfo child node of parent */
- list<shared_ptr<Certificate> > c = _certificates.leaf_to_root ();
- for (list<shared_ptr<Certificate> >::iterator i = c.begin(); i != c.end(); ++i) {
+ CertificateChain::List c = _certificates.leaf_to_root ();
+ for (CertificateChain::List::iterator i = c.begin(); i != c.end(); ++i) {
xmlpp::Element* data = key_info->add_child("X509Data", ns);
{
xmlSecDSigCtxDestroy (signature_context);
}
+
+bool
+Signer::valid () const
+{
+ if (!_certificates.valid ()) {
+ return false;
+ }
+
+ BIO* bio = BIO_new_mem_buf (const_cast<char *> (_key.c_str ()), -1);
+ if (!bio) {
+ throw MiscError ("could not create memory BIO");
+ }
+
+ RSA* private_key = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
+ RSA* public_key = _certificates.leaf()->public_key ();
+ bool const valid = !BN_cmp (private_key->n, public_key->n);
+ BIO_free (bio);
+
+ return valid;
+}
/** @class Signer
* @brief A class which can sign XML files.
*/
-class Signer : public boost::noncopyable
+class Signer
{
public:
+ Signer (boost::filesystem::path openssl);
+
/** @param c Certificate chain to sign with.
* @param k Key to sign with as a PEM-format string.
*/
CertificateChain const & certificates () const {
return _certificates;
}
+
+ CertificateChain& certificates () {
+ return _certificates;
+ }
+
+ std::string key () const {
+ return _key;
+ }
+
+ void set_key (std::string k) {
+ _key = k;
+ }
+
+ bool valid () const;
private:
+++ /dev/null
-/*
- Copyright (C) 2013-2014 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.
-
-*/
-
-/** @file src/signer_chain.cc
- * @brief Functions to make signer chains.
- */
-
-#include "signer_chain.h"
-#include "exceptions.h"
-#include "util.h"
-#include "KM_util.h"
-#include <openssl/sha.h>
-#include <openssl/bio.h>
-#include <openssl/evp.h>
-#include <boost/filesystem.hpp>
-#include <boost/algorithm/string.hpp>
-#include <fstream>
-#include <sstream>
-
-using std::string;
-using std::ofstream;
-using std::ifstream;
-using std::stringstream;
-using std::cout;
-
-/** Run a shell command.
- * @param cmd Command to run (UTF8-encoded).
- */
-static void
-command (string cmd)
-{
-#ifdef LIBDCP_WINDOWS
- /* We need to use CreateProcessW on Windows so that the UTF-8/16 mess
- is handled correctly.
- */
- int const wn = MultiByteToWideChar (CP_UTF8, 0, cmd.c_str(), -1, 0, 0);
- wchar_t* buffer = new wchar_t[wn];
- if (MultiByteToWideChar (CP_UTF8, 0, cmd.c_str(), -1, buffer, wn) == 0) {
- delete[] buffer;
- return;
- }
-
- int code = 1;
-
- STARTUPINFOW startup_info;
- memset (&startup_info, 0, sizeof (startup_info));
- startup_info.cb = sizeof (startup_info);
- PROCESS_INFORMATION process_info;
-
- /* XXX: this doesn't actually seem to work; failing commands end up with
- a return code of 0
- */
- if (CreateProcessW (0, buffer, 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &startup_info, &process_info)) {
- WaitForSingleObject (process_info.hProcess, INFINITE);
- DWORD c;
- if (GetExitCodeProcess (process_info.hProcess, &c)) {
- code = c;
- }
- CloseHandle (process_info.hProcess);
- CloseHandle (process_info.hThread);
- }
-
- delete[] buffer;
-#else
- cmd += " 2> /dev/null";
- int const r = system (cmd.c_str ());
- int const code = WEXITSTATUS (r);
-#endif
- if (code) {
- stringstream s;
- s << "error " << code << " in " << cmd << " within " << boost::filesystem::current_path();
- throw dcp::MiscError (s.str());
- }
-}
-
-/** Extract a public key from a private key and create a SHA1 digest of it.
- * @param private_key Private key
- * @param openssl openssl binary name (or full path if openssl is not on the system path).
- * @return SHA1 digest of corresponding public key, with escaped / characters.
- */
-static string
-public_key_digest (boost::filesystem::path private_key, boost::filesystem::path openssl)
-{
- boost::filesystem::path public_name = private_key.string() + ".public";
-
- /* Create the public key from the private key */
- stringstream s;
- s << "\"" << openssl.string() << "\" rsa -outform PEM -pubout -in " << private_key.string() << " -out " << public_name.string ();
- command (s.str().c_str ());
-
- /* Read in the public key from the file */
-
- string pub;
- ifstream f (public_name.string().c_str ());
- if (!f.good ()) {
- throw dcp::MiscError ("public key not found");
- }
-
- bool read = false;
- while (f.good ()) {
- string line;
- getline (f, line);
- if (line.length() >= 10 && line.substr(0, 10) == "-----BEGIN") {
- read = true;
- } else if (line.length() >= 8 && line.substr(0, 8) == "-----END") {
- break;
- } else if (read) {
- pub += line;
- }
- }
-
- /* Decode the base64 of the public key */
-
- unsigned char buffer[512];
- int const N = dcp::base64_decode (pub, buffer, 1024);
-
- /* Hash it with SHA1 (without the first 24 bytes, for reasons that are not entirely clear) */
-
- SHA_CTX context;
- if (!SHA1_Init (&context)) {
- throw dcp::MiscError ("could not init SHA1 context");
- }
-
- if (!SHA1_Update (&context, buffer + 24, N - 24)) {
- throw dcp::MiscError ("could not update SHA1 digest");
- }
-
- unsigned char digest[SHA_DIGEST_LENGTH];
- if (!SHA1_Final (digest, &context)) {
- throw dcp::MiscError ("could not finish SHA1 digest");
- }
-
- char digest_base64[64];
- string dig = Kumu::base64encode (digest, SHA_DIGEST_LENGTH, digest_base64, 64);
-#ifdef LIBDCP_WINDOWS
- boost::replace_all (dig, "/", "\\/");
-#else
- boost::replace_all (dig, "/", "\\\\/");
-#endif
- return dig;
-}
-
-/** Generate a chain of root, intermediate and leaf keys by running an OpenSSL binary.
- * @param directory Directory to write the files to.
- * @param openssl openssl binary path.
- */
-void
-dcp::make_signer_chain (boost::filesystem::path directory, boost::filesystem::path openssl)
-{
- boost::filesystem::path const cwd = boost::filesystem::current_path ();
-
- string quoted_openssl = "\"" + openssl.string() + "\"";
-
- boost::filesystem::current_path (directory);
- command (quoted_openssl + " genrsa -out ca.key 2048");
-
- {
- ofstream f ("ca.cnf");
- f << "[ req ]\n"
- << "distinguished_name = req_distinguished_name\n"
- << "x509_extensions = v3_ca\n"
- << "[ v3_ca ]\n"
- << "basicConstraints = critical,CA:true,pathlen:3\n"
- << "keyUsage = keyCertSign,cRLSign\n"
- << "subjectKeyIdentifier = hash\n"
- << "authorityKeyIdentifier = keyid:always,issuer:always\n"
- << "[ req_distinguished_name ]\n"
- << "O = Unique organization name\n"
- << "OU = Organization unit\n"
- << "CN = Entity and dnQualifier\n";
- }
-
- string const ca_subject = "/O=example.org/OU=example.org/CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION/dnQualifier=" + public_key_digest ("ca.key", openssl);
-
- {
- stringstream c;
- c << quoted_openssl
- << " req -new -x509 -sha256 -config ca.cnf -days 3650 -set_serial 5"
- << " -subj " << ca_subject << " -key ca.key -outform PEM -out ca.self-signed.pem";
- command (c.str().c_str());
- }
-
- command (quoted_openssl + " genrsa -out intermediate.key 2048");
-
- {
- ofstream f ("intermediate.cnf");
- f << "[ default ]\n"
- << "distinguished_name = req_distinguished_name\n"
- << "x509_extensions = v3_ca\n"
- << "[ v3_ca ]\n"
- << "basicConstraints = critical,CA:true,pathlen:2\n"
- << "keyUsage = keyCertSign,cRLSign\n"
- << "subjectKeyIdentifier = hash\n"
- << "authorityKeyIdentifier = keyid:always,issuer:always\n"
- << "[ req_distinguished_name ]\n"
- << "O = Unique organization name\n"
- << "OU = Organization unit\n"
- << "CN = Entity and dnQualifier\n";
- }
-
- string const inter_subject = "/O=example.org/OU=example.org/CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION/dnQualifier="
- + public_key_digest ("intermediate.key", openssl);
-
- {
- stringstream s;
- s << quoted_openssl
- << " req -new -config intermediate.cnf -days 3649 -subj " << inter_subject << " -key intermediate.key -out intermediate.csr";
- command (s.str().c_str());
- }
-
-
- command (
- quoted_openssl +
- " x509 -req -sha256 -days 3649 -CA ca.self-signed.pem -CAkey ca.key -set_serial 6"
- " -in intermediate.csr -extfile intermediate.cnf -extensions v3_ca -out intermediate.signed.pem"
- );
-
- command (quoted_openssl + " genrsa -out leaf.key 2048");
-
- {
- ofstream f ("leaf.cnf");
- f << "[ default ]\n"
- << "distinguished_name = req_distinguished_name\n"
- << "x509_extensions = v3_ca\n"
- << "[ v3_ca ]\n"
- << "basicConstraints = critical,CA:false\n"
- << "keyUsage = digitalSignature,keyEncipherment\n"
- << "subjectKeyIdentifier = hash\n"
- << "authorityKeyIdentifier = keyid,issuer:always\n"
- << "[ req_distinguished_name ]\n"
- << "O = Unique organization name\n"
- << "OU = Organization unit\n"
- << "CN = Entity and dnQualifier\n";
- }
-
- string const leaf_subject = "/O=example.org/OU=example.org/CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION/dnQualifier="
- + public_key_digest ("leaf.key", openssl);
-
- {
- stringstream s;
- s << quoted_openssl << " req -new -config leaf.cnf -days 3648 -subj " << leaf_subject << " -key leaf.key -outform PEM -out leaf.csr";
- command (s.str().c_str());
- }
-
- command (
- quoted_openssl +
- " x509 -req -sha256 -days 3648 -CA intermediate.signed.pem -CAkey intermediate.key"
- " -set_serial 7 -in leaf.csr -extfile leaf.cnf -extensions v3_ca -out leaf.signed.pem"
- );
-
- boost::filesystem::current_path (cwd);
-}
+++ /dev/null
-/*
- Copyright (C) 2013-2014 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.
-
-*/
-
-/** @file src/signer_chain.h
- * @brief Functions to make signer chains.
- */
-
-#include <boost/filesystem.hpp>
-
-namespace dcp {
-
-/** Create a chain of certificates for signing things.
- * @param directory Directory to write files to.
- * @param openssl Name of openssl binary (if it is on the path) or full path.
- */
-void make_signer_chain (boost::filesystem::path directory, boost::filesystem::path openssl);
-
-}
obj.source = """
argb_frame.cc
asset.cc
+ certificate_chain.cc
certificates.cc
colour_matrix.cc
content.cc
reel_subtitle_asset.cc
rgb_xyz.cc
signer.cc
- signer_chain.cc
sound_mxf.cc
sound_mxf_writer.cc
sound_frame.cc
headers = """
asset.h
+ certificate_chain.h
certificates.h
colour_matrix.h
cpl.h
ref.h
argb_frame.h
signer.h
- signer_chain.h
sound_frame.h
sound_mxf.h
sound_mxf_writer.h
#include <boost/test/unit_test.hpp>
#include "certificates.h"
+#include "signer.h"
+#include "util.h"
using std::list;
+using std::cout;
+using std::string;
using boost::shared_ptr;
BOOST_AUTO_TEST_CASE (certificates)
c.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
c.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
- list<shared_ptr<dcp::Certificate> > leaf_to_root = c.leaf_to_root ();
+ dcp::CertificateChain::List leaf_to_root = c.leaf_to_root ();
- list<shared_ptr<dcp::Certificate> >::iterator i = leaf_to_root.begin ();
+ dcp::CertificateChain::List::iterator i = leaf_to_root.begin ();
/* Leaf */
BOOST_CHECK_EQUAL (*i, c.leaf ());
BOOST_CHECK_EQUAL (test.certificate(), c.root()->certificate());
}
-/** Check that dcp::CertificateChain::validate() and ::attempt_reorder() basically work */
+/** Check that dcp::CertificateChain::valid() and ::attempt_reorder() basically work */
BOOST_AUTO_TEST_CASE (certificates_validation)
{
dcp::CertificateChain good1;
good1.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
good1.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
good1.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
- BOOST_CHECK (good1.verify ());
+ BOOST_CHECK (good1.valid ());
dcp::CertificateChain good2;
good2.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
- BOOST_CHECK (good2.verify ());
+ BOOST_CHECK (good2.valid ());
dcp::CertificateChain bad1;
bad1.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
bad1.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
- BOOST_CHECK (!bad1.verify ());
+ BOOST_CHECK (!bad1.valid ());
BOOST_CHECK (!bad1.attempt_reorder ());
dcp::CertificateChain bad2;
bad2.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
bad2.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
bad2.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
- BOOST_CHECK (!bad2.verify ());
+ BOOST_CHECK (!bad2.valid ());
BOOST_CHECK (bad2.attempt_reorder ());
dcp::CertificateChain bad3;
bad3.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
bad3.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
bad3.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
- BOOST_CHECK (!bad3.verify ());
+ BOOST_CHECK (!bad3.valid ());
BOOST_CHECK (bad3.attempt_reorder ());
dcp::CertificateChain bad4;
bad4.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
bad4.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
bad4.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
- BOOST_CHECK (!bad4.verify ());
+ BOOST_CHECK (!bad4.valid ());
BOOST_CHECK (bad4.attempt_reorder ());
dcp::CertificateChain bad5;
bad5.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
bad5.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
- BOOST_CHECK (!bad5.verify ());
+ BOOST_CHECK (!bad5.valid ());
BOOST_CHECK (!bad5.attempt_reorder ());
}
+
+/** Check that dcp::Signer::valid() basically works */
+BOOST_AUTO_TEST_CASE (signer_validation)
+{
+ /* Check a valid signer */
+ dcp::CertificateChain chain;
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
+ dcp::Signer signer (chain, dcp::file_to_string ("test/ref/crypt/leaf.key"));
+ BOOST_CHECK (signer.valid ());
+
+ /* Put in an unrelated key and the signer should no longer be valid */
+ dcp::Signer another_signer ("openssl");
+ signer.set_key (another_signer.key ());
+ BOOST_CHECK (!signer.valid ());
+}
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 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
dcp::DecryptedKDM kdm (
dcp::EncryptedKDM ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"),
- "test/data/private.key"
+ dcp::file_to_string ("test/data/private.key")
);
encrypted.add (kdm);
{
dcp::DecryptedKDM kdm (
dcp::EncryptedKDM ("test/data/target.pem.crt.de5d4eba-e683-41ca-bdda-aa4ad96af3f4.kdm.xml"),
- "test/data/private.key"
+ dcp::file_to_string ("test/data/private.key")
);
}
#include "reel.h"
#include "test.h"
#include "file.h"
-#include "signer_chain.h"
#include "subtitle_content.h"
#include "reel_mono_picture_asset.h"
#include "reel_sound_asset.h"
{
boost::filesystem::remove_all ("build/test/signer");
boost::filesystem::create_directory ("build/test/signer");
- dcp::make_signer_chain ("build/test/signer", "openssl");
Kumu::libdcp_test = true;
#include <libxml++/libxml++.h>
#include "encrypted_kdm.h"
#include "decrypted_kdm.h"
+#include "util.h"
using std::list;
using std::stringstream;
{
dcp::DecryptedKDM kdm (
dcp::EncryptedKDM ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"),
- "test/data/private.key"
+ dcp::file_to_string ("test/data/private.key")
);
list<dcp::DecryptedKDMKey> keys = kdm.keys ();
#include "cpl.h"
#include "mono_picture_frame.h"
#include "argb_frame.h"
-#include "signer_chain.h"
+#include "certificate_chain.h"
#include "mono_picture_mxf_writer.h"
#include "reel_picture_asset.h"
#include "reel_mono_picture_asset.h"
/* Build an encrypted picture MXF and a KDM for it and check that the KDM can be decrypted */
BOOST_AUTO_TEST_CASE (round_trip_test)
{
- boost::filesystem::remove_all ("build/test/signer");
- boost::filesystem::create_directory ("build/test/signer");
- dcp::make_signer_chain ("build/test/signer", "openssl");
-
- dcp::CertificateChain chain;
- chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/ca.self-signed.pem"))));
- chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/intermediate.signed.pem"))));
- chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/leaf.signed.pem"))));
-
- shared_ptr<dcp::Signer> signer (
- new dcp::Signer (
- chain,
- dcp::file_to_string ("test/data/signer.key")
- )
- );
+ shared_ptr<dcp::Signer> signer (new dcp::Signer ("openssl"));
boost::filesystem::path work_dir = "build/test/round_trip_test";
boost::filesystem::create_directory (work_dir);
kdm_A.encrypt(signer, signer->certificates().leaf(), dcp::MODIFIED_TRANSITIONAL_1).as_xml (kdm_file);
/* Reload the KDM, using our private key to decrypt it */
- dcp::DecryptedKDM kdm_B (dcp::EncryptedKDM (kdm_file), "build/test/signer/leaf.key");
+ dcp::DecryptedKDM kdm_B (dcp::EncryptedKDM (kdm_file), signer->key ());
/* Check that the decrypted KDMKeys are the same as the ones we started with */
BOOST_CHECK_EQUAL (kdm_A.keys().size(), kdm_B.keys().size());