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 
25 impl fmt::Debug for CertStore {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result26 	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
27 		fmt.debug_struct("CertStore").finish()
28 	}
29 }
30 
31 impl Drop for CertStore {
drop(&mut self)32     fn drop(&mut self) {
33         unsafe {
34             wincrypt::CertCloseStore(self.0, 0);
35         }
36     }
37 }
38 
39 impl Clone for CertStore {
clone(&self) -> CertStore40     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`.
49 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     /// Known valid values for `which` are "Root" and "My".
open_current_user(which: &str) -> io::Result<CertStore>106     pub fn open_current_user(which: &str) -> io::Result<CertStore> {
107         unsafe {
108             let data = OsStr::new(which)
109                              .encode_wide()
110                              .chain(Some(0))
111                              .collect::<Vec<_>>();
112             let store = wincrypt::CertOpenStore(wincrypt::CERT_STORE_PROV_SYSTEM_W as ntdef::LPCSTR,
113                                                 0,
114                                                 0,
115                                                 wincrypt::CERT_SYSTEM_STORE_CURRENT_USER,
116                                                 data.as_ptr() as *mut _);
117             if store.is_null() {
118                 Err(io::Error::last_os_error())
119             } else {
120                 Ok(CertStore(store))
121             }
122         }
123     }
124 
125     /// Opens up the specified key store within the context of the local
126     /// machine.
127     ///
128     /// Known valid values for `which` are "Root" and "My".
open_local_machine(which: &str) -> io::Result<CertStore>129     pub fn open_local_machine(which: &str) -> io::Result<CertStore> {
130         unsafe {
131             let data = OsStr::new(which)
132                              .encode_wide()
133                              .chain(Some(0))
134                              .collect::<Vec<_>>();
135             let store = wincrypt::CertOpenStore(wincrypt::CERT_STORE_PROV_SYSTEM_W as ntdef::LPCSTR,
136                                                 0,
137                                                 0,
138                                                 wincrypt::CERT_SYSTEM_STORE_LOCAL_MACHINE,
139                                                 data.as_ptr() as *mut _);
140             if store.is_null() {
141                 Err(io::Error::last_os_error())
142             } else {
143                 Ok(CertStore(store))
144             }
145         }
146     }
147 
148     /// Imports a PKCS#12-encoded key/certificate pair, returned as a
149     /// `CertStore` instance.
150     ///
151     /// The password must also be provided to decrypt the encoded data.
import_pkcs12(data: &[u8], password: Option<&str>) -> io::Result<CertStore>152     pub fn import_pkcs12(data: &[u8],
153                          password: Option<&str>)
154                          -> io::Result<CertStore> {
155         unsafe {
156             let mut blob = wincrypt::CRYPT_INTEGER_BLOB {
157                 cbData: data.len() as winapi::DWORD,
158                 pbData: data.as_ptr() as *mut u8,
159             };
160             let password = password.map(|s| {
161                 OsStr::new(s).encode_wide()
162                              .chain(Some(0))
163                              .collect::<Vec<_>>()
164             });
165             let password = password.as_ref().map(|s| s.as_ptr());
166             let password = password.unwrap_or(ptr::null());
167             let res = wincrypt::PFXImportCertStore(&mut blob,
168                                                    password,
169                                                    0);
170             if res.is_null() {
171                 Err(io::Error::last_os_error())
172             } else {
173                 Ok(CertStore(res))
174             }
175         }
176     }
177 
178     /// Returns an iterator over the certificates in this certificate store.
certs(&self) -> Certs179     pub fn certs(&self) -> Certs {
180         Certs { store: self, cur: None }
181     }
182 
183     /// Adds a certificate context to this store.
184     ///
185     /// This function will add the certificate specified in `cx` to this store.
186     /// A copy of the added certificate is returned.
add_cert(&mut self, cx: &CertContext, how: CertAdd) -> io::Result<CertContext>187     pub fn add_cert(&mut self,
188                     cx: &CertContext,
189                     how: CertAdd) -> io::Result<CertContext> {
190         unsafe {
191             let how = how as winapi::DWORD;
192             let mut ret = ptr::null();
193             let res = wincrypt::CertAddCertificateContextToStore(self.0,
194                                                                  cx.as_inner(),
195                                                                  how,
196                                                                  &mut ret);
197             if res != winapi::TRUE {
198                 Err(io::Error::last_os_error())
199             } else {
200                 Ok(CertContext::from_inner(ret))
201             }
202         }
203     }
204 
205     /// Exports this certificate store as a PKCS#12-encoded blob.
206     ///
207     /// The password specified will be the password used to unlock the returned
208     /// data.
export_pkcs12(&self, password: &str) -> io::Result<Vec<u8>>209     pub fn export_pkcs12(&self, password: &str) -> io::Result<Vec<u8>> {
210         unsafe {
211             let password = password.encode_utf16().chain(Some(0)).collect::<Vec<_>>();
212             let mut blob = wincrypt::CRYPT_DATA_BLOB {
213                 cbData: 0,
214                 pbData: 0 as *mut _,
215             };
216             let res = wincrypt::PFXExportCertStore(self.0,
217                                                    &mut blob,
218                                                    password.as_ptr(),
219                                                    wincrypt::EXPORT_PRIVATE_KEYS);
220             if res != winapi::TRUE {
221                 return Err(io::Error::last_os_error())
222             }
223             let mut ret = Vec::with_capacity(blob.cbData as usize);
224             blob.pbData = ret.as_mut_ptr();
225             let res = wincrypt::PFXExportCertStore(self.0,
226                                                    &mut blob,
227                                                    password.as_ptr(),
228                                                    wincrypt::EXPORT_PRIVATE_KEYS);
229             if res != winapi::TRUE {
230                 return Err(io::Error::last_os_error())
231             }
232             ret.set_len(blob.cbData as usize);
233             Ok(ret)
234         }
235     }
236 }
237 
238 /// An iterator over the certificates contained in a `CertStore`, returned by
239 /// `CertStore::iter`
240 pub struct Certs<'a> {
241     store: &'a CertStore,
242     cur: Option<CertContext>,
243 }
244 
245 impl<'a> Iterator for Certs<'a> {
246     type Item = CertContext;
247 
next(&mut self) -> Option<CertContext>248     fn next(&mut self) -> Option<CertContext> {
249         unsafe {
250             let cur = self.cur.take().map(|p| {
251                 let ptr = p.as_inner();
252                 mem::forget(p);
253                 ptr
254             });
255             let cur = cur.unwrap_or(ptr::null_mut());
256             let next = wincrypt::CertEnumCertificatesInStore(self.store.0, cur);
257 
258             if next.is_null() {
259                 self.cur = None;
260                 None
261             } else {
262                 let next = CertContext::from_inner(next);
263                 self.cur = Some(next.clone());
264                 Some(next)
265             }
266         }
267     }
268 }
269 
270 /// A builder type for imports of PKCS #12 archives.
271 #[derive(Default)]
272 pub struct PfxImportOptions {
273     password: Option<Vec<u16>>,
274     flags: winapi::DWORD,
275 }
276 
277 impl PfxImportOptions {
278     /// Returns a new `PfxImportOptions` with default settings.
new() -> PfxImportOptions279     pub fn new() -> PfxImportOptions {
280         PfxImportOptions::default()
281     }
282 
283     /// Sets the password to be used to decrypt the archive.
password(&mut self, password: &str) -> &mut PfxImportOptions284     pub fn password(&mut self, password: &str) -> &mut PfxImportOptions {
285         self.password = Some(password.encode_utf16().chain(Some(0)).collect());
286         self
287     }
288 
289     /// If set, the private key in the archive will not be persisted.
290     ///
291     /// If not set, private keys are persisted on disk and must be manually deleted.
no_persist_key(&mut self, no_persist_key: bool) -> &mut PfxImportOptions292     pub fn no_persist_key(&mut self, no_persist_key: bool) -> &mut PfxImportOptions {
293         self.flag(wincrypt::PKCS12_NO_PERSIST_KEY, no_persist_key)
294     }
295 
296     /// If set, all extended properties of the certificate will be imported.
include_extended_properties(&mut self, include_extended_properties: bool) -> &mut PfxImportOptions297     pub fn include_extended_properties(&mut self,
298                                        include_extended_properties: bool)
299                                        -> &mut PfxImportOptions {
300         self.flag(wincrypt::PKCS12_INCLUDE_EXTENDED_PROPERTIES, include_extended_properties)
301     }
302 
flag(&mut self, flag: winapi::DWORD, set: bool) -> &mut PfxImportOptions303     fn flag(&mut self, flag: winapi::DWORD, set: bool) -> &mut PfxImportOptions {
304         if set {
305             self.flags |= flag;
306         } else {
307             self.flags &= !flag;
308         }
309         self
310     }
311 
312     /// Imports certificates from a PKCS #12 archive, returning a `CertStore` containing them.
import(&self, data: &[u8]) -> io::Result<CertStore>313     pub fn import(&self, data: &[u8]) -> io::Result<CertStore> {
314         unsafe {
315             let mut blob = wincrypt::CRYPT_DATA_BLOB {
316                 cbData: cmp::min(data.len(), winapi::DWORD::max_value() as usize) as winapi::DWORD,
317                 pbData: data.as_ptr() as *const _ as *mut _,
318             };
319             let password = self.password.as_ref().map_or(ptr::null(), |p| p.as_ptr());
320 
321             let store = wincrypt::PFXImportCertStore(&mut blob, password, self.flags);
322             if store.is_null() {
323                 return Err(io::Error::last_os_error());
324             }
325             Ok(CertStore(store))
326         }
327     }
328 }
329 
330 /// Representation of an in-memory certificate store.
331 ///
332 /// Internally this contains a `CertStore` which this type can be converted to.
333 pub struct Memory(CertStore);
334 
335 impl Memory {
336     /// Creates a new in-memory certificate store which certificates and CTLs
337     /// can be added to.
338     ///
339     /// Initially the returned certificate store contains no certificates.
new() -> io::Result<Memory>340     pub fn new() -> io::Result<Memory> {
341         unsafe {
342             let store = wincrypt::CertOpenStore(wincrypt::CERT_STORE_PROV_MEMORY as ntdef::LPCSTR,
343                                                 0,
344                                                 0,
345                                                 0,
346                                                 ptr::null_mut());
347             if store.is_null() {
348                 Err(io::Error::last_os_error())
349             } else {
350                 Ok(Memory(CertStore(store)))
351             }
352         }
353     }
354 
355     /// Adds a new certificate to this memory store.
356     ///
357     /// For example the bytes could be a DER-encoded certificate.
add_encoded_certificate(&mut self, cert: &[u8]) -> io::Result<CertContext>358     pub fn add_encoded_certificate(&mut self, cert: &[u8]) -> io::Result<CertContext> {
359         unsafe {
360             let mut cert_context = ptr::null();
361 
362             let res = wincrypt::CertAddEncodedCertificateToStore((self.0).0,
363                                                                  wincrypt::X509_ASN_ENCODING |
364                                                                  wincrypt::PKCS_7_ASN_ENCODING,
365                                                                  cert.as_ptr() as *const _,
366                                                                  cert.len() as winapi::DWORD,
367                                                                  wincrypt::CERT_STORE_ADD_ALWAYS,
368                                                                  &mut cert_context);
369             if res == winapi::TRUE {
370                 Ok(CertContext::from_inner(cert_context))
371             } else {
372                 Err(io::Error::last_os_error())
373             }
374         }
375     }
376 
377     /// Adds a new CTL to this memory store, in its encoded form.
378     ///
379     /// This can be created through the `ctl_context::Builder` type.
add_encoded_ctl(&mut self, ctl: &[u8]) -> io::Result<CtlContext>380     pub fn add_encoded_ctl(&mut self, ctl: &[u8]) -> io::Result<CtlContext> {
381         unsafe {
382             let mut ctl_context = ptr::null();
383 
384             let res = wincrypt::CertAddEncodedCTLToStore((self.0).0,
385                                                          wincrypt::X509_ASN_ENCODING |
386                                                          wincrypt::PKCS_7_ASN_ENCODING,
387                                                          ctl.as_ptr() as *const _,
388                                                          ctl.len() as winapi::DWORD,
389                                                          wincrypt::CERT_STORE_ADD_ALWAYS,
390                                                          &mut ctl_context);
391             if res == winapi::TRUE {
392                 Ok(CtlContext::from_inner(ctl_context))
393             } else {
394                 Err(io::Error::last_os_error())
395             }
396         }
397     }
398 
399     /// Consumes this memory store, returning the underlying `CertStore`.
into_store(self) -> CertStore400     pub fn into_store(self) -> CertStore {
401         self.0
402     }
403 }
404 
405 #[cfg(test)]
406 mod test {
407     use super::*;
408     use crate::ctl_context::CtlContext;
409 
410     #[test]
load()411     fn load() {
412         let cert = include_bytes!("../test/cert.der");
413         let mut store = Memory::new().unwrap();
414         store.add_encoded_certificate(cert).unwrap();
415     }
416 
417     #[test]
create_ctl()418     fn create_ctl() {
419         let cert = include_bytes!("../test/self-signed.badssl.com.cer");
420         let mut store = Memory::new().unwrap();
421         let cert = store.add_encoded_certificate(cert).unwrap();
422 
423         CtlContext::builder()
424             .certificate(cert)
425             .usage("1.3.6.1.4.1.311.2.2.2")
426             .encode_and_sign()
427             .unwrap();
428     }
429 
430     #[test]
pfx_import()431     fn pfx_import() {
432         let pfx = include_bytes!("../test/identity.p12");
433         let store = PfxImportOptions::new()
434                         .include_extended_properties(true)
435                         .password("mypass")
436                         .import(pfx)
437                         .unwrap();
438         assert_eq!(store.certs().count(), 2);
439         let pkeys = store.certs()
440                          .filter(|c| {
441                              c.private_key().compare_key(true).silent(true).acquire().is_ok()
442                          })
443                          .count();
444         assert_eq!(pkeys, 1);
445     }
446 }
447