Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/pkcs11sign.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <Container.h>
#include <Exception.h>
#include <crypto/PKCS11Signer.h>

#include <iostream>
Expand Down
43 changes: 24 additions & 19 deletions src/SignatureXAdES_B.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(...) {
Expand Down Expand Up @@ -595,9 +592,8 @@ void SignatureXAdES_B::checkDigest(XMLNode digest, const vector<unsigned char> &
* 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))
Expand Down Expand Up @@ -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<X509Cert> untrusted;
for(auto x509Data = signature/"KeyInfo"/"X509Data"; x509Data; x509Data++)
{
X509Cert signingCert = signingCertificate();
vector<X509Cert::KeyUsage> 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<unsigned char> 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)
Expand Down
4 changes: 2 additions & 2 deletions src/SignatureXAdES_B.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned char> &data);
X509Cert checkSigningCertificate(bool noqscd, tm validation_time = {}) const;

XMLNode signature;
ASiContainer *bdoc {};
Expand All @@ -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;
};
}
46 changes: 42 additions & 4 deletions src/SignatureXAdES_LT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,14 @@ void SignatureXAdES_LT::validate(const string &policy) const
* Find OCSP response that matches with signingCertificate.
* If none is found throw all OCSP validation exceptions.
*/
vector<X509Cert> untrusted = certificateValues(revocationValues);
bool foundSignerOCSP = false;
vector<Exception> ocspExceptions;
for(auto resp = ocspValues/"EncapsulatedOCSPValue"; resp; resp++)
{
OCSP ocsp(resp);
try {
ocsp.verifyResponse(signingCertificate());
ocsp.verifyResponse(signingCertificate(), untrusted);
foundSignerOCSP = true;
} catch(const Exception &e) {
ocspExceptions.push_back(e);
Expand Down Expand Up @@ -221,7 +222,7 @@ void SignatureXAdES_LT::extendSignatureProfile(Signer *signer)
cert.issuerName().c_str());

OCSP ocsp(cert, issuer, signer->userAgent());
ocsp.verifyResponse(cert);
ocsp.verifyResponse(cert, {});

addCertificateValue(id() + "-CA-CERT", issuer);
addOCSPValue(id().replace(0, 1, "N"), ocsp);
Expand Down Expand Up @@ -255,6 +256,41 @@ void SignatureXAdES_LT::addOCSPValue(const string &id, const OCSP &ocsp)
enc = ocsp;
}

vector<X509Cert> SignatureXAdES_LT::certificateValues(XMLNode until) const
{
vector<X509Cert> certs;
auto addCert = [&certs](XMLNode cert) {
try {
certs.emplace_back(cert);
} catch(const Exception &e) {
WARN("Failed to parse certificate from EncapsulatedX509Certificate: %s", e.msg().c_str());
}
};

// KeyInfo intermediate certificates (skip first = signing cert)
bool first = true;
for(auto x509Data = signature/"KeyInfo"/"X509Data"; x509Data; x509Data++)
{
for(auto x509Cert = x509Data/"X509Certificate"; x509Cert; x509Cert++)
{
if(first) { first = false; continue; }
addCert(x509Cert);
}
}

// CertificateValues certificates that are available before the guarded node.
for(auto elem: unsignedSignatureProperties())
{
if(until && elem == until)
break;
if(elem.name() != "CertificateValues" || elem.ns() != XADES_NS)
continue;
for(auto cert = elem/"EncapsulatedX509Certificate"; cert; cert++)
addCert(cert);
};
return certs;
}

/**
* Get value of UnsignedProperties\UnsignedSignatureProperties\RevocationValues\OCSPValues\EncapsulatedOCSPValue
* which contains whole OCSP response
Expand All @@ -264,12 +300,14 @@ OCSP SignatureXAdES_LT::getOCSPResponseValue() const
{
try
{
auto ocspValues = unsignedSignatureProperties()/"RevocationValues"/"OCSPValues";
auto revocationValues = unsignedSignatureProperties()/"RevocationValues";
vector<X509Cert> untrusted = certificateValues(revocationValues);
auto ocspValues = revocationValues/"OCSPValues";
for(auto resp = ocspValues/"EncapsulatedOCSPValue"; resp; resp++)
{
try {
OCSP ocsp(resp);
ocsp.verifyResponse(signingCertificate());
ocsp.verifyResponse(signingCertificate(), untrusted);
return ocsp;
} catch(const Exception &) {
}
Expand Down
1 change: 1 addition & 0 deletions src/SignatureXAdES_LT.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class SignatureXAdES_LT: public SignatureXAdES_T

void addOCSPValue(const std::string &id, const OCSP &ocsp);
void addCertificateValue(const std::string& certId, const X509Cert& x509);
std::vector<X509Cert> certificateValues(XMLNode until = XMLNode{}) const;
OCSP getOCSPResponseValue() const;
};

Expand Down
2 changes: 1 addition & 1 deletion src/SignatureXAdES_T.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
18 changes: 16 additions & 2 deletions src/crypto/OCSP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ OCSP::operator vector<unsigned char>() const
/**
* Check that response was signed with trusted OCSP certificate
*/
void OCSP::verifyResponse(const X509Cert &cert) const
void OCSP::verifyResponse(const X509Cert &cert, const vector<X509Cert> &untrusted) const
{
if(!basic)
THROW("Failed to verify OCSP response.");
Expand All @@ -202,7 +202,10 @@ void OCSP::verifyResponse(const X509Cert &cert) const
sk_X509_push(stack.get(), i.handle());
}
}
for(const X509Cert &i: untrusted)
sk_X509_push(stack.get(), i.handle());
auto store = X509CertStore::createStore(X509CertStore::OCSP, tm);
ERR_clear_error();
if(OCSP_basic_verify(basic.get(), stack.get(), store.get(), OCSP_NOCHECKS | OCSP_PARTIAL_CHAIN) != 1)
{
unsigned long err = ERR_get_error();
Expand All @@ -217,9 +220,20 @@ void OCSP::verifyResponse(const X509Cert &cert) const
throw OpenSSLException(EXCEPTION_PARAMS("Failed to verify OCSP response."), err);
}

// Find issuer before OCSP validation to activate region TSL
// Find issuer from TSL or from untrusted CertificateValues
X509Cert issuer = X509CertStore::instance()->findIssuer(cert, X509CertStore::CA);
if(!issuer)
{
for(const X509Cert &i: untrusted)
{
if(X509_check_issued(i.handle(), cert.handle()) == X509_V_OK)
{
issuer = i;
break;
}
}
}
if(!issuer)
{
Exception e(EXCEPTION_PARAMS("Certificate status: unknown"));
e.setCode(Exception::CertificateUnknown);
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/OCSP.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace digidoc
std::vector<unsigned char> nonce() const;
tm producedAt() const;
X509Cert responderCert() const;
void verifyResponse(const X509Cert &cert) const;
void verifyResponse(const X509Cert &cert, const std::vector<X509Cert> &untrusted) const;

operator std::vector<unsigned char>() const;

Expand Down
1 change: 0 additions & 1 deletion src/crypto/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

#include <openssl/x509.h>

#include <algorithm>
#include <map>
#include <optional>

Expand Down
4 changes: 3 additions & 1 deletion src/crypto/Signer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

#pragma once

#include "../Exception.h"
#include "../Exports.h"

#include <memory>
#include <string>
#include <vector>

namespace digidoc
{
Expand Down
13 changes: 11 additions & 2 deletions src/crypto/X509Cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ X509Cert::X509Cert(X509Cert &&other) noexcept = default;
/**
* Clean up underlying OpenSSL X509 data.
*/
X509Cert::~X509Cert() = default;
X509Cert::~X509Cert() noexcept = default;

/**
* Encodes the X509 certificate using DER encoding.
Expand Down Expand Up @@ -578,7 +578,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<X509Cert> &untrusted) const
{
return X509CertStore::instance()->verify(*this, noqscd, validation_time, untrusted);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/crypto/X509Cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<X509Cert> &untrusted) const;

X509* handle() const;
operator std::vector<unsigned char>() const;
Expand Down
62 changes: 36 additions & 26 deletions src/crypto/X509CertStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,34 +154,41 @@ int X509CertStore::validate(int ok, X509_STORE_CTX *ctx)
}

auto *type = static_cast<Type*>(X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), 0));
X509 *x509 = X509_STORE_CTX_get0_cert(ctx);
auto current = util::date::to_string(X509_VERIFY_PARAM_get_time(X509_STORE_CTX_get0_param(ctx)));
for(const TSL::Service &s: *instance()->d)
{
if(type->find(s.type) == type->cend()) // correct service type
continue;
if(none_of(s.certs, [&](const X509Cert &issuer) {
if(issuer == x509) // certificate is listed by service
return true;
if(X509_check_issued(issuer.handle(), x509) != X509_V_OK) // certificate is issued by service (function checks only issuer name)
return false;
auto pub = make_unique_ptr<EVP_PKEY_free>(X509_get_pubkey(issuer.handle()));
if(X509_verify(x509, pub.get()) == 1) // certificate is signed by service
return true;
ERR_clear_error();
return false;
})) // certificate is trusted by service
continue;
for(auto i = s.validity.crbegin(), end = s.validity.crend(); i != end; ++i)
auto trusted = [&](X509 *x509) {
for(const TSL::Service &s: *instance()->d)
{
if(current < i->first) // Search older status
if(type->find(s.type) == type->cend()) // correct service type
continue;
if(!i->second.has_value()) // Has revoked
break;
X509_STORE_CTX_set_ex_data(ctx, 0, const_cast<TSL::Qualifiers*>(&i->second));
return 1;
if(none_of(s.certs, [&](const X509Cert &issuer) {
if(issuer == x509) // certificate is listed by service
return true;
if(X509_check_issued(issuer.handle(), x509) != X509_V_OK) // certificate is issued by service (function checks only issuer name)
return false;
auto pub = make_unique_ptr<EVP_PKEY_free>(X509_get_pubkey(issuer.handle()));
if(X509_verify(x509, pub.get()) == 1) // certificate is signed by service
return true;
ERR_clear_error();
return false;
})) // certificate is trusted by service
continue;
for(auto i = s.validity.crbegin(), end = s.validity.crend(); i != end; ++i)
{
if(current < i->first) // Search older status
continue;
if(!i->second.has_value()) // Has revoked
break;
X509_STORE_CTX_set_ex_data(ctx, 0, const_cast<TSL::Qualifiers*>(&i->second));
return true;
}
}
}
return false;
};
X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);
X509 *target_cert = X509_STORE_CTX_get0_cert(ctx);
if((current_cert && trusted(current_cert)) ||
(target_cert && target_cert != current_cert && trusted(target_cert)))
return 1;
return ok;
}

Expand All @@ -200,14 +207,17 @@ void X509CertStore::update() const
* 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<X509Cert> &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_free>(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)
{
Expand Down
Loading
Loading