Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ readme = "README.md"
[features]
vendored = ["openssl/vendored"]

[dependencies]
lazy_static = "1.0"
rustc-serialize = "0.3"

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
security-framework = "0.3.1"
security-framework-sys = "0.3.1"
lazy_static = "1.0"
libc = "0.2"
tempfile = "3.0"

Expand Down
34 changes: 25 additions & 9 deletions src/imp/openssl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ use self::openssl::error::ErrorStack;
use self::openssl::hash::MessageDigest;
use self::openssl::nid::Nid;
use self::openssl::pkcs12::Pkcs12;
use self::openssl::pkey::PKey;
use self::openssl::pkey::{PKey, Private};
use self::openssl::ssl::{
self, MidHandshakeSslStream, SslAcceptor, SslConnector, SslContextBuilder, SslMethod,
SslVerifyMode,
};
use self::openssl::x509::{X509, X509VerifyResult};
use self::openssl::x509::{X509VerifyResult, X509};
use std::error;
use std::fmt;
use std::io;
use std::sync::{Once, ONCE_INIT};
use pem;

use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder};
use self::openssl::pkey::Private;

#[cfg(have_min_max_version)]
fn supported_protocols(
Expand Down Expand Up @@ -155,7 +155,7 @@ impl From<ErrorStack> for Error {
pub struct Identity {
pkey: PKey<Private>,
cert: X509,
chain: Vec<X509>,
chain: Option<Vec<X509>>,
}

impl Identity {
Expand All @@ -165,7 +165,19 @@ impl Identity {
Ok(Identity {
pkey: parsed.pkey,
cert: parsed.cert,
chain: parsed.chain.into_iter().flat_map(|x| x).collect(),
chain: parsed.chain.map(|stack| stack.into_iter().collect()),
})
}

pub fn from_pkcs8(buf: &[u8], key: &[u8]) -> Result<Identity, Error> {
let pkey = PKey::private_key_from_pem(key)?;
let p_block = pem::PemBlock::new(buf);
let mut chain: Vec<X509> = p_block.map(|buf| X509::from_pem(buf).unwrap()).collect();
let cert = chain.pop();
Ok(Identity {
pkey,
cert: cert.expect("need identity cert"),
chain: Some(chain),
})
}
}
Expand Down Expand Up @@ -265,8 +277,10 @@ impl TlsConnector {
if let Some(ref identity) = builder.identity {
connector.set_certificate(&identity.0.cert)?;
connector.set_private_key(&identity.0.pkey)?;
for cert in identity.0.chain.iter().rev() {
connector.add_extra_chain_cert(cert.to_owned())?;
if let Some(ref chain) = identity.0.chain {
for cert in chain.iter().rev() {
connector.add_extra_chain_cert(cert.to_owned())?;
}
}
}
supported_protocols(builder.min_protocol, builder.max_protocol, &mut connector)?;
Expand Down Expand Up @@ -314,8 +328,10 @@ impl TlsAcceptor {
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
acceptor.set_private_key(&builder.identity.0.pkey)?;
acceptor.set_certificate(&builder.identity.0.cert)?;
for cert in builder.identity.0.chain.iter().rev() {
acceptor.add_extra_chain_cert(cert.to_owned())?;
if let Some(ref chain) = builder.identity.0.chain {
for cert in chain.iter().rev() {
acceptor.add_extra_chain_cert(cert.to_owned())?;
}
}
supported_protocols(builder.min_protocol, builder.max_protocol, &mut acceptor)?;

Expand Down
38 changes: 37 additions & 1 deletion src/imp/schannel.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
extern crate schannel;

use self::schannel::cert_context::{CertContext, HashAlgorithm};
use self::schannel::cert_context::{CertContext, HashAlgorithm, KeySpec};
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
use self::schannel::crypt_prov::{AcquireOptions, ProviderType};
use self::schannel::tls_stream;
use std::error;
use std::fmt;
Expand Down Expand Up @@ -96,6 +97,41 @@ impl Identity {

Ok(Identity { cert: identity })
}

pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> {
let mut store = Memory::new()?.into_store();
let mut cert_iter = crate::pem::PemBlock::new(pem).into_iter();
let leaf = cert_iter.next().unwrap();
let cert = CertContext::from_pem(std::str::from_utf8(leaf).unwrap()).unwrap();

let mut options = AcquireOptions::new();
options.container("schannel");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to remember there being problems on windows if the same container name is used multiple times. Probably worth a test that parses a couple of identities to confirm.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the test case that caused issues on my previous attempt: b0b9164

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this test still needs to be added.

let type_ = ProviderType::rsa_full();

let mut container = match options.acquire(type_) {
Ok(container) => container,
Err(_) => options.new_keyset(true).acquire(type_).unwrap(),
};
let key = crate::pem::pem_to_der(key, Some(crate::pem::PEM_PRIVATE_KEY)).unwrap();
container.import()
.import_pkcs8(&key)
.unwrap();

cert.set_key_prov_info()
.container("schannel")
.type_(type_)
.keep_open(true)
.key_spec(KeySpec::key_exchange())
.set()
.unwrap();
let mut context = store.add_cert(&cert, CertAdd::Always)?;

for int_cert in cert_iter {
let certificate = Certificate::from_pem(int_cert)?;
context = store.add_cert(&certificate.0, schannel::cert_store::CertAdd::Always)?;
}
Ok(Identity{cert: context})
}
}

#[derive(Clone)]
Expand Down
30 changes: 30 additions & 0 deletions src/imp/security_framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use self::security_framework::os::macos::certificate::{PropertyType, SecCertific
#[cfg(not(target_os = "ios"))]
use self::security_framework::os::macos::certificate_oids::CertificateOid;
#[cfg(not(target_os = "ios"))]
use self::security_framework::os::macos::identity::SecIdentityExt;
#[cfg(not(target_os = "ios"))]
use self::security_framework::os::macos::import_export::{ImportOptions, SecItems};
#[cfg(not(target_os = "ios"))]
use self::security_framework::os::macos::keychain::{self, KeychainSettings, SecKeychain};
Expand Down Expand Up @@ -85,6 +87,34 @@ pub struct Identity {
}

impl Identity {
pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> {
let dir = TempDir::new().unwrap();
let keychain = keychain::CreateOptions::new()
.password("password")
.create(dir.path().join("identity.keychain"))
.unwrap();

let mut items = SecItems::default();

ImportOptions::new()
.filename("key.pem")
.items(&mut items)
.keychain(&keychain)
.import(&key)?;

ImportOptions::new()
.filename("chain.pem")
.items(&mut items)
.keychain(&keychain)
.import(&pem)?;

let ident = SecIdentity::with_certificate(&[keychain], &items.certificates[0])?;
Ok(Identity {
identity: ident,
chain: items.certificates
})
}

pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
let mut imports = Identity::import_options(buf, pass)?;
let import = imports.pop().unwrap();
Expand Down
11 changes: 10 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,14 @@
#![warn(missing_docs)]

#[macro_use]
#[cfg(any(target_os = "macos", target_os = "ios"))]
extern crate lazy_static;

#[cfg(test)]
extern crate hex;

extern crate rustc_serialize;
mod pem;

use std::any::Any;
use std::error;
use std::fmt;
Expand Down Expand Up @@ -183,6 +185,13 @@ impl Identity {
let identity = imp::Identity::from_pkcs12(der, password)?;
Ok(Identity(identity))
}

/// buf is the contents of a file containing a chain of PEM encoded certificates
/// key is the contents of a file containing a PEM encoded private key
pub fn from_pkcs8(buf: &[u8], key: &[u8]) -> Result<Identity> {
let identity = imp::Identity::from_pkcs8(buf, key)?;
Ok(Identity(identity))
}
}

/// An X509 certificate.
Expand Down
Loading