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 /// 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 open_current_user(which: &str) -> io::Result<CertStore>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 open_local_machine(which: &str) -> io::Result<CertStore>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. import_pkcs12(data: &[u8], password: Option<&str>) -> io::Result<CertStore>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. certs(&self) -> Certs180 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. add_cert(&mut self, cx: &CertContext, how: CertAdd) -> io::Result<CertContext>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. export_pkcs12(&self, password: &str) -> io::Result<Vec<u8>>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 next(&mut self) -> Option<CertContext>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. new() -> PfxImportOptions280 pub fn new() -> PfxImportOptions { 281 PfxImportOptions::default() 282 } 283 284 /// Sets the password to be used to decrypt the archive. password(&mut self, password: &str) -> &mut PfxImportOptions285 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. no_persist_key(&mut self, no_persist_key: bool) -> &mut PfxImportOptions293 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. include_extended_properties(&mut self, include_extended_properties: bool) -> &mut PfxImportOptions298 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 flag(&mut self, flag: winapi::DWORD, set: bool) -> &mut PfxImportOptions304 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. import(&self, data: &[u8]) -> io::Result<CertStore>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. new() -> io::Result<Memory>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. add_encoded_certificate(&mut self, cert: &[u8]) -> io::Result<CertContext>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. add_encoded_ctl(&mut self, ctl: &[u8]) -> io::Result<CtlContext>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`. into_store(self) -> CertStore401 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] load()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] create_ctl()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] pfx_import()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