From 19f3b8a11acc576135e94741975cd1d27e64794f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 14 Feb 2017 19:37:25 -0800 Subject: [PATCH] Support PKCS#8 private key deserialization Closes #581 --- openssl-sys/src/lib.rs | 2 +- openssl/src/pkey.rs | 63 ++++++++++++++++++++++++++++++++++++++++- openssl/test/pkcs8.der | Bin 0 -> 1298 bytes 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 openssl/test/pkcs8.der diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 125acd945..a599c3692 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 5608dd51f..6dbe8ec73 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 GIT binary patch literal 1298 zcmXqLV&yY%VB^$k^Jx3d%gD-WU~C|b;PM#ov2ZN>%T`)^Xp$-u69mU*O(chrh4mpUd_%$F3WCP&;7A^Gf##15r4;W zd!hOnUp_sy@wg+mty{{zZ?+~!Qr^MG3cr1(O6%O)B3iy~6@N=!frVsYoJ#ttY1N-Q zySBeO-W+|<=q}r@)&`EgNer7l{?C5F)nh0y^S?3YgDD9qla7S(7tJu4#IY_TgJZgj zYMz)}9gkA&z8h=eFE;Ks&)R15*|x!zYm!(lFULDI=}YV`obLX1%MIUTDn<6EaPa&& z+W9&(Wy`9AoK`kGcjGqyRTTGm5HoEn`=1%<#}<9)niBKc{7>fU1kX^M(lT*Jjl`T^dCSlK%ruDQe(~1a@O$g2tLGzvcV(@*d3u(~ z$rYF!fi2w@(++7#S>AfvRf(|6C0i7!Gb z=4r$me4p)b$+fg@^R}PLQG7RbW=@_iY@{mTbLoZ6?)>Dfoi%^=@w_p0$+5`h&wrME zX3@^lX<>regrDlVXYPw_Hdy;83yE_~LuM)cqO zF8z-?RzAO)dSoHLm2r^&UY+@e`8VkPSRSRmzXKd2`CA&z8DttS$xHv3M(}ueY3-!?vyR z_`XBp?3${)g0sBtIs83XGii?AV>?Se*Y>y19`mL@-s0kZ>SR;auN@hV`3WZ9S6{ez zq{scDH0Spd@ufK*K5JGt-w8?omGL+Bn#YP0lP&%)FN>XXI*gaq!remIE3A3Pi;llX zw(oQFnevxEY>j5e0*jgjFJ|cU>7Q@-a$aIW{Mrkz8KX8Wjeg=Rk?dK)^z729hTYsN zxvy+Fw(t$9`>=!{8RgfJu`?b|iuZbTa_KWoU02t8Q|$~gv^NLXZ`;WlJ8}P? z-%T?wzkU^RYWECvF8?UEi9cgHYYIa6iI&`b_%0$h9JJ?T)lQ HF})N38x?Gj literal 0 HcmV?d00001 -- GitLab