From 3a2281b69e2297c5b9dab0e90a615dec37ee9105 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 23 Feb 2026 15:23:50 +0200 Subject: [PATCH] Fetch additional intermediate certificates for validation IB-7992 Signed-off-by: Raul Metsma --- src/SignatureXAdES_B.cpp | 43 ++++++++++++++++++++---------------- src/SignatureXAdES_B.h | 4 ++-- src/SignatureXAdES_T.cpp | 2 +- src/crypto/Signer.cpp | 1 - src/crypto/Signer.h | 4 +++- src/crypto/X509Cert.cpp | 13 +++++++++-- src/crypto/X509Cert.h | 3 ++- src/crypto/X509CertStore.cpp | 9 +++++--- src/crypto/X509CertStore.h | 4 ++-- 9 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/SignatureXAdES_B.cpp b/src/SignatureXAdES_B.cpp index 6f1723853..fa9cb9253 100644 --- a/src/SignatureXAdES_B.cpp +++ b/src/SignatureXAdES_B.cpp @@ -544,11 +544,8 @@ void SignatureXAdES_B::validate(const string &policy) const if(!signatureref.empty()) EXCEPTION_ADD(exception, "Manifest references and signature references do not match"); - try { checkKeyInfo(); } - catch(const Exception& e) { exception.addCause(e); } - - try { checkSigningCertificate(policy == POLv1); } - catch(const Exception& e) { exception.addCause(e); } + auto signingCertificate = checkSigningCertificate(policy == POLv1); + checkKeyInfo(signingCertificate); } catch(const Exception &e) { exception.addCause(e); } catch(...) { @@ -595,9 +592,8 @@ void SignatureXAdES_B::checkDigest(XMLNode digest, const vector & * Verify if SigningCertificate matches with * XAdES::SigningCertificate/SigningCertificateV2 Digest and IssuerSerial info */ -void SignatureXAdES_B::checkKeyInfo() const +void SignatureXAdES_B::checkKeyInfo(const X509Cert &x509) const { - X509Cert x509 = signingCertificate(); if(auto sigCert = signedSignatureProperties()/"SigningCertificate") { if(auto certs = sigCert/"Cert"; certs || !(certs + 1)) @@ -626,21 +622,30 @@ void SignatureXAdES_B::checkKeyInfo() const * Check if signing certificate was issued by trusted party. * @throws Exception on a problem with signing certificate */ -void SignatureXAdES_B::checkSigningCertificate(bool noqscd) const +X509Cert SignatureXAdES_B::checkSigningCertificate(bool noqscd, tm validation_time) const { - try + X509Cert signingCertificate; + vector untrusted; + for(auto x509Data = signature/"KeyInfo"/"X509Data"; x509Data; x509Data++) { - X509Cert signingCert = signingCertificate(); - vector usage = signingCert.keyUsage(); - if(!contains(usage, X509Cert::NonRepudiation)) - THROW("Signing certificate does not contain NonRepudiation key usage flag"); - if(!signingCertificate().verify(noqscd)) - THROW("Unable to verify signing certificate"); - } - catch(const Exception &e) - { - THROW_CAUSE( e, "Unable to verify signing certificate" ); + for(auto x509Cert = x509Data/"X509Certificate"; x509Cert; x509Cert++) + { + vector cert = x509Cert; + if(cert.empty()) + continue; + if(!signingCertificate) + signingCertificate = X509Cert(cert); + else + untrusted.emplace_back(cert); + } } + if(!signingCertificate) + THROW("Signature does not contain signer certificate"); + if(!contains(signingCertificate.keyUsage(), X509Cert::NonRepudiation)) + THROW("Signing certificate does not contain NonRepudiation key usage flag"); + if(!signingCertificate.verify(noqscd, validation_time, untrusted)) + THROW("Unable to verify signing certificate"); + return signingCertificate; } void SignatureXAdES_B::addDataObjectFormat(const string &uri, const string &mime) diff --git a/src/SignatureXAdES_B.h b/src/SignatureXAdES_B.h index 56f12e485..ba9127952 100644 --- a/src/SignatureXAdES_B.h +++ b/src/SignatureXAdES_B.h @@ -92,6 +92,7 @@ namespace digidoc constexpr XMLNode signedSignatureProperties() const noexcept; static void checkCertID(XMLNode certID, const X509Cert &cert); static void checkDigest(XMLNode digest, const std::vector &data); + X509Cert checkSigningCertificate(bool noqscd, tm validation_time = {}) const; XMLNode signature; ASiContainer *bdoc {}; @@ -116,7 +117,6 @@ namespace digidoc constexpr XMLNode V1orV2(std::string_view v1, std::string_view v2) const noexcept; // offline checks - void checkSigningCertificate(bool noqscd) const; - void checkKeyInfo() const; + void checkKeyInfo(const X509Cert &x509) const; }; } diff --git a/src/SignatureXAdES_T.cpp b/src/SignatureXAdES_T.cpp index e2312a8bb..b62a514c5 100644 --- a/src/SignatureXAdES_T.cpp +++ b/src/SignatureXAdES_T.cpp @@ -114,7 +114,7 @@ void SignatureXAdES_T::validate(const std::string &policy) const signatures->c14n(digest, canonicalizationMethod, signatureValue()); }); - if(!signingCertificate().verify(policy == POLv1, tsa.time())) + if(!checkSigningCertificate(policy == POLv1, tsa.time())) THROW("Signing certificate was not valid on signing time"); auto completeCertRefs = usp/"CompleteCertificateRefs"; diff --git a/src/crypto/Signer.cpp b/src/crypto/Signer.cpp index b36a0e4c5..d780585d0 100644 --- a/src/crypto/Signer.cpp +++ b/src/crypto/Signer.cpp @@ -28,7 +28,6 @@ #include -#include #include #include diff --git a/src/crypto/Signer.h b/src/crypto/Signer.h index d23c18a00..2442c7a7a 100644 --- a/src/crypto/Signer.h +++ b/src/crypto/Signer.h @@ -19,9 +19,11 @@ #pragma once -#include "../Exception.h" +#include "../Exports.h" #include +#include +#include namespace digidoc { diff --git a/src/crypto/X509Cert.cpp b/src/crypto/X509Cert.cpp index 23c8b6290..28e07f094 100644 --- a/src/crypto/X509Cert.cpp +++ b/src/crypto/X509Cert.cpp @@ -272,7 +272,7 @@ X509Cert::X509Cert(X509Cert &&other) noexcept = default; /** * Clean up underlying X509 data. */ -X509Cert::~X509Cert() = default; +X509Cert::~X509Cert() noexcept = default; /** * Encodes the X509 certificate using DER encoding. @@ -510,7 +510,16 @@ bool X509Cert::isValid(time_t *t) const */ bool X509Cert::verify(bool noqscd, tm validation_time) const { - return X509CertStore::instance()->verify(*this, noqscd, validation_time); + return X509CertStore::instance()->verify(*this, noqscd, validation_time, {}); +} + +/** + * Returns true if certificate is signed by trusted issuer + * @throws Exception if error + */ +bool X509Cert::verify(bool noqscd, tm validation_time, const vector &untrusted) const +{ + return X509CertStore::instance()->verify(*this, noqscd, validation_time, untrusted); } /** diff --git a/src/crypto/X509Cert.h b/src/crypto/X509Cert.h index d0776490e..4c056058f 100644 --- a/src/crypto/X509Cert.h +++ b/src/crypto/X509Cert.h @@ -86,7 +86,7 @@ namespace digidoc explicit X509Cert(const std::string &path, Format format = Pem); X509Cert(X509Cert &&other) noexcept; X509Cert(const X509Cert &other); - ~X509Cert(); + ~X509Cert() noexcept; std::string serial() const; std::string issuerName(const std::string &obj = std::string()) const; @@ -97,6 +97,7 @@ namespace digidoc bool isCA() const; bool isValid(time_t *t = nullptr) const; bool verify(bool noqscd, tm validation_time = {}) const; + bool verify(bool noqscd, tm validation_time, const std::vector &untrusted) const; X509* handle() const; operator std::vector() const; diff --git a/src/crypto/X509CertStore.cpp b/src/crypto/X509CertStore.cpp index d211ca7d6..120e677a6 100644 --- a/src/crypto/X509CertStore.cpp +++ b/src/crypto/X509CertStore.cpp @@ -69,7 +69,7 @@ X509CertStore::X509CertStore() /** * Release all certificates. */ -X509CertStore::~X509CertStore() = default; +X509CertStore::~X509CertStore() noexcept = default; void X509CertStore::activate(const X509Cert &cert) const { @@ -194,14 +194,17 @@ int X509CertStore::validate(int ok, X509_STORE_CTX *ctx) * Check if X509Cert is signed by trusted issuer * @throw Exception if error */ -bool X509CertStore::verify(const X509Cert &cert, bool noqscd, tm validation_time) const +bool X509CertStore::verify(const X509Cert &cert, bool noqscd, tm validation_time, const vector &untrusted) const { activate(cert); if(util::date::is_empty(validation_time)) ASN1_TIME_to_tm(X509_get0_notBefore(cert.handle()), &validation_time); auto store = createStore(X509CertStore::CA, validation_time); auto csc = make_unique_ptr(X509_STORE_CTX_new()); - if(!X509_STORE_CTX_init(csc.get(), store.get(), cert.handle(), nullptr)) + auto stack = make_unique_ptr(sk_X509_new_null(), [](auto *sk) { sk_X509_free(sk); }); + for(const X509Cert &i: untrusted) + sk_X509_push(stack.get(), i.handle()); + if(!X509_STORE_CTX_init(csc.get(), store.get(), cert.handle(), stack.get())) THROW_OPENSSLEXCEPTION("Failed to init X509_STORE_CTX"); if(X509_verify_cert(csc.get()) <= 0) { diff --git a/src/crypto/X509CertStore.h b/src/crypto/X509CertStore.h index 61c6e724c..554574d4b 100644 --- a/src/crypto/X509CertStore.h +++ b/src/crypto/X509CertStore.h @@ -49,11 +49,11 @@ namespace digidoc X509Cert findIssuer(const X509Cert &cert, const Type &type) const; static X509Cert issuerFromAIA(const X509Cert &cert); static unique_free_t createStore(const Type &type, tm &tm); - bool verify(const X509Cert &cert, bool noqscd, tm validation_time = {}) const; + bool verify(const X509Cert &cert, bool noqscd, tm validation_time, const std::vector &untrusted) const; private: X509CertStore(); - ~X509CertStore(); + ~X509CertStore() noexcept; DISABLE_COPY(X509CertStore); static int validate(int ok, X509_STORE_CTX *ctx);