Skip to content
Merged
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
10 changes: 1 addition & 9 deletions src/lib/ffi/ffi_cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,20 +372,12 @@ int botan_x509_cert_view_string_values(botan_x509_cert_t cert,
return BOTAN_FFI_ERROR_OUT_OF_RANGE;
};

auto wrap_or_empty = [](std::string str) -> std::vector<std::string> {
if(str.empty()) {
return {};
} else {
return {std::move(str)};
}
};

return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) -> int {
switch(value_type) {
case BOTAN_X509_CRL_DISTRIBUTION_URLS:
return enumerate_crl_distribution_points(c, index);
case BOTAN_X509_OCSP_RESPONDER_URLS:
return enumerate(wrap_or_empty(c.ocsp_responder()), index);
return enumerate(c.ocsp_responders(), index);
case BOTAN_X509_CA_ISSUERS_URLS:
return enumerate(c.ca_issuers(), index);
case BOTAN_X509_PEM_ENCODING:
Expand Down
12 changes: 6 additions & 6 deletions src/lib/x509/x509_ext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -632,18 +632,18 @@ std::vector<uint8_t> Authority_Information_Access::encode_inner() const {
DER_Encoder der(output);

der.start_sequence();
// OCSP
if(!m_ocsp_responder.empty()) {
const ASN1_String url(m_ocsp_responder, ASN1_Type::Ia5String);
// OCSP Responders
for(const auto& ocsp_responder : m_ocsp_responders) {
const ASN1_String url(ocsp_responder, ASN1_Type::Ia5String);
der.start_sequence()
.encode(OID::from_string("PKIX.OCSP"))
.add_object(ASN1_Type(6), ASN1_Class::ContextSpecific, url.value())
.end_cons();
}

// CA Issuers
for(const auto& ca_isser : m_ca_issuers) {
const ASN1_String asn1_ca_issuer(ca_isser, ASN1_Type::Ia5String);
for(const auto& ca_issuer : m_ca_issuers) {
const ASN1_String asn1_ca_issuer(ca_issuer, ASN1_Type::Ia5String);
der.start_sequence()
.encode(OID::from_string("PKIX.CertificateAuthorityIssuers"))
.add_object(ASN1_Type(6), ASN1_Class::ContextSpecific, asn1_ca_issuer.value())
Expand All @@ -668,7 +668,7 @@ void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in)
const BER_Object name = info.get_next_object();

if(name.is_a(6, ASN1_Class::ContextSpecific)) {
m_ocsp_responder = ASN1::to_string(name);
m_ocsp_responders.push_back(ASN1::to_string(name));
}
}
if(oid == OID::from_string("PKIX.CertificateAuthorityIssuers")) {
Expand Down
22 changes: 17 additions & 5 deletions src/lib/x509/x509_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,16 +322,28 @@ class BOTAN_PUBLIC_API(2, 0) Certificate_Policies final : public Certificate_Ext
class BOTAN_PUBLIC_API(2, 0) Authority_Information_Access final : public Certificate_Extension {
public:
std::unique_ptr<Certificate_Extension> copy() const override {
return std::make_unique<Authority_Information_Access>(m_ocsp_responder, m_ca_issuers);
return std::make_unique<Authority_Information_Access>(m_ocsp_responders, m_ca_issuers);
}

Authority_Information_Access() = default;

BOTAN_DEPRECATED("Use constructor with list of OCSP responders")
explicit Authority_Information_Access(std::string_view ocsp,
const std::vector<std::string>& ca_issuers = std::vector<std::string>()) :
m_ocsp_responder(ocsp), m_ca_issuers(ca_issuers) {}
m_ocsp_responders{std::string(ocsp)}, m_ca_issuers(ca_issuers) {}

std::string ocsp_responder() const { return m_ocsp_responder; }
explicit Authority_Information_Access(std::vector<std::string> ocsp_responders,
std::vector<std::string> ca_issuers = std::vector<std::string>()) :
m_ocsp_responders(std::move(ocsp_responders)), m_ca_issuers(std::move(ca_issuers)) {}

BOTAN_DEPRECATED("Use ocsp_responders") std::string ocsp_responder() const {
if(m_ocsp_responders.empty()) {
return {};
}
return m_ocsp_responders[0];
Comment thread
reneme marked this conversation as resolved.
}

const std::vector<std::string>& ocsp_responders() const { return m_ocsp_responders; }

static OID static_oid() { return OID({1, 3, 6, 1, 5, 5, 7, 1, 1}); }

Expand All @@ -342,12 +354,12 @@ class BOTAN_PUBLIC_API(2, 0) Authority_Information_Access final : public Certifi
private:
std::string oid_name() const override { return "PKIX.AuthorityInformationAccess"; }

bool should_encode() const override { return (!m_ocsp_responder.empty() || !m_ca_issuers.empty()); }
bool should_encode() const override { return (!m_ocsp_responders.empty() || !m_ca_issuers.empty()); }

std::vector<uint8_t> encode_inner() const override;
void decode_inner(const std::vector<uint8_t>& in) override;

std::string m_ocsp_responder;
std::vector<std::string> m_ocsp_responders;
std::vector<std::string> m_ca_issuers;
};

Expand Down
21 changes: 16 additions & 5 deletions src/lib/x509/x509cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct X509_Certificate_Data {
std::vector<OID> m_cert_policies;

std::vector<std::string> m_crl_distribution_points;
std::string m_ocsp_responder;
std::vector<std::string> m_ocsp_responders;
std::vector<std::string> m_ca_issuers;

std::vector<uint8_t> m_issuer_dn_bits_sha256;
Expand Down Expand Up @@ -265,7 +265,7 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
}

if(const auto* ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Information_Access>()) {
data->m_ocsp_responder = ext->ocsp_responder();
data->m_ocsp_responders = ext->ocsp_responders();
data->m_ca_issuers = ext->ca_issuers();
}

Expand Down Expand Up @@ -538,7 +538,14 @@ bool X509_Certificate::is_critical(std::string_view ex_name) const {
}

std::string X509_Certificate::ocsp_responder() const {
return data().m_ocsp_responder;
if(data().m_ocsp_responders.empty()) {
return {};
}
return data().m_ocsp_responders[0];
Comment thread
reneme marked this conversation as resolved.
}

const std::vector<std::string>& X509_Certificate::ocsp_responders() const {
return data().m_ocsp_responders;
}

std::vector<std::string> X509_Certificate::ca_issuers() const {
Expand Down Expand Up @@ -798,8 +805,12 @@ std::string X509_Certificate::to_string() const {
}
}

if(!ocsp_responder().empty()) {
out << "OCSP responder " << ocsp_responder() << "\n";
const auto& ocsp_responders = this->ocsp_responders();
if(!ocsp_responders.empty()) {
out << "OCSP Responders:\n";
for(const auto& ocsp_responder : ocsp_responders) {
out << " URI: " << ocsp_responder << "\n";
}
}

const std::vector<std::string> ca_issuers = this->ca_issuers();
Expand Down
7 changes: 6 additions & 1 deletion src/lib/x509/x509cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,12 @@ class BOTAN_PUBLIC_API(2, 0) X509_Certificate : public X509_Object {
/**
* Return the listed address of an OCSP responder, or empty if not set
*/
std::string ocsp_responder() const;
BOTAN_DEPRECATED("Use ocsp_responders") std::string ocsp_responder() const;

/**
* Return the listed addresses of OCSP responders, or empty if not set
*/
const std::vector<std::string>& ocsp_responders() const;

/**
* Return the listed addresses of ca issuers, or empty if not set
Expand Down
55 changes: 55 additions & 0 deletions src/tests/data/x509/misc/contains_multiple_ocsp_responders.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
-----BEGIN CERTIFICATE-----
MIIFvzCCA6egAwIBAgIUbNlFYpSi5yeivRmg+5P61FgbKucwDQYJKoZIhvcNAQEL
BQAwLjEsMCoGA1UEAwwjVGVzdCBDZXJ0aWZpY2F0ZSB3aXRoIE11bHRpcGxlIE9D
U1AwHhcNMjYwMTEyMTMxNTE0WhcNMzYwMTEwMTMxNTE0WjAuMSwwKgYDVQQDDCNU
ZXN0IENlcnRpZmljYXRlIHdpdGggTXVsdGlwbGUgT0NTUDCCAiIwDQYJKoZIhvcN
AQEBBQADggIPADCCAgoCggIBAJ1P8yscKZ5lKggYIxMjRrID52eXuo1K1i5MyllF
qljU5cKrsCHXQRNSS1qIc2k3mYwTUerGT7a4uLG/Z6WHA5xgt6Q4ivSmKvcm56Ta
fiSEgHBxG5BrgemUciCbjNSJTX2N+8urrCbLYkuS6+DLfUdTPy7RGEijBJZ4+/rc
7FNiU6WA0O4X5vm6lUBhCKnqE4Yq15bi40fTWW65e7O1mrtyjT7tcfGm4Dla5UCi
oTQ2xedubjP2PPLgjw1yHYNX5y+TRN/A7pZDRv0/irK7AaTjIc69JY3u3kDmVeB1
fjqSveaCrhjc6ZdEDYSnE3YxSoqrbwU513ArwH9ujtuaiDtj1eIcYOGLoh076j7S
/1iugUhTsDeveHU1fG9idiiktNyRUWRCzMvL97u0og+mQRus4BrsZhukj6VzjOmG
xXLjq/S6mHMG7N2Rk0NyOVomzV+DqfhxT2u53beww8ISJxAFZZOEx1ys9FRJNoc5
BewbpAi3nRtxE2BPIzL89kDmjDsXuJTipVatN0LlI1KOwXLfjt1fAKIl88cDx0xW
Y5sqiI5vX/vh5doveRzwwyLrCyiPnVzcFL0v9sIFZnFsc0F8FSfnf5yXOX0j2omC
Ymll9FJUeU7tzuiiDNjdz5iOOcLXAYlchcDPGiuOTwJTXLJ5QecGOfXfSJRE5vnk
jgCbAgMBAAGjgdQwgdEwHQYDVR0OBBYEFOI4tY7OLK/QNpmDBjE7lnO6oNG5MB8G
A1UdIwQYMBaAFOI4tY7OLK/QNpmDBjE7lnO6oNG5MAwGA1UdEwQFMAMBAf8wgYAG
CCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AxLmV4YW1wbGUu
Y29tMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcDIuZXhhbXBsZS5jb20wJAYIKwYB
BQUHMAGGGGh0dHA6Ly9vY3NwMy5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOC
AgEARhrXE1DcGt7UJtH1j0zxMEgfKzgbnolLVgaWMzZ//1sOH6Qk8P2mfOyjm+l3
4pH6bz/h8+D8TglOKDlcdniwOHXsuP2w4CG9LfQS3YprKH4ltGaQCQ5cL1OMWWq4
MlQ80tHe43kSbJ6eQF+EZr5WGxEwA8hcAaQ08F6QEiPN1h0zOpTfIa4zU0ExUYGm
dCs1ST0RNQuCkEogKnyl6iFzd4AzshHkmzeqdSjvewsbO+UDwSsy/R5pYfzX2IJE
zg8J77q5fy43WJrcubT4uu2fsUKjHMxjpgdYrkIlLtNByviBj6px/4JK2wrczPJ0
+hPOa5boE7P+qpimklmBHnOXDo5BTpRCZXwvHQHKB1JHPmLAGLibF4ljzJ32H44h
DUIV/z1P2Jx2duxh6CIgfOJAZBSln2blpHSpuSemt2j1orM+tT+DWuIz+H3uzKm6
O2TmYGi5R0+wvV8a1ux4HsUMYU9yW+g88LVYRzWWw89g8aRbkVQ6OAAvYgMtJj7d
VDnoCzt9VOU6269w4N2jmQ1IYa3sP60JbdpW+pvfeN35WARmvwcPEBrQYiLl92Fu
e/P6h6L4nMkVS952DJCCDdBceDIh2CPD8t0/XHZqdLRYCaeiOVe+zNYTe6TY1gcx
Soy/P3vJc7UfvNtl0PZx3Fs5UCb5O0RhJXdOn58l8lScCWo=
-----END CERTIFICATE-----


openssl req -new -x509 -days 3650 -nodes \
-newkey rsa:4096 \
-keyout multi_ocsp_key.pem \
-out multi_ocsp_cert.pem \
-config multi_ocsp.cnf

[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no

[ req_distinguished_name ]
CN = Test Certificate with Multiple OCSP

[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:TRUE
authorityInfoAccess = OCSP;URI:http://ocsp1.example.com,OCSP;URI:http://ocsp2.example.com,OCSP;URI:http://ocsp3.example.com
36 changes: 36 additions & 0 deletions src/tests/unit_x509.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,31 @@ Test::Result test_x509_encode_authority_info_access_extension() {

result.test_is_true("OCSP URI available", !cert.ocsp_responder().empty());
result.test_is_true("CA Issuer URI available", !cert.ca_issuers().empty());

// create a certificate with multiple OCSP URIs
Botan::X509_Cert_Options opts_multi_ocsp = req_opts1(sig_algo);
const std::vector<std::string> ocsp_uris = {"http://ocsp.example.com", "http://backup-ocsp.example.com"};
opts_multi_ocsp.extensions.add(std::make_unique<Botan::Cert_Extension::Authority_Information_Access>(ocsp_uris));

req = Botan::X509::create_cert_req(opts_multi_ocsp, *key, hash_fn, *rng);

cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));

const auto* aia_ext =
cert.v3_extensions().get_extension_object_as<Botan::Cert_Extension::Authority_Information_Access>();
result.test_is_true("AIA extension present", aia_ext != nullptr);

const auto ocsp_responders = aia_ext->ocsp_responders();
result.test_sz_eq("number of OCSP responder URIs", ocsp_responders.size(), 2);
result.test_str_eq("First OCSP responder URI matches", ocsp_responders[0], "http://ocsp.example.com");
result.test_str_eq("Second OCSP responder URI matches", ocsp_responders[1], "http://backup-ocsp.example.com");

const auto cert_ocsp_responders = cert.ocsp_responders();
result.test_sz_eq("Certificate: number of OCSP responder URIs", cert_ocsp_responders.size(), 2);
result.test_str_eq(
"Certificate: First OCSP responder URI matches", cert_ocsp_responders[0], "http://ocsp.example.com");
result.test_str_eq(
"Certificate: Second OCSP responder URI matches", cert_ocsp_responders[1], "http://backup-ocsp.example.com");
#endif

return result;
Expand Down Expand Up @@ -686,6 +711,17 @@ Test::Result test_x509_authority_info_access_extension() {
"ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
result.test_str_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");

// contains AIA extension with multiple OCSP responders
const Botan::X509_Certificate aia_cert_multi_ocsp(
Test::data_file("x509/misc/contains_multiple_ocsp_responders.pem"));

const auto& ocsp_responders_multi = aia_cert_multi_ocsp.ocsp_responders();
result.test_sz_eq("number of OCSP responders", ocsp_responders_multi.size(), 3);
result.test_str_eq("First OCSP responder URL matches", ocsp_responders_multi[0], "http://ocsp1.example.com");
result.test_str_eq("Second OCSP responder URL matches", ocsp_responders_multi[1], "http://ocsp2.example.com");
result.test_str_eq("Third OCSP responder URL matches", ocsp_responders_multi[2], "http://ocsp3.example.com");
result.test_is_true("no CA Issuer URI available", aia_cert_multi_ocsp.ca_issuers().empty());

return result;
}

Expand Down
Loading