diff options
| author | Carl Hetherington <cth@carlh.net> | 2017-06-05 14:41:27 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2017-06-05 14:41:27 +0100 |
| commit | 284f2473293bad0b28a7b9abd47e954328a61ab9 (patch) | |
| tree | 493d37c021ea2b1f4da93685d7d65f19b82c50d5 /src | |
| parent | 3a217e6cce46127abaa1463c394d3baf05b02616 (diff) | |
Fix up management of certificate chain validity.
Diffstat (limited to 'src')
| -rw-r--r-- | src/certificate_chain.cc | 148 | ||||
| -rw-r--r-- | src/certificate_chain.h | 23 | ||||
| -rw-r--r-- | src/exceptions.cc | 6 | ||||
| -rw-r--r-- | src/exceptions.h | 6 | ||||
| -rw-r--r-- | src/util.cc | 2 |
5 files changed, 127 insertions, 58 deletions
diff --git a/src/certificate_chain.cc b/src/certificate_chain.cc index d6b8a7a0..3ea6db60 100644 --- a/src/certificate_chain.cc +++ b/src/certificate_chain.cc @@ -327,9 +327,8 @@ CertificateChain::CertificateChain (string s) } } - if (!attempt_reorder ()) { - throw MiscError ("could not find certificate chain order"); - } + /* This will throw an exception if the chain cannot be ordered */ + leaf_to_root (); } /** @return Root certificate */ @@ -337,7 +336,7 @@ Certificate CertificateChain::root () const { DCP_ASSERT (!_certificates.empty()); - return _certificates.front (); + return root_to_leaf().front (); } /** @return Leaf certificate */ @@ -345,26 +344,25 @@ Certificate CertificateChain::leaf () const { DCP_ASSERT (!_certificates.empty()); - return _certificates.back (); + return root_to_leaf().back (); } -/** @return Certificates in order from root to leaf */ +/** @return Certificates in order from leaf to root */ CertificateChain::List -CertificateChain::root_to_leaf () const +CertificateChain::leaf_to_root () const { - return _certificates; + List l = root_to_leaf (); + l.reverse (); + return l; } -/** @return Certificates in order from leaf to root */ CertificateChain::List -CertificateChain::leaf_to_root () const +CertificateChain::unordered () const { - List c = _certificates; - c.reverse (); - return c; + return _certificates; } -/** Add a certificate to the end of the chain. +/** Add a certificate to the chain. * @param c Certificate to add. */ void @@ -399,40 +397,49 @@ CertificateChain::remove (int i) } } -/** Check to see if the chain is valid (i.e. root signs the intermediate, intermediate +bool +CertificateChain::chain_valid () const +{ + return chain_valid (_certificates); +} + +/** Check to see if a chain is valid (i.e. root signs the intermediate, intermediate * signs the leaf and so on) and that the private key (if there is one) matches the * leaf certificate. - * @param valid if non-0 and the CertificateChain is not valid, this is filled in with - * an explanation. * @return true if it's ok, false if not. */ bool -CertificateChain::valid (string* reason) const +CertificateChain::chain_valid (List const & chain) const { - /* Check the certificate chain */ + /* Here I am taking a chain of certificates A/B/C/D and checking validity of B wrt A, + C wrt B and D wrt C. It also appears necessary to check the issuer of B/C/D matches + the subject of A/B/C; I don't understand why. I'm sure there's a better way of doing + this with OpenSSL but the documentation does not appear not likely to reveal it + any time soon. + */ X509_STORE* store = X509_STORE_new (); if (!store) { throw MiscError ("could not create X509 store"); } - int n = 1; - for (List::const_iterator i = _certificates.begin(); i != _certificates.end(); ++i) { + /* Put all the certificates into the store */ + for (List::const_iterator i = chain.begin(); i != chain.end(); ++i) { + if (!X509_STORE_add_cert (store, i->x509 ())) { + X509_STORE_free (store); + return false; + } + } + + /* Verify each one */ + for (List::const_iterator i = chain.begin(); i != chain.end(); ++i) { List::const_iterator j = i; ++j; - if (j == _certificates.end ()) { + if (j == chain.end ()) { break; } - if (!X509_STORE_add_cert (store, i->x509 ())) { - X509_STORE_free (store); - if (reason) { - *reason = "X509_STORE_add_cert failed"; - } - return false; - } - X509_STORE_CTX* ctx = X509_STORE_CTX_new (); if (!ctx) { X509_STORE_free (store); @@ -443,34 +450,46 @@ CertificateChain::valid (string* reason) const if (!X509_STORE_CTX_init (ctx, store, j->x509(), 0)) { X509_STORE_CTX_free (ctx); X509_STORE_free (store); - if (reason) { - *reason = "X509_STORE_CTX_init failed"; - } - return false; + throw MiscError ("could not initialise X509 store context"); } - int v = X509_verify_cert (ctx); + int const v = X509_verify_cert (ctx); X509_STORE_CTX_free (ctx); - if (v == 0) { + if (v != 1) { + X509_STORE_free (store); + return false; + } + + /* I don't know why OpenSSL doesn't check this in verify_cert, but without this check + the certificates_validation8 test fails. + */ + if (j->issuer() != i->subject()) { X509_STORE_free (store); - if (reason) { - *reason = String::compose ("X509_verify_cert failed for certificate number %1", n); - } return false; } - ++n; } X509_STORE_free (store); - /* Check that the leaf certificate matches the private key, if there is one */ + return true; +} - if (!_key) { +/** Check that there is a valid private key for the leaf certificate. + * Will return true if there are no certificates. + */ +bool +CertificateChain::private_key_valid () const +{ + if (_certificates.empty ()) { return true; } + if (!_key) { + return false; + } + BIO* bio = BIO_new_mem_buf (const_cast<char *> (_key->c_str ()), -1); if (!bio) { throw MiscError ("could not create memory BIO"); @@ -491,29 +510,44 @@ CertificateChain::valid (string* reason) const #endif BIO_free (bio); - if (!valid && reason) { - *reason = "leaf certificate does not match private key"; - } - return valid; } -/** @return true if the chain is now in order from root to leaf, - * false if no correct order was found. - */ bool -CertificateChain::attempt_reorder () +CertificateChain::valid (string* reason) const +{ + try { + root_to_leaf (); + } catch (CertificateChainError& e) { + if (reason) { + *reason = "certificates do not form a chain"; + } + return false; + } + + if (!private_key_valid ()) { + if (reason) { + *reason = "private key does not exist, or does not match leaf certificate"; + } + return false; + } + + return true; +} + +/** @return Certificates in order from root to leaf */ +CertificateChain::List +CertificateChain::root_to_leaf () const { - List original = _certificates; - _certificates.sort (); + List rtl = _certificates; + rtl.sort (); do { - if (valid ()) { - return true; + if (chain_valid (rtl)) { + return rtl; } - } while (std::next_permutation (_certificates.begin(), _certificates.end ())); + } while (std::next_permutation (rtl.begin(), rtl.end())); - _certificates = original; - return false; + throw CertificateChainError ("certificate chain is not consistent"); } /** Add a <Signer> and <ds:Signature> nodes to an XML node. diff --git a/src/certificate_chain.h b/src/certificate_chain.h index 657b9ce3..e38123eb 100644 --- a/src/certificate_chain.h +++ b/src/certificate_chain.h @@ -47,6 +47,15 @@ namespace xmlpp { class Node; } +struct certificates_validation1; +struct certificates_validation2; +struct certificates_validation3; +struct certificates_validation4; +struct certificates_validation5; +struct certificates_validation6; +struct certificates_validation7; +struct certificates_validation8; + namespace dcp { /** @class CertificateChain @@ -87,9 +96,11 @@ public: List leaf_to_root () const; List root_to_leaf () const; + List unordered () const; bool valid (std::string* reason = 0) const; - bool attempt_reorder (); + bool chain_valid () const; + bool private_key_valid () const; void sign (xmlpp::Element* parent, Standard standard) const; void add_signature_value (xmlpp::Node* parent, std::string ns) const; @@ -106,6 +117,16 @@ public: private: friend class ::certificates; + friend class ::certificates_validation1; + friend class ::certificates_validation2; + friend class ::certificates_validation3; + friend class ::certificates_validation4; + friend class ::certificates_validation5; + friend class ::certificates_validation6; + friend class ::certificates_validation7; + friend class ::certificates_validation8; + + bool chain_valid (List const & chain) const; /** Our certificates, not in any particular order */ List _certificates; diff --git a/src/exceptions.cc b/src/exceptions.cc index fbd8c85f..51dd6b01 100644 --- a/src/exceptions.cc +++ b/src/exceptions.cc @@ -102,3 +102,9 @@ KDMFormatError::KDMFormatError (std::string message) { } + +CertificateChainError::CertificateChainError (std::string message) + : runtime_error (message) +{ + +} diff --git a/src/exceptions.h b/src/exceptions.h index ed77ae3a..fb17801f 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -185,6 +185,12 @@ public: KDMFormatError (std::string message); }; +class CertificateChainError : public std::runtime_error +{ +public: + CertificateChainError (std::string message); +}; + } #endif diff --git a/src/util.cc b/src/util.cc index 6dde5b37..6f09e8aa 100644 --- a/src/util.cc +++ b/src/util.cc @@ -243,6 +243,8 @@ dcp::init () if (xmlSecCryptoInit() < 0) { throw MiscError ("could not initialise xmlsec-crypto"); } + + OpenSSL_add_all_algorithms(); } /** Decode a base64 string. The base64 decode routine in KM_util.cpp |
