/*************************************************************************** * Copyright (C) gempa GmbH * * All rights reserved. * * Contact: gempa GmbH (seiscomp-dev@gempa.de) * * * * GNU Affero General Public License Usage * * This file may be used under the terms of the GNU Affero * * Public License version 3.0 as published by the Free Software Foundation * * and appearing in the file LICENSE included in the packaging of this * * file. Please review the following information to ensure the GNU Affero * * Public License version 3.0 requirements will be met: * * https://www.gnu.org/licenses/agpl-3.0.html. * * * * Other Usage * * Alternatively, this file may be used in accordance with the terms and * * conditions contained in a signed written agreement between you and * * gempa GmbH. * ***************************************************************************/ #ifndef SEISCOMP_UTILS_CERTIFICATESTORE_H #define SEISCOMP_UTILS_CERTIFICATESTORE_H #include #include #include #include #include #include #include namespace Seiscomp { namespace Util { DEFINE_SMARTPOINTER(CertificateContext); /** * @brief An agency related certificate context. * This class manages information for a particular subject hash. * It saves the last accessed certificate for caching, the list of available * certificates and the certificate revocation list. * * It provides functions to lookup certificates based on a reference time * or a given digest and a reference signature. * * While looking up certificates the revocation list is considered as well. * Revoked certificates will be ignored. */ class SC_SYSTEM_CORE_API CertificateContext : public Core::BaseObject { // ---------------------------------------------------------------------- // X'truction // ---------------------------------------------------------------------- protected: //! C'tor CertificateContext(); public: //! D'tor ~CertificateContext(); // ---------------------------------------------------------------------- // Public interface // ---------------------------------------------------------------------- public: /** * @brief Returns a certficate valid for a given reference time. * @param referenceTime The reference time * @return An X509 certificate or null */ const X509 *findCertificate(const Core::Time &referenceTime) const; /** * @brief Returns a certificate by signing the digest and comparing it * against the reference signature. * @param digest Address pointing to the digest * @param nDigest Number of digest bytes * @param signature OpenSSL ECDSA signature * @return A certficate or null */ const X509 *findCertificate(const char *digest, size_t nDigest, const ECDSA_SIG *signature) const; // /** // * @brief Checks if an certificate has been revoked // * Omit the reference time to disable the time check. // * @param cert The X509 certificate // * @param referenceTime The reference time // * @return // */ // bool isRevoked(const X509 *cert, // const Core::Time &referenceTime = Core::Time()) const; // ---------------------------------------------------------------------- // Members // ---------------------------------------------------------------------- protected: typedef std::map Certs; typedef std::map CRLs; // Cache mutable X509 *_cert; mutable const ASN1_TIME *_begin; mutable const ASN1_TIME *_end; // Certificates and revocation list Certs _certs; CRLs _crls; friend class CertificateStore; }; DEFINE_SMARTPOINTER(CertificateStore); /** * @brief An OpenSSL certificate store. * * @code * CertificateStore store; * if ( !store.init("/path/to/store") ) { * exit(1); * } * * // Get context for authority "gempa" * const CertificateContext *ctx = store.getContext("792241d4"); * if ( !ctx ) { * exit(1); * } * * const X509 *cert = ctx.findCerticiate(Core::Time::GMT()); * if ( !cert ) { * exit(1); * } * * // Do something with the certificate * @endcode */ class SC_SYSTEM_CORE_API CertificateStore : public Core::BaseObject { // ---------------------------------------------------------------------- // X'truction // ---------------------------------------------------------------------- public: //! C'tor CertificateStore(); //! D'tor ~CertificateStore(); // ---------------------------------------------------------------------- // Public interface // ---------------------------------------------------------------------- public: /** * @brief Returns the global certificate store. * The global store is going to be used by components that do not * accept a certificate store as part of their interface. * @return The certificate store reference. */ static CertificateStore &global(); //! Initializes the store and opens the passed directory. bool init(const std::string &baseDirectory); /** * @brief Checks if the certificate store is valid * @return True, if the store is valid */ bool isValid() const; /** * @brief Returns a certificate context for a given authority. * @param hash Pointer to OpenSSL X509 name hash * @param len Number of bytes of authority string * @return The context or null. */ const CertificateContext *getContext(const char *hash, size_t len); /** * @brief Returns a certificate context for a given authority. * @param hash The given OpenSSL X509 name hash * @return The context or null. */ const CertificateContext *getContext(const std::string &hash); /** * @brief Validates an ECDSA signature against the certificates in * the store. * @param authority The authority * @param len Then length of the authority string in bytes * @param digest The digest block * @param nDigest The length of the digest block in bytes * @param signature The ECDSA signature to check against * @param matchedCertificate The matched certificate if requested * @return Success flag */ bool validate(const char *authority, size_t len, const char *digest, size_t nDigest, const ECDSA_SIG *signature, const X509 **matchedCertificate = 0); bool validate(const std::string &authority, const char *digest, size_t nDigest, const ECDSA_SIG *signature, const X509 **matchedCertificate = 0); /** * @brief Loads certificates for a specific hash from directory * @param List in which matching certs should be added to * @param hash The given OpenSSL X509 name hash * @param baseDirectory The base directory used for the * recursive search * @return True on success. */ bool loadCerts(CertificateContext::Certs &certs, const std::string &hash, const std::string &baseDirectory); /** * @brief Loads crls for a specific hash from directory * @param crls List in which matching crls should be added to * @param hash The given OpenSSL X509 name hash * @param baseDirectory The base directory used for the * recursive search * @return True on success */ bool loadCRLs(CertificateContext::CRLs &crls, const std::string &hash, const std::string &baseDirectory); // ---------------------------------------------------------------------- // Members // ---------------------------------------------------------------------- protected: typedef std::map Lookup; static CertificateStore _global; Lookup _lookup; std::mutex _storeMutex; std::string _baseDirectory; }; inline CertificateStore &CertificateStore:: global() { return _global; } inline bool CertificateStore::isValid() const { return !_baseDirectory.empty(); } } } #endif