Skip to content

Commit eb269b8

Browse files
committed
Implement PKCS8 certificate support for all three backends.
1 parent 7d34900 commit eb269b8

File tree

9 files changed

+448
-15
lines changed

9 files changed

+448
-15
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ readme = "README.md"
1010
[features]
1111
vendored = ["openssl/vendored"]
1212

13+
[dependencies]
14+
lazy_static = "1.0"
15+
rustc-serialize = "0.3"
16+
1317
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
1418
security-framework = "0.3.1"
1519
security-framework-sys = "0.3.1"
16-
lazy_static = "1.0"
1720
libc = "0.2"
1821
tempfile = "3.0"
1922

src/imp/openssl.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ use self::openssl::error::ErrorStack;
55
use self::openssl::hash::MessageDigest;
66
use self::openssl::nid::Nid;
77
use self::openssl::pkcs12::Pkcs12;
8-
use self::openssl::pkey::PKey;
8+
use self::openssl::pkey::{PKey, Private};
99
use self::openssl::ssl::{
1010
self, MidHandshakeSslStream, SslAcceptor, SslConnector, SslContextBuilder, SslMethod,
1111
SslVerifyMode,
1212
};
13-
use self::openssl::x509::{X509, X509VerifyResult};
13+
use self::openssl::x509::{X509VerifyResult, X509};
1414
use std::error;
1515
use std::fmt;
1616
use std::io;
1717
use std::sync::{Once, ONCE_INIT};
18+
use pem;
1819

1920
use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder};
20-
use self::openssl::pkey::Private;
2121

2222
#[cfg(have_min_max_version)]
2323
fn supported_protocols(
@@ -155,7 +155,7 @@ impl From<ErrorStack> for Error {
155155
pub struct Identity {
156156
pkey: PKey<Private>,
157157
cert: X509,
158-
chain: Vec<X509>,
158+
chain: Option<Vec<X509>>,
159159
}
160160

161161
impl Identity {
@@ -165,7 +165,19 @@ impl Identity {
165165
Ok(Identity {
166166
pkey: parsed.pkey,
167167
cert: parsed.cert,
168-
chain: parsed.chain.into_iter().flat_map(|x| x).collect(),
168+
chain: parsed.chain.map(|stack| stack.into_iter().collect()),
169+
})
170+
}
171+
172+
pub fn from_pkcs8(buf: &[u8], key: &[u8]) -> Result<Identity, Error> {
173+
let pkey = PKey::private_key_from_pem(key)?;
174+
let p_block = pem::PemBlock::new(buf);
175+
let mut chain: Vec<X509> = p_block.map(|buf| X509::from_pem(buf).unwrap()).collect();
176+
let cert = chain.pop();
177+
Ok(Identity {
178+
pkey,
179+
cert: cert.expect("need identity cert"),
180+
chain: Some(chain),
169181
})
170182
}
171183
}
@@ -265,8 +277,10 @@ impl TlsConnector {
265277
if let Some(ref identity) = builder.identity {
266278
connector.set_certificate(&identity.0.cert)?;
267279
connector.set_private_key(&identity.0.pkey)?;
268-
for cert in identity.0.chain.iter().rev() {
269-
connector.add_extra_chain_cert(cert.to_owned())?;
280+
if let Some(ref chain) = identity.0.chain {
281+
for cert in chain.iter().rev() {
282+
connector.add_extra_chain_cert(cert.to_owned())?;
283+
}
270284
}
271285
}
272286
supported_protocols(builder.min_protocol, builder.max_protocol, &mut connector)?;
@@ -314,8 +328,10 @@ impl TlsAcceptor {
314328
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
315329
acceptor.set_private_key(&builder.identity.0.pkey)?;
316330
acceptor.set_certificate(&builder.identity.0.cert)?;
317-
for cert in builder.identity.0.chain.iter().rev() {
318-
acceptor.add_extra_chain_cert(cert.to_owned())?;
331+
if let Some(ref chain) = builder.identity.0.chain {
332+
for cert in chain.iter().rev() {
333+
acceptor.add_extra_chain_cert(cert.to_owned())?;
334+
}
319335
}
320336
supported_protocols(builder.min_protocol, builder.max_protocol, &mut acceptor)?;
321337

src/imp/schannel.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
extern crate schannel;
22

3-
use self::schannel::cert_context::{CertContext, HashAlgorithm};
3+
use self::schannel::cert_context::{CertContext, HashAlgorithm, KeySpec};
44
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
55
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
6+
use self::schannel::crypt_prov::{AcquireOptions, ProviderType};
67
use self::schannel::tls_stream;
78
use std::error;
89
use std::fmt;
@@ -96,6 +97,41 @@ impl Identity {
9697

9798
Ok(Identity { cert: identity })
9899
}
100+
101+
pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> {
102+
let mut store = Memory::new()?.into_store();
103+
let mut cert_iter = crate::pem::PemBlock::new(pem).into_iter();
104+
let leaf = cert_iter.next().unwrap();
105+
let cert = CertContext::from_pem(std::str::from_utf8(leaf).unwrap()).unwrap();
106+
107+
let mut options = AcquireOptions::new();
108+
options.container("schannel");
109+
let type_ = ProviderType::rsa_full();
110+
111+
let mut container = match options.acquire(type_) {
112+
Ok(container) => container,
113+
Err(_) => options.new_keyset(true).acquire(type_).unwrap(),
114+
};
115+
let key = crate::pem::pem_to_der(key, Some(crate::pem::PEM_PRIVATE_KEY)).unwrap();
116+
container.import()
117+
.import_pkcs8(&key)
118+
.unwrap();
119+
120+
cert.set_key_prov_info()
121+
.container("schannel")
122+
.type_(type_)
123+
.keep_open(true)
124+
.key_spec(KeySpec::key_exchange())
125+
.set()
126+
.unwrap();
127+
let mut context = store.add_cert(&cert, CertAdd::Always)?;
128+
129+
for int_cert in cert_iter {
130+
let certificate = Certificate::from_pem(int_cert)?;
131+
context = store.add_cert(&certificate.0, schannel::cert_store::CertAdd::Always)?;
132+
}
133+
Ok(Identity{cert: context})
134+
}
99135
}
100136

101137
#[derive(Clone)]

src/imp/security_framework.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use self::security_framework::os::macos::certificate::{PropertyType, SecCertific
2323
#[cfg(not(target_os = "ios"))]
2424
use self::security_framework::os::macos::certificate_oids::CertificateOid;
2525
#[cfg(not(target_os = "ios"))]
26+
use self::security_framework::os::macos::identity::SecIdentityExt;
27+
#[cfg(not(target_os = "ios"))]
2628
use self::security_framework::os::macos::import_export::{ImportOptions, SecItems};
2729
#[cfg(not(target_os = "ios"))]
2830
use self::security_framework::os::macos::keychain::{self, KeychainSettings, SecKeychain};
@@ -85,6 +87,34 @@ pub struct Identity {
8587
}
8688

8789
impl Identity {
90+
pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> {
91+
let dir = TempDir::new().unwrap();
92+
let keychain = keychain::CreateOptions::new()
93+
.password("password")
94+
.create(dir.path().join("identity.keychain"))
95+
.unwrap();
96+
97+
let mut items = SecItems::default();
98+
99+
ImportOptions::new()
100+
.filename("key.pem")
101+
.items(&mut items)
102+
.keychain(&keychain)
103+
.import(&key)?;
104+
105+
ImportOptions::new()
106+
.filename("chain.pem")
107+
.items(&mut items)
108+
.keychain(&keychain)
109+
.import(&pem)?;
110+
111+
let ident = SecIdentity::with_certificate(&[keychain], &items.certificates[0])?;
112+
Ok(Identity {
113+
identity: ident,
114+
chain: items.certificates
115+
})
116+
}
117+
88118
pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
89119
let mut imports = Identity::import_options(buf, pass)?;
90120
let import = imports.pop().unwrap();

src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,14 @@
9797
#![warn(missing_docs)]
9898

9999
#[macro_use]
100-
#[cfg(any(target_os = "macos", target_os = "ios"))]
101100
extern crate lazy_static;
102101

103102
#[cfg(test)]
104103
extern crate hex;
105104

105+
extern crate rustc_serialize;
106+
mod pem;
107+
106108
use std::any::Any;
107109
use std::error;
108110
use std::fmt;
@@ -183,6 +185,13 @@ impl Identity {
183185
let identity = imp::Identity::from_pkcs12(der, password)?;
184186
Ok(Identity(identity))
185187
}
188+
189+
/// buf is the contents of a file containing a chain of PEM encoded certificates
190+
/// key is the contents of a file containing a PEM encoded private key
191+
pub fn from_pkcs8(buf: &[u8], key: &[u8]) -> Result<Identity> {
192+
let identity = imp::Identity::from_pkcs8(buf, key)?;
193+
Ok(Identity(identity))
194+
}
186195
}
187196

188197
/// An X509 certificate.

0 commit comments

Comments
 (0)