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