diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 125acd945c54d61a94bb3c9dcbc0af5b159a9dc3..a599c36927048028618df227b253e838dc741c4c 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1639,7 +1639,7 @@ extern { e: *mut ENGINE, key: *const c_uchar, keylen: c_int) -> *mut EVP_PKEY; - + pub fn d2i_PKCS8PrivateKey_bio(bp: *mut BIO, x: *mut *mut EVP_PKEY, cb: Option, u: *mut c_void) -> *mut EVP_PKEY; pub fn EVP_PKEY_CTX_ctrl(ctx: *mut EVP_PKEY_CTX, keytype: c_int, optype: c_int, cmd: c_int, p1: c_int, p2: *mut c_void) -> c_int; diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 5608dd51fccb0f40d9281ca7427be123fc1aeada..6dbe8ec731d2a0d3c11d4fd92e45a4272f12f609 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -1,6 +1,7 @@ use libc::{c_void, c_char, c_int}; use std::ptr; use std::mem; +use std::ffi::CString; use ffi; use foreign_types::{Opaque, ForeignType, ForeignTypeRef}; @@ -11,7 +12,7 @@ use dsa::Dsa; use ec::EcKey; use rsa::{Rsa, Padding}; use error::ErrorStack; -use util::{CallbackState, invoke_passwd_cb_old}; +use util::{CallbackState, invoke_passwd_cb, invoke_passwd_cb_old}; foreign_type! { type CType = ffi::EVP_PKEY; @@ -140,6 +141,47 @@ impl PKey { private_key_from_pem!(PKey, ffi::PEM_read_bio_PrivateKey); public_key_from_pem!(PKey, ffi::PEM_read_bio_PUBKEY); + /// Deserializes a DER-formatted PKCS#8 private key, using a callback to retrieve the password + /// if the key is encrpyted. + /// + /// The callback should copy the password into the provided buffer and return the number of + /// bytes written. + pub fn private_key_from_pkcs8_callback(der: &[u8], callback: F) -> Result + where F: FnOnce(&mut [u8]) -> Result + { + unsafe { + ffi::init(); + let mut cb = CallbackState::new(callback); + let bio = try!(MemBioSlice::new(der)); + cvt_p(ffi::d2i_PKCS8PrivateKey_bio(bio.as_ptr(), + ptr::null_mut(), + Some(invoke_passwd_cb::), + &mut cb as *mut _ as *mut _)) + .map(PKey) + } + } + + /// Deserializes a DER-formatted PKCS#8 private key, using the supplied password if the key is + /// encrypted. + /// + /// # Panics + /// + /// Panics if `passphrase` contains an embedded null. + pub fn private_key_from_pkcs8_passphrase(der: &[u8], + passphrase: &[u8]) + -> Result { + unsafe { + ffi::init(); + let bio = try!(MemBioSlice::new(der)); + let passphrase = CString::new(passphrase).unwrap(); + cvt_p(ffi::d2i_PKCS8PrivateKey_bio(bio.as_ptr(), + ptr::null_mut(), + None, + passphrase.as_ptr() as *const _ as *mut _)) + .map(PKey) + } + } + #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")] pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result where F: FnOnce(&mut [c_char]) -> usize @@ -200,6 +242,25 @@ mod tests { assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); } + #[test] + fn test_encrypted_pkcs8_passphrase() { + let key = include_bytes!("../test/pkcs8.der"); + PKey::private_key_from_pkcs8_passphrase(key, b"mypass").unwrap(); + } + + #[test] + fn test_encrypted_pkcs8_callback() { + let mut password_queried = false; + let key = include_bytes!("../test/pkcs8.der"); + PKey::private_key_from_pkcs8_callback(key, |password| { + password_queried = true; + password[..6].copy_from_slice(b"mypass"); + Ok(6) + }) + .unwrap(); + assert!(password_queried); + } + #[test] fn test_private_key_from_pem() { let key = include_bytes!("../test/key.pem"); diff --git a/openssl/test/pkcs8.der b/openssl/test/pkcs8.der new file mode 100644 index 0000000000000000000000000000000000000000..47a2db8ad99c55424b3eba09cf195d9a826d9f17 Binary files /dev/null and b/openssl/test/pkcs8.der differ