Skip to content

Commit

Permalink
Allow certificate private key lookup
Browse files Browse the repository at this point in the history
We have to pull some ncrypt stuff in temporarily until
retep998/winapi-rs#319 lands.
  • Loading branch information
sfackler committed Aug 17, 2016
1 parent cc0ae57 commit 701dbe1
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 5 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
6 changes: 6 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extern crate build;

// FIXME https://github.com/retep998/winapi-rs/pull/319
fn main() {
build::link("ncrypt", true)
}
Binary file added i686/libncrypt.a
Binary file not shown.
61 changes: 59 additions & 2 deletions src/cert_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<KeyHandle> {
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 {
Expand Down
7 changes: 6 additions & 1 deletion src/cert_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
38 changes: 38 additions & 0 deletions src/key_handle.rs
Original file line number Diff line number Diff line change
@@ -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,
}
}
}
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![warn(missing_docs)]
#![allow(non_upper_case_globals)]

extern crate advapi32;
extern crate crypt32;
extern crate kernel32;
extern crate secur32;
Expand All @@ -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;

Expand Down Expand Up @@ -42,6 +46,10 @@ trait Inner<T> {
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 {
Expand Down
Binary file added x86_64/libncrypt.a
Binary file not shown.

0 comments on commit 701dbe1

Please sign in to comment.