diff --git a/Cargo.toml b/Cargo.toml index 1d2849a..dbb7a32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,13 +8,15 @@ documentation = "http://steffengy.github.io/schannel-rs/doc/schannel/" repository = "https://github.com/steffengy/schannel-rs" readme = "README.md" keywords = ["windows", "schannel", "tls", "ssl", "https"] +build = "build.rs" [dependencies] +advapi32-sys = "0.2" crypt32-sys = "0.2" kernel32-sys = "0.2" lazy_static = "0.2" secur32-sys="0.2" winapi = "0.2.8" -[dev-dependencies] -advapi32-sys = "0.2.0" +[build-dependencies] +winapi-build = "0.1" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..4fa08ae --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +extern crate build; + +// FIXME https://github.com/retep998/winapi-rs/pull/319 +fn main() { + build::link("ncrypt", true) +} diff --git a/i686/libncrypt.a b/i686/libncrypt.a new file mode 100644 index 0000000..38becdd Binary files /dev/null and b/i686/libncrypt.a differ diff --git a/src/cert_context.rs b/src/cert_context.rs index d1b97c6..40451e5 100644 --- a/src/cert_context.rs +++ b/src/cert_context.rs @@ -5,11 +5,16 @@ use std::io; use std::mem; use std::os::windows::prelude::*; use std::ptr; - use crypt32; use winapi; -use Inner; +use {Inner, KeyHandlePriv}; +use key_handle::KeyHandle; + +// FIXME https://github.com/retep998/winapi-rs/pull/318 +const CRYPT_ACQUIRE_COMPARE_KEY_FLAG: winapi::DWORD = 0x4; +const CRYPT_ACQUIRE_SILENT_FLAG: winapi::DWORD = 0x40; +const CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG: winapi::DWORD = 0x10000; /// Wrapper of a winapi certificate, or a `PCCERT_CONTEXT`. #[derive(Debug)] @@ -104,6 +109,14 @@ impl CertContext { Ok(ret == 0) } + /// Returns a builder used to acquire the private key corresponding to this certificate. + pub fn private_key<'a>(&'a self) -> AcquirePrivateKeyOptions<'a> { + AcquirePrivateKeyOptions { + cert: self, + flags: 0, + } + } + /// Deletes this certificate from its certificate store. pub fn delete(self) -> io::Result<()> { unsafe { @@ -193,6 +206,50 @@ impl CertContext { } } +pub struct AcquirePrivateKeyOptions<'a> { + cert: &'a CertContext, + flags: winapi::DWORD, +} + +impl<'a> AcquirePrivateKeyOptions<'a> { + pub fn compare_key(&mut self, compare_key: bool) -> &mut AcquirePrivateKeyOptions<'a> { + self.flag(CRYPT_ACQUIRE_COMPARE_KEY_FLAG, compare_key) + } + + pub fn silent(&mut self, silent: bool) -> &mut AcquirePrivateKeyOptions<'a> { + self.flag(CRYPT_ACQUIRE_SILENT_FLAG, silent) + } + + fn flag(&mut self, flag: winapi::DWORD, set: bool) -> &mut AcquirePrivateKeyOptions<'a> { + if set { + self.flags |= flag; + } else { + self.flags &= !flag; + } + self + } + + pub fn acquire(&self) -> io::Result { + unsafe { + let flags = self.flags | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG; + let mut handle = 0; + let mut spec = 0; + let mut free = winapi::FALSE; + let res = crypt32::CryptAcquireCertificatePrivateKey(self.cert.0, + flags, + ptr::null_mut(), + &mut handle, + &mut spec, + &mut free); + if res != winapi::TRUE { + return Err(io::Error::last_os_error()); + } + assert!(free == winapi::TRUE); + Ok(KeyHandle::new(handle, spec)) + } + } +} + impl Clone for CertContext { fn clone(&self) -> CertContext { unsafe { diff --git a/src/cert_store.rs b/src/cert_store.rs index 516ad18..c8d9997 100644 --- a/src/cert_store.rs +++ b/src/cert_store.rs @@ -414,11 +414,16 @@ mod test { fn pfx_import() { let pfx = include_bytes!("../test/identity.p12"); let mut store = PfxImportOptions::new() - .no_persist_key(true) .include_extended_properties(true) .password("mypass") .import(pfx) .unwrap(); assert_eq!(store.certs().count(), 2); + let pkeys = store.certs() + .filter(|c| { + c.private_key().compare_key(true).silent(true).acquire().is_ok() + }) + .count(); + assert_eq!(pkeys, 1); } } diff --git a/src/key_handle.rs b/src/key_handle.rs new file mode 100644 index 0000000..5a2e11f --- /dev/null +++ b/src/key_handle.rs @@ -0,0 +1,38 @@ +//! Private keys. + +use advapi32; +use winapi; + +use KeyHandlePriv; + +/// A handle to a private key. +pub struct KeyHandle { + handle: winapi::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, + spec: winapi::DWORD, +} + +// FIXME https://github.com/retep998/winapi-rs/pull/319 +extern "system" { + fn NCryptFreeObject(handle: winapi::NCRYPT_HANDLE) -> winapi::SECURITY_STATUS; +} + +impl Drop for KeyHandle { + fn drop(&mut self) { + unsafe { + if self.spec == winapi::CERT_NCRYPT_KEY_SPEC { + NCryptFreeObject(self.handle); + } else { + advapi32::CryptReleaseContext(self.handle, 0); + } + } + } +} + +impl KeyHandlePriv for KeyHandle { + fn new(handle: winapi::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, spec: winapi::DWORD) -> KeyHandle { + KeyHandle { + handle: handle, + spec: spec, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 9fac1e7..4c470ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ #![warn(missing_docs)] #![allow(non_upper_case_globals)] +extern crate advapi32; extern crate crypt32; extern crate kernel32; extern crate secur32; @@ -12,9 +13,12 @@ extern crate lazy_static; use std::ptr; +use key_handle::KeyHandle; + pub mod cert_context; pub mod cert_store; /* pub */ mod ctl_context; +pub mod key_handle; pub mod schannel_cred; pub mod tls_stream; @@ -42,6 +46,10 @@ trait Inner { fn get_mut(&mut self) -> &mut T; } +trait KeyHandlePriv { + fn new(handle: winapi::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, spec: winapi::DWORD) -> KeyHandle; +} + unsafe fn secbuf(buftype: winapi::c_ulong, bytes: Option<&mut [u8]>) -> winapi::SecBuffer { let (ptr, len) = match bytes { diff --git a/x86_64/libncrypt.a b/x86_64/libncrypt.a new file mode 100644 index 0000000..df47475 Binary files /dev/null and b/x86_64/libncrypt.a differ