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