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