Repository: randombit/botan
Pull request #1931 (2019-05-03 14:40:35)

Add a system certificate store for Windows
Completing the trinity of system certificate stores.

It allows access into the windows system certificate store, using the Windows API and wincrypt.

Currently implemented are:

* `Certificate_Store::find_cert()`
* `Certificate_Store::find_all_certs()`
* `Certificate_Store::find_cert_by_pubkey_sha1()`
* `Certificate_Store::all_subjects()`

The Windows API for finding multiple certificates works iteratively by passing the result of a previous call of `CertFindCertificateInStore` into the next call. This function will then take care of freeing the previous `PCCERT_CONTEXT`. The RAII-wrapper around those handles is mainly there for exception-safety.

codecov-io: # [Codecov](https://codecov.io/gh/randombit/botan/pull/1931?src=pr&el=h1) Report
> Merging [#1931](https://codecov.io/gh/randombit/botan/pull/1931?src=pr&el=desc) into [master](https://codecov.io/gh/randombit/botan/commit/f3506ee5df67d92616bc98291d06109d2553a078?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `81.25%`.

[![Impacted file tree graph](https://codecov.io/gh/randombit/botan/pull/1931/graphs/tree.svg?width=650&token=VR6iYmSPH8&height=150&src=pr)](https://codecov.io/gh/randombit/botan/pull/1931?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1931      +/-   ##
==========================================
- Coverage   91.38%   91.37%   -0.01%     
==========================================
  Files         531      531              
  Lines       56479    56511      +32     
  Branches     5877     5881       +4     
==========================================
+ Hits        51611    51638      +27     
- Misses       4868     4873       +5
```


| [Impacted Files](https://codecov.io/gh/randombit/botan/pull/1931?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/lib/x509/certstor\_system/certstor\_system.cpp](https://codecov.io/gh/randombit/botan/pull/1931/diff?src=pr&el=tree#diff-c3JjL2xpYi94NTA5L2NlcnRzdG9yX3N5c3RlbS9jZXJ0c3Rvcl9zeXN0ZW0uY3Bw) | `86.66% <ø> (ø)` | :arrow_up: |
| [src/tests/test\_certstor\_utils.cpp](https://codecov.io/gh/randombit/botan/pull/1931/diff?src=pr&el=tree#diff-c3JjL3Rlc3RzL3Rlc3RfY2VydHN0b3JfdXRpbHMuY3Bw) | `88.23% <ø> (ø)` | :arrow_up: |
| [src/tests/test\_certstor\_system.cpp](https://codecov.io/gh/randombit/botan/pull/1931/diff?src=pr&el=tree#diff-c3JjL3Rlc3RzL3Rlc3RfY2VydHN0b3Jfc3lzdGVtLmNwcA==) | `83.09% <81.25%> (-0.54%)` | :arrow_down: |
| [src/lib/pk\_pad/emsa\_raw/emsa\_raw.cpp](https://codecov.io/gh/randombit/botan/pull/1931/diff?src=pr&el=tree#diff-c3JjL2xpYi9wa19wYWQvZW1zYV9yYXcvZW1zYV9yYXcuY3Bw) | `74.28% <0%> (+2.85%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/randombit/botan/pull/1931?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/randombit/botan/pull/1931?src=pr&el=footer). Last update [f3506ee...f9fed8d](https://codecov.io/gh/randombit/botan/pull/1931?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Tymolc: I had to exclude MinGW for now, because `CertFindCertificateInStore` is not implemented there.
Tymolc: Alright, I'm finally done fighting with the Travis-side CI. Appveyor still seems to have some problems. @randombit could you maybe cancel all the appveyor builds but the last one, so it doesn't take all that long to see if there are still issues?
I also think this is ready for another review.
Tymolc: Hm, Windows doesn't seem to find the `D-TRUST Root Class 3 CA 2 EV 2009`, which according to https://social.technet.microsoft.com/wiki/contents/articles/51151.microsoft-trusted-root-certificate-program-participants-as-of-january-30-2018.aspx#D should be available. Now I am unsure if the cert, for some reason, isn't available on the Appveyor agent or if there is a bug. Do you have more access to the AppVeyor agents than the Travis agents by any chance?
randombit: It appears it is possible to RDP to AppVeyor build VMs https://www.appveyor.com/docs/how-to/rdp-to-build-worker/ I can check into this but probably not until late tonight.

randombit: The build VM has an oddly sparse list of CA certs.

![image](https://user-images.githubusercontent.com/469092/57542206-fd749580-731e-11e9-98b0-ac2d07277fd2.png)

Tymolc: Alright, thanks. I think I will just disable the test on Windows, then. It somewhat uncovered a bug on Linux, so I feel like it has some worth there (even though what it is testing isn't actually the reason it failed on Linux...)
reneme: At least one windows installation seems to fail with
```
System Certificate Store - Find all Certificate Subjects ran 1 tests 1 FAILED
Failure 1: Time 30160807230117Z does not seem to be valid
```
:-(
randombit: In the screenshot you can see their local "master-win2016" cert expires in 3016. Weird that MS even lets you create something like this. Currently we reject as a general sanity check any dates > 2200. I was expecting to be dead by the time it became a problem.

I'll write a patch to update the limit to 3100 tonight. Then we are, uh, future proofed? Something like that.
randombit: Rebase to pick up 249edbf. PR looks good, ready to merge when CI is green.

Patch:
diff --git a/doc/authors.txt b/doc/authors.txt
index 954081b55..7f5f90d0b 100644
--- a/doc/authors.txt
+++ b/doc/authors.txt
@@ -89,6 +89,7 @@ souch
 t0b3
 tcely
 Technische Universitat Darmstadt
+Tim Oesterreich
 Tobias @neverhub
 Tomasz Frydrych
 Uri Blumenthal
diff --git a/doc/todo.rst b/doc/todo.rst
index 07119f2ad..ca524c944 100644
--- a/doc/todo.rst
+++ b/doc/todo.rst
@@ -69,7 +69,6 @@ Multiparty Protocols
 External Providers, Hardware Support
 ----------------------------------------
 
-* Access to system certificate stores (Windows, OS X)
 * Extend OpenSSL provider (DH, HMAC, CMAC, GCM)
 * Support using BoringSSL instead of OpenSSL or LibreSSL
 * /dev/crypto provider (ciphers, hashes)
diff --git a/src/build-data/os/windows.txt b/src/build-data/os/windows.txt
index 8eab164df..d11bacc37 100644
--- a/src/build-data/os/windows.txt
+++ b/src/build-data/os/windows.txt
@@ -32,6 +32,8 @@ virtual_lock
 
 threads
 filesystem
+
+certificate_store
 </target_features>
 
 <aliases>
diff --git a/src/lib/x509/certstor_system/certstor_system.cpp b/src/lib/x509/certstor_system/certstor_system.cpp
index 1650a1784..8b469dec9 100644
--- a/src/lib/x509/certstor_system/certstor_system.cpp
+++ b/src/lib/x509/certstor_system/certstor_system.cpp
@@ -9,6 +9,8 @@
 
 #if defined(BOTAN_HAS_CERTSTOR_MACOS)
    #include <botan/certstor_macos.h>
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+   #include <botan/certstor_windows.h>
 #elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
    #include <botan/certstor_flatfile.h>
 #endif
@@ -19,6 +21,8 @@ System_Certificate_Store::System_Certificate_Store()
    {
 #if defined(BOTAN_HAS_CERTSTOR_MACOS)
    m_system_store = std::make_shared<Certificate_Store_MacOS>();
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+   m_system_store = std::make_shared<Certificate_Store_Windows>();
 #elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
    m_system_store = std::make_shared<Flatfile_Certificate_Store>(BOTAN_SYSTEM_CERT_BUNDLE, true);
 #else
diff --git a/src/lib/x509/certstor_system_windows/certstor_windows.cpp b/src/lib/x509/certstor_system_windows/certstor_windows.cpp
new file mode 100644
index 000000000..58ef40fd7
--- /dev/null
+++ b/src/lib/x509/certstor_system_windows/certstor_windows.cpp
@@ -0,0 +1,255 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2018-2019 Patrik Fiedler, Tim Oesterreich
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_windows.h>
+#include <botan/der_enc.h>
+
+#include <array>
+#include <vector>
+
+#define NOMINMAX 1
+#define _WINSOCKAPI_ // stop windows.h including winsock.h
+#include <windows.h>
+#include <wincrypt.h>
+
+namespace Botan {
+namespace {
+
+using Cert_Pointer = std::shared_ptr<const Botan::X509_Certificate>;
+using Cert_Vector = std::vector<Cert_Pointer>;
+const std::array<const char*, 2> cert_store_names{"Root", "CA"};
+
+/**
+ * Abstract RAII wrapper for PCCERT_CONTEXT and HCERTSTORE
+ * The Windows API partly takes care of those pointers destructions itself.
+ * Especially, iteratively calling `CertFindCertificateInStore` with the previous PCCERT_CONTEXT
+ * will free the context and return a new one. In this case, this guard takes care of freeing the context
+ * in case of an exception and at the end of the iterative process.
+ */
+template<class T>
+class Handle_Guard
+   {
+   public:
+      Handle_Guard(T context)
+         : m_context(context)
+         {
+         }
+
+      Handle_Guard(const Handle_Guard<T>& rhs) = delete;
+      Handle_Guard(Handle_Guard<T>&& rhs) :
+         m_context(std::move(rhs.m_context))
+         {
+         rhs.m_context = nullptr;
+         }
+
+      ~Handle_Guard()
+         {
+         close<T>();
+         }
+
+      operator bool() const
+         {
+         return m_context != nullptr;
+         }
+
+      bool assign(T context)
+         {
+         m_context = context;
+         return m_context != nullptr;
+         }
+
+      T& get()
+         {
+         return m_context;
+         }
+
+      const T& get() const
+         {
+         return m_context;
+         }
+
+      T operator->()
+         {
+         return m_context;
+         }
+
+   private:
+      template<class T2 = T>
+      typename std::enable_if<std::is_same<T2, PCCERT_CONTEXT>::value>::type close()
+         {
+         if(m_context)
+            {
+            CertFreeCertificateContext(m_context);
+            }
+         }
+
+      template<class T2 = T>
+      typename std::enable_if<std::is_same<T2, HCERTSTORE>::value>::type close()
+         {
+         if(m_context)
+            {
+            // second parameter is a flag that tells the store how to deallocate memory
+            // using the default "0", this function works like decreasing the reference counter
+            // in a shared_ptr
+            CertCloseStore(m_context, 0);
+            }
+         }
+
+      T m_context;
+   };
+
+HCERTSTORE open_cert_store(const char* cert_store_name)
+   {
+   auto store = CertOpenSystemStore(NULL, cert_store_name);
+   if(!store)
+      {
+      throw Botan::Internal_Error(
+         "failed to open windows certificate store '" + std::string(cert_store_name) +
+         "' (Error Code: " +
+         std::to_string(::GetLastError()) + ")");
+      }
+   return store;
+   }
+
+Cert_Vector search_cert_stores(const _CRYPTOAPI_BLOB& blob, const DWORD& find_type,
+                               std::function<bool(const Cert_Vector& certs, Cert_Pointer cert)> filter,
+                               bool return_on_first_found)
+   {
+   Cert_Vector certs;
+   for(const auto store_name : cert_store_names)
+      {
+      Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+      Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
+      while(cert_context.assign(CertFindCertificateInStore(
+                                   windows_cert_store.get(), PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+                                   NULL, find_type,
+                                   &blob, cert_context.get())))
+         {
+         auto cert = std::make_shared<X509_Certificate>(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+         if(filter(certs, cert))
+            {
+            if(return_on_first_found)
+               {
+               return {cert};
+               }
+            certs.push_back(cert);
+            }
+         }
+      }
+
+   return certs;
+   }
+
+bool already_contains_certificate(const Cert_Vector& certs, Cert_Pointer cert)
+   {
+   return std::any_of(certs.begin(), certs.end(), [&](std::shared_ptr<const Botan::X509_Certificate> c)
+      {
+      return *c == *cert;
+      });
+   }
+
+Cert_Vector find_cert_by_dn_and_key_id(const Botan::X509_DN& subject_dn,
+                                       const std::vector<uint8_t>& key_id,
+                                       bool return_on_first_found)
+   {
+   _CRYPTOAPI_BLOB blob;
+   DWORD find_type;
+   std::vector<uint8_t> dn_data;
+
+   // if key_id is available, prefer searching that, as it should be "more unique" than the subject DN
+   if(key_id.empty())
+      {
+      find_type = CERT_FIND_SUBJECT_NAME;
+      DER_Encoder encoder(dn_data);
+      subject_dn.encode_into(encoder);
+      blob.cbData = static_cast<DWORD>(dn_data.size());
+      blob.pbData = reinterpret_cast<BYTE*>(dn_data.data());
+      }
+   else
+      {
+      find_type = CERT_FIND_KEY_IDENTIFIER;
+      blob.cbData = static_cast<DWORD>(key_id.size());
+      blob.pbData = const_cast<BYTE*>(key_id.data());
+      }
+
+   auto filter = [&](const Cert_Vector& certs, Cert_Pointer cert)
+      {
+      return !already_contains_certificate(certs, cert) && (key_id.empty() || cert->subject_dn() == subject_dn);
+      };
+
+   return search_cert_stores(blob, find_type, filter, return_on_first_found);
+   }
+} // namespace
+
+Certificate_Store_Windows::Certificate_Store_Windows() {}
+
+std::vector<X509_DN> Certificate_Store_Windows::all_subjects() const
+   {
+   std::vector<X509_DN> subject_dns;
+   for(const auto store_name : cert_store_names)
+      {
+      Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+      Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
+
+      // Handle_Guard::assign exchanges the underlying pointer. No RAII is needed here, because the Windows API takes care of
+      // freeing the previous context.
+      while(cert_context.assign(CertEnumCertificatesInStore(windows_cert_store.get(), cert_context.get())))
+         {
+         X509_Certificate cert(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+         subject_dns.push_back(cert.subject_dn());
+         }
+      }
+
+   return subject_dns;
+   }
+
+Cert_Pointer Certificate_Store_Windows::find_cert(const Botan::X509_DN& subject_dn,
+      const std::vector<uint8_t>& key_id) const
+   {
+   const auto certs = find_cert_by_dn_and_key_id(subject_dn, key_id, true);
+   return certs.empty() ? nullptr : certs.front();
+   }
+
+Cert_Vector Certificate_Store_Windows::find_all_certs(
+   const X509_DN& subject_dn,
+   const std::vector<uint8_t>& key_id) const
+   {
+   return find_cert_by_dn_and_key_id(subject_dn, key_id, false);
+   }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+   {
+   if(key_hash.size() != 20)
+      {
+      throw Invalid_Argument("Certificate_Store_Windows::find_cert_by_pubkey_sha1 invalid hash");
+      }
+
+   CRYPT_HASH_BLOB blob;
+   blob.cbData = static_cast<DWORD>(key_hash.size());
+   blob.pbData = const_cast<BYTE*>(key_hash.data());
+
+   auto filter = [](const Cert_Vector&, Cert_Pointer) { return true; };
+
+   const auto certs = search_cert_stores(blob, CERT_FIND_KEY_IDENTIFIER, filter, true);
+   return certs.empty() ? nullptr : certs.front();
+   }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256(
+   const std::vector<uint8_t>& subject_hash) const
+   {
+   BOTAN_UNUSED(subject_hash);
+   throw Not_Implemented("Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256");
+   }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_Windows::find_crl_for(const X509_Certificate& subject) const
+   {
+   // TODO: this could be implemented by using the CertFindCRLInStore function
+   BOTAN_UNUSED(subject);
+   return {};
+   }
+}
diff --git a/src/lib/x509/certstor_system_windows/certstor_windows.h b/src/lib/x509/certstor_system_windows/certstor_windows.h
new file mode 100644
index 000000000..f47e718c8
--- /dev/null
+++ b/src/lib/x509/certstor_system_windows/certstor_windows.h
@@ -0,0 +1,70 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019      Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+#define BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+
+#include <botan/certstor.h>
+
+namespace Botan {
+/**
+* Certificate Store that is backed by the system trust store on Windows.
+*/
+class BOTAN_PUBLIC_API(2, 11) Certificate_Store_Windows final : public Certificate_Store
+   {
+   public:
+      Certificate_Store_Windows();
+
+      Certificate_Store_Windows(const Certificate_Store_Windows&) = default;
+      Certificate_Store_Windows(Certificate_Store_Windows&&) = default;
+      Certificate_Store_Windows& operator=(const Certificate_Store_Windows&) = default;
+      Certificate_Store_Windows& operator=(Certificate_Store_Windows&&) = default;
+
+      /**
+      * @return DNs for all certificates managed by the store
+      */
+      std::vector<X509_DN> all_subjects() const override;
+
+      /**
+      * Find a certificate by Subject DN and (optionally) key identifier
+      * @return the first certificate that matches
+      */
+      std::shared_ptr<const X509_Certificate> find_cert(
+         const X509_DN& subject_dn,
+         const std::vector<uint8_t>& key_id) const override;
+
+      /**
+      * Find all certificates with a given Subject DN.
+      * Subject DN and even the key identifier might not be unique.
+      */
+      std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+               const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+      /**
+      * Find a certificate by searching for one with a matching SHA-1 hash of
+      * public key.
+      * @return a matching certificate or nullptr otherwise
+      */
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+      /**
+       * @throws Botan::Not_Implemented
+       */
+      std::shared_ptr<const X509_Certificate>
+      find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+      /**
+       * Not Yet Implemented
+       * @return nullptr;
+       */
+      std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+   };
+}
+
+#endif
diff --git a/src/lib/x509/certstor_system_windows/info.txt b/src/lib/x509/certstor_system_windows/info.txt
new file mode 100644
index 000000000..8d6dce6c6
--- /dev/null
+++ b/src/lib/x509/certstor_system_windows/info.txt
@@ -0,0 +1,15 @@
+<defines>
+CERTSTOR_WINDOWS -> 20190430
+</defines>
+
+<os_features>
+win32,certificate_store
+</os_features>
+
+<header:public>
+certstor_windows.h
+</header:public>
+
+<libs>
+windows -> crypt32.lib
+</libs>
\ No newline at end of file


Test patch:
diff --git a/src/tests/test_certstor_system.cpp b/src/tests/test_certstor_system.cpp
index 854bf4bae..48bcc7246 100644
--- a/src/tests/test_certstor_system.cpp
+++ b/src/tests/test_certstor_system.cpp
@@ -76,6 +76,33 @@ Test::Result find_cert_by_subject_dn(Botan::Certificate_Store& certstore)
    return result;
    }
 
+Test::Result find_cert_by_utf8_subject_dn(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find Certificate by UTF8 subject DN");
+
+   try
+      {
+      auto dn = get_utf8_dn();
+
+      result.start_timer();
+      auto cert = certstore.find_cert(dn, std::vector<uint8_t>());
+      result.end_timer();
+
+      if(result.test_not_null("found certificate", cert.get()))
+         {
+         auto cns = cert->subject_dn().get_attribute("CN");
+         result.test_is_eq("exactly one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "D-TRUST Root Class 3 CA 2 EV 2009");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
 Test::Result find_cert_by_subject_dn_and_key_id(Botan::Certificate_Store& certstore)
    {
    Test::Result result("System Certificate Store - Find Certificate by subject DN and key ID");
@@ -131,6 +158,43 @@ Test::Result find_certs_by_subject_dn_and_key_id(Botan::Certificate_Store& certs
    return result;
    }
 
+Test::Result find_all_certs_by_subject_dn(Botan::Certificate_Store& certstore)
+   {
+   Test::Result result("System Certificate Store - Find all Certificates by subject DN");
+
+   try
+      {
+      auto dn = get_dn();
+
+      result.start_timer();
+      auto certs = certstore.find_all_certs(dn, std::vector<uint8_t>());
+      result.end_timer();
+
+      // check for duplications
+      sort(certs.begin(), certs.end());
+      for(int i = 1; i < certs.size(); ++i)
+         {
+         if(certs[i=1] == certs[i])
+            {
+            result.test_failure("find_all_certs produced duplicated result");
+            }
+         }
+
+      if(result.confirm("result not empty", !certs.empty()))
+         {
+         auto cns = certs.front()->subject_dn().get_attribute("CN");
+         result.test_gte("at least one CN", cns.size(), size_t(1));
+         result.test_eq("CN", cns.front(), "DST Root CA X3");
+         }
+      }
+   catch(std::exception& e)
+      {
+      result.test_failure(e.what());
+      }
+
+   return result;
+   }
+
 Test::Result find_all_subjects(Botan::Certificate_Store& certstore)
    {
    Test::Result result("System Certificate Store - Find all Certificate Subjects");
@@ -241,6 +305,7 @@ class Certstor_System_Tests final : public Test
             }
          catch(Botan::Not_Implemented& e)
             {
+            BOTAN_UNUSED(e);
             open_result.test_note("Skipping due to not available in current build");
             return {open_result};
             }
@@ -258,9 +323,13 @@ class Certstor_System_Tests final : public Test
          results.push_back(find_certificate_by_pubkey_sha1(*system));
          results.push_back(find_cert_by_subject_dn(*system));
          results.push_back(find_cert_by_subject_dn_and_key_id(*system));
+         results.push_back(find_all_certs_by_subject_dn(*system));
          results.push_back(find_certs_by_subject_dn_and_key_id(*system));
          results.push_back(find_all_subjects(*system));
          results.push_back(no_certificate_matches(*system));
+#if !defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+         results.push_back(find_cert_by_utf8_subject_dn(*system));
+#endif
 #if defined(BOTAN_HAS_CERTSTOR_MACOS)
          results.push_back(certificate_matching_with_dn_normalization(*system));
 #endif
diff --git a/src/tests/test_certstor_utils.cpp b/src/tests/test_certstor_utils.cpp
index 007271cc8..68b0f8010 100644
--- a/src/tests/test_certstor_utils.cpp
+++ b/src/tests/test_certstor_utils.cpp
@@ -24,7 +24,7 @@ Botan::X509_DN read_dn(const std::string hex)
 
 Botan::X509_DN get_dn()
    {
-   // Public key fingerprint of "DST Root CA X3"
+   // ASN.1 encoded subject DN of "DST Root CA X3"
    // This certificate is in the standard "System Roots" of any macOS setup,
    // serves as the trust root of botan.randombit.net and expires on
    // Thursday, 30. September 2021 at 16:01:15 Central European Summer Time