Botan  2.11.0
Crypto and TLS for C++11
certstor_windows.cpp
Go to the documentation of this file.
1 /*
2 * Certificate Store
3 * (C) 1999-2019 Jack Lloyd
4 * (C) 2018-2019 Patrik Fiedler, Tim Oesterreich
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8 
9 #include <botan/certstor_windows.h>
10 #include <botan/der_enc.h>
11 
12 #include <array>
13 #include <vector>
14 
15 #define NOMINMAX 1
16 #define _WINSOCKAPI_ // stop windows.h including winsock.h
17 #include <windows.h>
18 #include <wincrypt.h>
19 
20 namespace Botan {
21 namespace {
22 
23 using Cert_Pointer = std::shared_ptr<const Botan::X509_Certificate>;
24 using Cert_Vector = std::vector<Cert_Pointer>;
25 const std::array<const char*, 2> cert_store_names{"Root", "CA"};
26 
27 /**
28  * Abstract RAII wrapper for PCCERT_CONTEXT and HCERTSTORE
29  * The Windows API partly takes care of those pointers destructions itself.
30  * Especially, iteratively calling `CertFindCertificateInStore` with the previous PCCERT_CONTEXT
31  * will free the context and return a new one. In this case, this guard takes care of freeing the context
32  * in case of an exception and at the end of the iterative process.
33  */
34 template<class T>
35 class Handle_Guard
36  {
37  public:
38  Handle_Guard(T context)
39  : m_context(context)
40  {
41  }
42 
43  Handle_Guard(const Handle_Guard<T>& rhs) = delete;
44  Handle_Guard(Handle_Guard<T>&& rhs) :
45  m_context(std::move(rhs.m_context))
46  {
47  rhs.m_context = nullptr;
48  }
49 
50  ~Handle_Guard()
51  {
52  close<T>();
53  }
54 
55  operator bool() const
56  {
57  return m_context != nullptr;
58  }
59 
60  bool assign(T context)
61  {
62  m_context = context;
63  return m_context != nullptr;
64  }
65 
66  T& get()
67  {
68  return m_context;
69  }
70 
71  const T& get() const
72  {
73  return m_context;
74  }
75 
76  T operator->()
77  {
78  return m_context;
79  }
80 
81  private:
82  template<class T2 = T>
83  typename std::enable_if<std::is_same<T2, PCCERT_CONTEXT>::value>::type close()
84  {
85  if(m_context)
86  {
87  CertFreeCertificateContext(m_context);
88  }
89  }
90 
91  template<class T2 = T>
92  typename std::enable_if<std::is_same<T2, HCERTSTORE>::value>::type close()
93  {
94  if(m_context)
95  {
96  // second parameter is a flag that tells the store how to deallocate memory
97  // using the default "0", this function works like decreasing the reference counter
98  // in a shared_ptr
99  CertCloseStore(m_context, 0);
100  }
101  }
102 
103  T m_context;
104  };
105 
106 HCERTSTORE open_cert_store(const char* cert_store_name)
107  {
108  auto store = CertOpenSystemStore(NULL, cert_store_name);
109  if(!store)
110  {
111  throw Botan::Internal_Error(
112  "failed to open windows certificate store '" + std::string(cert_store_name) +
113  "' (Error Code: " +
114  std::to_string(::GetLastError()) + ")");
115  }
116  return store;
117  }
118 
119 Cert_Vector search_cert_stores(const _CRYPTOAPI_BLOB& blob, const DWORD& find_type,
120  std::function<bool(const Cert_Vector& certs, Cert_Pointer cert)> filter,
121  bool return_on_first_found)
122  {
123  Cert_Vector certs;
124  for(const auto store_name : cert_store_names)
125  {
126  Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
127  Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
128  while(cert_context.assign(CertFindCertificateInStore(
129  windows_cert_store.get(), PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
130  NULL, find_type,
131  &blob, cert_context.get())))
132  {
133  auto cert = std::make_shared<X509_Certificate>(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
134  if(filter(certs, cert))
135  {
136  if(return_on_first_found)
137  {
138  return {cert};
139  }
140  certs.push_back(cert);
141  }
142  }
143  }
144 
145  return certs;
146  }
147 
148 bool already_contains_certificate(const Cert_Vector& certs, Cert_Pointer cert)
149  {
150  return std::any_of(certs.begin(), certs.end(), [&](std::shared_ptr<const Botan::X509_Certificate> c)
151  {
152  return *c == *cert;
153  });
154  }
155 
156 Cert_Vector find_cert_by_dn_and_key_id(const Botan::X509_DN& subject_dn,
157  const std::vector<uint8_t>& key_id,
158  bool return_on_first_found)
159  {
160  _CRYPTOAPI_BLOB blob;
161  DWORD find_type;
162  std::vector<uint8_t> dn_data;
163 
164  // if key_id is available, prefer searching that, as it should be "more unique" than the subject DN
165  if(key_id.empty())
166  {
167  find_type = CERT_FIND_SUBJECT_NAME;
168  DER_Encoder encoder(dn_data);
169  subject_dn.encode_into(encoder);
170  blob.cbData = static_cast<DWORD>(dn_data.size());
171  blob.pbData = reinterpret_cast<BYTE*>(dn_data.data());
172  }
173  else
174  {
175  find_type = CERT_FIND_KEY_IDENTIFIER;
176  blob.cbData = static_cast<DWORD>(key_id.size());
177  blob.pbData = const_cast<BYTE*>(key_id.data());
178  }
179 
180  auto filter = [&](const Cert_Vector& certs, Cert_Pointer cert)
181  {
182  return !already_contains_certificate(certs, cert) && (key_id.empty() || cert->subject_dn() == subject_dn);
183  };
184 
185  return search_cert_stores(blob, find_type, filter, return_on_first_found);
186  }
187 } // namespace
188 
190 
191 std::vector<X509_DN> Certificate_Store_Windows::all_subjects() const
192  {
193  std::vector<X509_DN> subject_dns;
194  for(const auto store_name : cert_store_names)
195  {
196  Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
197  Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
198 
199  // Handle_Guard::assign exchanges the underlying pointer. No RAII is needed here, because the Windows API takes care of
200  // freeing the previous context.
201  while(cert_context.assign(CertEnumCertificatesInStore(windows_cert_store.get(), cert_context.get())))
202  {
203  X509_Certificate cert(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
204  subject_dns.push_back(cert.subject_dn());
205  }
206  }
207 
208  return subject_dns;
209  }
210 
212  const std::vector<uint8_t>& key_id) const
213  {
214  const auto certs = find_cert_by_dn_and_key_id(subject_dn, key_id, true);
215  return certs.empty() ? nullptr : certs.front();
216  }
217 
219  const X509_DN& subject_dn,
220  const std::vector<uint8_t>& key_id) const
221  {
222  return find_cert_by_dn_and_key_id(subject_dn, key_id, false);
223  }
224 
225 Cert_Pointer Certificate_Store_Windows::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
226  {
227  if(key_hash.size() != 20)
228  {
229  throw Invalid_Argument("Certificate_Store_Windows::find_cert_by_pubkey_sha1 invalid hash");
230  }
231 
232  CRYPT_HASH_BLOB blob;
233  blob.cbData = static_cast<DWORD>(key_hash.size());
234  blob.pbData = const_cast<BYTE*>(key_hash.data());
235 
236  auto filter = [](const Cert_Vector&, Cert_Pointer) { return true; };
237 
238  const auto certs = search_cert_stores(blob, CERT_FIND_KEY_IDENTIFIER, filter, true);
239  return certs.empty() ? nullptr : certs.front();
240  }
241 
243  const std::vector<uint8_t>& subject_hash) const
244  {
245  BOTAN_UNUSED(subject_hash);
246  throw Not_Implemented("Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256");
247  }
248 
249 std::shared_ptr<const X509_CRL> Certificate_Store_Windows::find_crl_for(const X509_Certificate& subject) const
250  {
251  // TODO: this could be implemented by using the CertFindCRLInStore function
252  BOTAN_UNUSED(subject);
253  return {};
254  }
255 }
std::vector< X509_DN > all_subjects() const override
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
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:213
void encode_into(class DER_Encoder &) const override
Definition: x509_dn.cpp:241
MechanismType type
std::shared_ptr< const X509_Certificate > find_cert_by_pubkey_sha1(const std::vector< uint8_t > &key_hash) const override
std::shared_ptr< const X509_Certificate > find_cert_by_raw_subject_dn_sha256(const std::vector< uint8_t > &subject_hash) const override
const X509_DN & subject_dn() const
Definition: x509cert.cpp:438
Definition: alg_id.cpp:13
#define BOTAN_UNUSED(...)
Definition: assert.h:142
std::shared_ptr< const X509_Certificate > find_cert(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
fe T
Definition: ge.cpp:37
std::shared_ptr< const X509_CRL > find_crl_for(const X509_Certificate &subject) const override