1 //! Bindings to winapi's certificate-store related APIs.
2
3 use std::cmp;
4 use std::ffi::OsStr;
5 use std::fmt;
6 use std::io;
7 use std::mem;
8 use std::os::windows::prelude::*;
9 use std::ptr;
10 use winapi::shared::minwindef as winapi;
11 use winapi::shared::ntdef;
12 use winapi::um::wincrypt;
13
14 use crate::cert_context::CertContext;
15 use crate::ctl_context::CtlContext;
16
17 use crate::Inner;
18
19 /// Representation of certificate store on Windows, wrapping a `HCERTSTORE`.
20 pub struct CertStore(wincrypt::HCERTSTORE);
21
22 unsafe impl Sync for CertStore {}
23 unsafe impl Send for CertStore {}
24
as_u32(&self) -> u3225 impl fmt::Debug for CertStore {
26 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
27 fmt.debug_struct("CertStore").finish()
28 }
29 }
from_str(str: &str) -> Result<Version>30
31 impl Drop for CertStore {
32 fn drop(&mut self) {
33 unsafe {
34 wincrypt::CertCloseStore(self.0, 0);
35 }
36 }
37 }
38
from_u32(val: u32) -> Result<Version>39 impl Clone for CertStore {
40 fn clone(&self) -> CertStore {
41 unsafe { CertStore(wincrypt::CertDuplicateStore(self.0)) }
42 }
43 }
44
45 inner!(CertStore, wincrypt::HCERTSTORE);
46
47 /// Argument to the `add_cert` function indicating how a certificate should be
48 /// added to a `CertStore`.
default() -> Version49 pub enum CertAdd {
50 /// The function makes no check for an existing matching certificate or link
51 /// to a matching certificate. A new certificate is always added to the
52 /// store. This can lead to duplicates in a store.
53 Always = wincrypt::CERT_STORE_ADD_ALWAYS as isize,
54
55 /// If a matching certificate or a link to a matching certificate exists,
56 /// the operation fails.
57 New = wincrypt::CERT_STORE_ADD_NEW as isize,
58
59 /// If a matching certificate or a link to a matching certificate exists and
60 /// the NotBefore time of the existing context is equal to or greater than
61 /// the NotBefore time of the new context being added, the operation fails.
62 ///
63 /// If the NotBefore time of the existing context is less than the NotBefore
64 /// time of the new context being added, the existing certificate or link is
65 /// deleted and a new certificate is created and added to the store. If a
66 /// matching certificate or a link to a matching certificate does not exist,
67 /// a new link is added.
68 Newer = wincrypt::CERT_STORE_ADD_NEWER as isize,
69
70 /// If a matching certificate or a link to a matching certificate exists and
71 /// the NotBefore time of the existing context is equal to or greater than
72 /// the NotBefore time of the new context being added, the operation fails.
73 ///
74 /// If the NotBefore time of the existing context is less than the NotBefore
75 /// time of the new context being added, the existing context is deleted
76 /// before creating and adding the new context. The new added context
77 /// inherits properties from the existing certificate.
78 NewerInheritProperties = wincrypt::CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES as isize,
79
80 /// If a link to a matching certificate exists, that existing certificate or
81 /// link is deleted and a new certificate is created and added to the store.
82 /// If a matching certificate or a link to a matching certificate does not
83 /// exist, a new link is added.
84 ReplaceExisting = wincrypt::CERT_STORE_ADD_REPLACE_EXISTING as isize,
85
86 /// If a matching certificate exists in the store, the existing context is
87 /// not replaced. The existing context inherits properties from the new
88 /// certificate.
89 ReplaceExistingInheritProperties =
90 wincrypt::CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES as isize,
91
92 /// If a matching certificate or a link to a matching certificate exists,
93 /// that existing certificate or link is used and properties from the
94 /// new certificate are added. The function does not fail, but it does
95 /// not add a new context. The existing context is duplicated and returned.
96 ///
97 /// If a matching certificate or a link to a matching certificate does
98 /// not exist, a new certificate is added.
99 UseExisting = wincrypt::CERT_STORE_ADD_USE_EXISTING as isize,
100 }
101
102 impl CertStore {
103 /// Opens up the specified key store within the context of the current user.
104 ///
105 /// Common valid values for `which` are "My", "Root", "Trust", "CA".
106 /// Additonal MSDN docs https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certopenstore#remarks
107 pub fn open_current_user(which: &str) -> io::Result<CertStore> {
108 unsafe {
109 let data = OsStr::new(which)
110 .encode_wide()
111 .chain(Some(0))
112 .collect::<Vec<_>>();
113 let store = wincrypt::CertOpenStore(wincrypt::CERT_STORE_PROV_SYSTEM_W as ntdef::LPCSTR,
114 0,
115 0,
116 wincrypt::CERT_SYSTEM_STORE_CURRENT_USER,
117 data.as_ptr() as *mut _);
118 if store.is_null() {
119 Err(io::Error::last_os_error())
120 } else {
121 Ok(CertStore(store))
122 }
123 }
124 }
125
126 /// Opens up the specified key store within the context of the local machine.
127 ///
128 /// Common valid values for `which` are "My", "Root", "Trust", "CA".
129 /// Additonal MSDN docs https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certopenstore#remarks
130 pub fn open_local_machine(which: &str) -> io::Result<CertStore> {
131 unsafe {
132 let data = OsStr::new(which)
133 .encode_wide()
134 .chain(Some(0))
135 .collect::<Vec<_>>();
136 let store = wincrypt::CertOpenStore(wincrypt::CERT_STORE_PROV_SYSTEM_W as ntdef::LPCSTR,
137 0,
138 0,
139 wincrypt::CERT_SYSTEM_STORE_LOCAL_MACHINE,
140 data.as_ptr() as *mut _);
141 if store.is_null() {
142 Err(io::Error::last_os_error())
143 } else {
144 Ok(CertStore(store))
145 }
146 }
147 }
148
149 /// Imports a PKCS#12-encoded key/certificate pair, returned as a
150 /// `CertStore` instance.
151 ///
152 /// The password must also be provided to decrypt the encoded data.
153 pub fn import_pkcs12(data: &[u8],
154 password: Option<&str>)
155 -> io::Result<CertStore> {
156 unsafe {
157 let mut blob = wincrypt::CRYPT_INTEGER_BLOB {
158 cbData: data.len() as winapi::DWORD,
159 pbData: data.as_ptr() as *mut u8,
160 };
161 let password = password.map(|s| {
162 OsStr::new(s).encode_wide()
163 .chain(Some(0))
164 .collect::<Vec<_>>()
165 });
166 let password = password.as_ref().map(|s| s.as_ptr());
167 let password = password.unwrap_or(ptr::null());
168 let res = wincrypt::PFXImportCertStore(&mut blob,
169 password,
170 0);
171 if res.is_null() {
172 Err(io::Error::last_os_error())
173 } else {
174 Ok(CertStore(res))
175 }
176 }
177 }
178
179 /// Returns an iterator over the certificates in this certificate store.
180 pub fn certs(&self) -> Certs {
181 Certs { store: self, cur: None }
182 }
183
184 /// Adds a certificate context to this store.
185 ///
186 /// This function will add the certificate specified in `cx` to this store.
187 /// A copy of the added certificate is returned.
188 pub fn add_cert(&mut self,
189 cx: &CertContext,
190 how: CertAdd) -> io::Result<CertContext> {
191 unsafe {
192 let how = how as winapi::DWORD;
193 let mut ret = ptr::null();
194 let res = wincrypt::CertAddCertificateContextToStore(self.0,
195 cx.as_inner(),
196 how,
197 &mut ret);
198 if res != winapi::TRUE {
199 Err(io::Error::last_os_error())
200 } else {
201 Ok(CertContext::from_inner(ret))
202 }
203 }
204 }
205
206 /// Exports this certificate store as a PKCS#12-encoded blob.
207 ///
208 /// The password specified will be the password used to unlock the returned
209 /// data.
210 pub fn export_pkcs12(&self, password: &str) -> io::Result<Vec<u8>> {
211 unsafe {
212 let password = password.encode_utf16().chain(Some(0)).collect::<Vec<_>>();
213 let mut blob = wincrypt::CRYPT_DATA_BLOB {
214 cbData: 0,
215 pbData: 0 as *mut _,
216 };
217 let res = wincrypt::PFXExportCertStore(self.0,
218 &mut blob,
219 password.as_ptr(),
220 wincrypt::EXPORT_PRIVATE_KEYS);
221 if res != winapi::TRUE {
222 return Err(io::Error::last_os_error())
223 }
224 let mut ret = Vec::with_capacity(blob.cbData as usize);
225 blob.pbData = ret.as_mut_ptr();
226 let res = wincrypt::PFXExportCertStore(self.0,
227 &mut blob,
228 password.as_ptr(),
229 wincrypt::EXPORT_PRIVATE_KEYS);
230 if res != winapi::TRUE {
231 return Err(io::Error::last_os_error())
232 }
233 ret.set_len(blob.cbData as usize);
234 Ok(ret)
235 }
236 }
237 }
238
239 /// An iterator over the certificates contained in a `CertStore`, returned by
240 /// `CertStore::iter`
241 pub struct Certs<'a> {
242 store: &'a CertStore,
243 cur: Option<CertContext>,
244 }
245
246 impl<'a> Iterator for Certs<'a> {
247 type Item = CertContext;
248
249 fn next(&mut self) -> Option<CertContext> {
250 unsafe {
251 let cur = self.cur.take().map(|p| {
252 let ptr = p.as_inner();
253 mem::forget(p);
254 ptr
255 });
256 let cur = cur.unwrap_or(ptr::null_mut());
257 let next = wincrypt::CertEnumCertificatesInStore(self.store.0, cur);
258
259 if next.is_null() {
260 self.cur = None;
261 None
262 } else {
263 let next = CertContext::from_inner(next);
264 self.cur = Some(next.clone());
265 Some(next)
266 }
267 }
268 }
269 }
270
271 /// A builder type for imports of PKCS #12 archives.
272 #[derive(Default)]
273 pub struct PfxImportOptions {
274 password: Option<Vec<u16>>,
275 flags: winapi::DWORD,
276 }
277
278 impl PfxImportOptions {
279 /// Returns a new `PfxImportOptions` with default settings.
280 pub fn new() -> PfxImportOptions {
281 PfxImportOptions::default()
282 }
283
284 /// Sets the password to be used to decrypt the archive.
285 pub fn password(&mut self, password: &str) -> &mut PfxImportOptions {
286 self.password = Some(password.encode_utf16().chain(Some(0)).collect());
287 self
288 }
289
290 /// If set, the private key in the archive will not be persisted.
291 ///
292 /// If not set, private keys are persisted on disk and must be manually deleted.
293 pub fn no_persist_key(&mut self, no_persist_key: bool) -> &mut PfxImportOptions {
294 self.flag(wincrypt::PKCS12_NO_PERSIST_KEY, no_persist_key)
295 }
296
297 /// If set, all extended properties of the certificate will be imported.
298 pub fn include_extended_properties(&mut self,
299 include_extended_properties: bool)
300 -> &mut PfxImportOptions {
301 self.flag(wincrypt::PKCS12_INCLUDE_EXTENDED_PROPERTIES, include_extended_properties)
302 }
303
304 fn flag(&mut self, flag: winapi::DWORD, set: bool) -> &mut PfxImportOptions {
305 if set {
306 self.flags |= flag;
307 } else {
308 self.flags &= !flag;
309 }
310 self
311 }
312
313 /// Imports certificates from a PKCS #12 archive, returning a `CertStore` containing them.
314 pub fn import(&self, data: &[u8]) -> io::Result<CertStore> {
315 unsafe {
316 let mut blob = wincrypt::CRYPT_DATA_BLOB {
317 cbData: cmp::min(data.len(), winapi::DWORD::max_value() as usize) as winapi::DWORD,
318 pbData: data.as_ptr() as *const _ as *mut _,
319 };
320 let password = self.password.as_ref().map_or(ptr::null(), |p| p.as_ptr());
321
322 let store = wincrypt::PFXImportCertStore(&mut blob, password, self.flags);
323 if store.is_null() {
324 return Err(io::Error::last_os_error());
325 }
326 Ok(CertStore(store))
327 }
328 }
329 }
330
331 /// Representation of an in-memory certificate store.
332 ///
333 /// Internally this contains a `CertStore` which this type can be converted to.
334 pub struct Memory(CertStore);
335
336 impl Memory {
337 /// Creates a new in-memory certificate store which certificates and CTLs
338 /// can be added to.
339 ///
340 /// Initially the returned certificate store contains no certificates.
341 pub fn new() -> io::Result<Memory> {
342 unsafe {
343 let store = wincrypt::CertOpenStore(wincrypt::CERT_STORE_PROV_MEMORY as ntdef::LPCSTR,
344 0,
345 0,
346 0,
347 ptr::null_mut());
348 if store.is_null() {
349 Err(io::Error::last_os_error())
350 } else {
351 Ok(Memory(CertStore(store)))
352 }
353 }
354 }
355
356 /// Adds a new certificate to this memory store.
357 ///
358 /// For example the bytes could be a DER-encoded certificate.
359 pub fn add_encoded_certificate(&mut self, cert: &[u8]) -> io::Result<CertContext> {
360 unsafe {
361 let mut cert_context = ptr::null();
362
363 let res = wincrypt::CertAddEncodedCertificateToStore((self.0).0,
364 wincrypt::X509_ASN_ENCODING |
365 wincrypt::PKCS_7_ASN_ENCODING,
366 cert.as_ptr() as *const _,
367 cert.len() as winapi::DWORD,
368 wincrypt::CERT_STORE_ADD_ALWAYS,
369 &mut cert_context);
370 if res == winapi::TRUE {
371 Ok(CertContext::from_inner(cert_context))
372 } else {
373 Err(io::Error::last_os_error())
374 }
375 }
376 }
377
378 /// Adds a new CTL to this memory store, in its encoded form.
379 ///
380 /// This can be created through the `ctl_context::Builder` type.
381 pub fn add_encoded_ctl(&mut self, ctl: &[u8]) -> io::Result<CtlContext> {
382 unsafe {
383 let mut ctl_context = ptr::null();
384
385 let res = wincrypt::CertAddEncodedCTLToStore((self.0).0,
386 wincrypt::X509_ASN_ENCODING |
387 wincrypt::PKCS_7_ASN_ENCODING,
388 ctl.as_ptr() as *const _,
389 ctl.len() as winapi::DWORD,
390 wincrypt::CERT_STORE_ADD_ALWAYS,
391 &mut ctl_context);
392 if res == winapi::TRUE {
393 Ok(CtlContext::from_inner(ctl_context))
394 } else {
395 Err(io::Error::last_os_error())
396 }
397 }
398 }
399
400 /// Consumes this memory store, returning the underlying `CertStore`.
401 pub fn into_store(self) -> CertStore {
402 self.0
403 }
404 }
405
406 #[cfg(test)]
407 mod test {
408 use super::*;
409 use crate::ctl_context::CtlContext;
410
411 #[test]
412 fn load() {
413 let cert = include_bytes!("../test/cert.der");
414 let mut store = Memory::new().unwrap();
415 store.add_encoded_certificate(cert).unwrap();
416 }
417
418 #[test]
419 fn create_ctl() {
420 let cert = include_bytes!("../test/self-signed.badssl.com.cer");
421 let mut store = Memory::new().unwrap();
422 let cert = store.add_encoded_certificate(cert).unwrap();
423
424 CtlContext::builder()
425 .certificate(cert)
426 .usage("1.3.6.1.4.1.311.2.2.2")
427 .encode_and_sign()
428 .unwrap();
429 }
430
431 #[test]
432 fn pfx_import() {
433 let pfx = include_bytes!("../test/identity.p12");
434 let store = PfxImportOptions::new()
435 .include_extended_properties(true)
436 .password("mypass")
437 .import(pfx)
438 .unwrap();
439 assert_eq!(store.certs().count(), 2);
440 let pkeys = store.certs()
441 .filter(|c| {
442 c.private_key().compare_key(true).silent(true).acquire().is_ok()
443 })
444 .count();
445 assert_eq!(pkeys, 1);
446 }
447 }
448