// ============================================================================ // // = LIBRARY // ULib - c++ library // // = FILENAME // certificate.h - interface to a X509 certificate // // = AUTHOR // Stefano Casazza // // ============================================================================ #ifndef ULIB_CERTIFICATE_H #define ULIB_CERTIFICATE_H 1 #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x10100000L # define X509_get_notBefore X509_getm_notBefore # define X509_get_notAfter X509_getm_notAfter #endif template class UVector; template class UHashMap; /** * This class provides all the services the openssl X509 certificate supports. * This class contains a openssl X509 structure and basically acts as a wrapper to functions that act on that structure */ class U_EXPORT UCertificate { public: // Check for memory error U_MEMORY_TEST // Allocator e Deallocator U_MEMORY_ALLOCATOR U_MEMORY_DEALLOCATOR /** * Constructs this object takes X509 as type */ UCertificate(X509* px509 = U_NULLPTR) : x509(px509) { U_TRACE_CTOR(0, UCertificate, "%p", px509) } /** * Constructs this object from the a encoded string. * The type specifies the type of encoding the string is in, e.g. DER or PEM * * @param encoding a string of bytes * @param type the Certificate's encoding type */ static X509* readX509(const UString& x, const char* format = U_NULLPTR); UCertificate(const UString& x, const char* format = U_NULLPTR) { U_TRACE_CTOR(0, UCertificate, "%V,%S", x.rep, format) x509 = readX509(x, format); } /** * Deletes this object */ void clear() { U_TRACE_NO_PARAM(0, "UCertificate::clear()") U_INTERNAL_ASSERT_POINTER(x509) U_SYSCALL_VOID(X509_free, "%p", x509); x509 = U_NULLPTR; } ~UCertificate() { U_TRACE_DTOR(0, UCertificate) if (x509) clear(); } bool isValid() const { U_TRACE_NO_PARAM(0, "UCertificate::isValid()") if (x509) U_RETURN(true); U_RETURN(false); } bool isSelfSigned() const { return isIssued(*this); } bool isSameIssuerAndSubject() const; void set(X509* px509) { U_TRACE(0, "UCertificate::set(%p)", px509) if (isValid()) clear(); if (px509) x509 = X509_dup(px509); } bool set(const UString& x, const char* format = U_NULLPTR) { U_TRACE(0, "UCertificate::set(%V,%S)", x.rep, format) set(readX509(x, format)); if (x509) U_RETURN(true); U_RETURN(false); } /** * Duplicate this object */ void duplicate() { U_TRACE_NO_PARAM(0, "UCertificate::duplicate()") x509 = X509_dup(x509); } X509* getX509() const { return x509; } /** * Returns the file name from CApath */ static UString getFileName(long hash, bool crl = false, bool* exist = U_NULLPTR); static UString getFileName(X509* _x509) { U_TRACE(0, "UCertificate::getFileName(%p)", _x509) U_INTERNAL_ASSERT_POINTER(_x509) long hash = U_SYSCALL(X509_subject_name_hash, "%p", _x509); return getFileName(hash, false, U_NULLPTR); } UString getFileName() const { return getFileName(x509); } /** * Indicate if the certificate has been issued by a CA * @param ca the CA we want to know if it has issued the certificate * @return true if ca has issued the certificate, else false */ bool isIssued(const UCertificate& ca) const; /** * Returns CA that signed this certificate */ static UString getName(X509_NAME* n, bool bldap = false); static UString getIssuer(X509* _x509, bool bldap = false) { U_TRACE(1, "UCertificate::getIssuer(%p,%b)", _x509, bldap) U_INTERNAL_ASSERT_POINTER(_x509) X509_NAME* n = (X509_NAME*) U_SYSCALL(X509_get_issuer_name, "%p", _x509); return getName(n, bldap); } UString getIssuer(bool bldap = false) const { return getIssuer(x509, bldap); } UString getIssuerForLDAP() const { return getIssuer(true); } /** * Returns subject of this certificate */ static UString getSubject(X509* _x509, bool bldap = false) { U_TRACE(1, "UCertificate::getSubject(%p,%b)", _x509, bldap) U_INTERNAL_ASSERT_POINTER(_x509) X509_NAME* n = (X509_NAME*) U_SYSCALL(X509_get_subject_name, "%p", _x509); return getName(n, bldap); } UString getSubject(bool bldap = false) const { return getSubject(x509, bldap); } UString getSubjectForLDAP() const { return getSubject(true); } /** * Returns the versionNumber of this certificate */ static long getVersionNumber(X509* a) { U_TRACE(1, "UCertificate::getVersionNumber(%p)", a) U_INTERNAL_ASSERT_POINTER(a) long version = U_SYSCALL(X509_get_version, "%p", a); U_RETURN(version); } long getVersionNumber() const { return getVersionNumber(x509); } /** * Returns the serialNumber of this certificate, which together with * the issuing CA's name, uniquely identifies this certificate */ static long getSerialNumber(X509* a) { U_TRACE(0, "UCertificate::getSerialNumber(%p)", a) U_INTERNAL_ASSERT_POINTER(a) long serial = ASN1_INTEGER_get( X509_get_serialNumber(a) ); U_RETURN(serial); } long getSerialNumber() const { return getSerialNumber(x509); } static UString checkForSerialNumber(long number); static UString checkForSerialNumber(const char* str) { U_TRACE(0, "UCertificate::checkForSerialNumber(%S)", str) U_INTERNAL_ASSERT_POINTER(str) return checkForSerialNumber(strtol(str, U_NULLPTR, 0)); } static long hashCode(X509* a) { U_TRACE(1, "UCertificate::hashCode(%p)", a) U_INTERNAL_ASSERT_POINTER(a) long hash = U_SYSCALL(X509_subject_name_hash, "%p", a); U_RETURN(hash); } long hashCode() { return hashCode(x509); } /** * Returns publicKey of this certificate */ static EVP_PKEY* getSubjectPublicKey(X509* a) { U_TRACE(1, "UCertificate::getSubjectPublicKey(%p)", a) U_INTERNAL_ASSERT_POINTER(a) EVP_PKEY* evp = (EVP_PKEY*) U_SYSCALL(X509_get_pubkey, "%p", a); U_RETURN_POINTER(evp, EVP_PKEY); } EVP_PKEY* getSubjectPublicKey() const { return getSubjectPublicKey(x509); } /** * Returns if a private key matches the public key of this certificate */ #ifdef HAVE_OPENSSL_98 static bool matchPrivateKey(X509* a, EVP_PKEY* privateKey) { U_TRACE(1, "UCertificate::matchPrivateKey(%p,%p)", a, privateKey) U_INTERNAL_ASSERT_POINTER(a) EVP_PKEY* publicKey = (EVP_PKEY*) U_SYSCALL(X509_get_pubkey, "%p", a); if (U_SYSCALL(EVP_PKEY_cmp, "%p,%p", publicKey, privateKey) == 1) U_RETURN(true); U_RETURN(false); } bool matchPrivateKey(EVP_PKEY* privateKey) const { return matchPrivateKey(x509, privateKey); } #endif /** * Returns the earliest time that the certificate is valid */ const char* getNotBefore() const __pure { U_TRACE_NO_PARAM(0, "UCertificate::getNotBefore()") U_INTERNAL_ASSERT_POINTER(x509) ASN1_UTCTIME* utctime = X509_get_notBefore(x509); U_RETURN((const char*)utctime->data); } /** * Returns the latest time that the certificate is valid */ const char* getNotAfter() const __pure { U_TRACE_NO_PARAM(0, "UCertificate::getNotAfter()") U_INTERNAL_ASSERT_POINTER(x509) ASN1_UTCTIME* utctime = X509_get_notAfter(x509); U_RETURN((const char*)utctime->data); } /** * Checks validity of this certificate comparing to current time */ bool checkValidity() const { U_TRACE_NO_PARAM(0, "UCertificate::checkValidity()") U_gettimeofday // NB: optimization if it is enough a time resolution of one second... if (checkValidity(u_now->tv_sec)) U_RETURN(true); U_RETURN(false); } /** * Checks validity of this certificate comparing to given time */ bool checkValidity(time_t time) const { U_TRACE(0, "UCertificate::checkValidity(%ld)", time) /** * Not Before: Jan 25 11:54:00 2005 GMT * Not After : Jan 25 11:54:00 2006 GMT */ if (time >= UTimeDate::getSecondFromDate(getNotBefore()) && time <= UTimeDate::getSecondFromDate(getNotAfter())) { U_RETURN(true); } U_RETURN(false); } /** * Returns bool value to indicate the correctness of this certificate */ bool verify(EVP_PKEY* publicKey) const { U_TRACE(0, "UCertificate::verify(%p)", publicKey) U_INTERNAL_ASSERT_POINTER(x509) if (U_SYSCALL(X509_verify, "%p,%p", x509, publicKey) <= 0) U_RETURN(false); U_RETURN(true); } static X509_STORE_CTX* csc; bool verify(STACK_OF(X509)* chain = U_NULLPTR, time_t certsVerificationTime = 0) const; /** * Retrieves the signer's certificates from this certificate, * it does check their validity and whether any signatures are valid */ static bool verify_result; unsigned getSignerCertificates(UVector& vec, STACK_OF(X509)* chain, time_t certsVerificationTime) const; /** * Returns either the DER or PEM or BASE64 encoding of the certificate depending on the value of format */ UString getEncoded(const char* format = "PEM") const; /** * Returns the Modulus base64 of the certificate public key */ UString getModulus() const; /** * Returns the Exponent base64 of the certificate public key */ UString getExponent() const; /** * parse cert's \c authorityInfoAccess extension and find all the * instances of the \c id-ad-caIssuers access method which can be: * HTTP uri: pointer to a cert file or pointer to a certs-only CMS msg ie. a .p7[bc] file * LDAP uri: LDAP server name + directory entry + attributes ie. \c cACertificate and/or \c crossCertificatePair */ uint32_t getCAIssuers(UVector& vec) const; /** * Returns RevocationURL for the CA that issued this certificate */ uint32_t getRevocationURL(UVector& vec) const; // "X509v3 CRL Distribution Points" /** * Indicate the data certificate ("issuer","serial") for logging... */ static void setForLog(X509* a, UString& buffer, const char* fmt = " (\"%v\",\"%ld\")") { U_TRACE(0, "UCertificate::setForLog(%p,%p,%S)", a, &buffer, fmt) UString issuer = getIssuer(a, true); buffer.snprintf(fmt, strlen(fmt), issuer.rep, getSerialNumber(a)); } void setForLog(UString& buffer, const char* fmt = " (\"%v\",\"%ld\")") const { setForLog(x509, buffer, fmt); } static STACK_OF(X509)* loadCerts(const UString& content); static bool isEqual(X509* a, X509* b) { U_TRACE(0, "UCertificate::isEqual(%p,%p)", a, b) if (U_SYSCALL(X509_cmp, "%p,%p", a, b) == 0) U_RETURN(true); U_RETURN(false); } bool operator==(const UCertificate& c) const { return isEqual(x509, c.x509); } bool operator!=(const UCertificate& c) const { return !isEqual(x509, c.x509); } // STREAM UString print(unsigned long nmflag = 0, unsigned long cflag = 0) const; #ifdef U_STDCPP_ENABLE friend U_EXPORT ostream& operator<<(ostream& os, const UCertificate& c); # ifdef DEBUG const char* dump(bool reset) const; # endif #endif protected: X509* x509; static UVector* vcert; static int verifyCallback(int ok, X509_STORE_CTX* ctx); private: static UString getRevocationURI(const void* gens) U_NO_EXPORT; U_DISALLOW_ASSIGN(UCertificate) }; #endif