From 387e78257b578fef5933142085aefa1c76722b49 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 13 Nov 2016 16:09:52 +0000 Subject: [PATCH] Support serialization of encrypted private keys Switch to PEM_write_bio_PKCS8PrivateKey since the other function outputs nonstandard PEM when encrypting. --- openssl-sys/src/lib.rs | 4 ++++ openssl/src/dsa.rs | 10 ++++++++++ openssl/src/macros.rs | 20 ++++++++++++++++++++ openssl/src/pkey.rs | 12 +++++++++++- openssl/src/rsa.rs | 16 +++++++++++++--- 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 1b84b690..63714b7e 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1496,6 +1496,10 @@ extern { kstr: *mut c_uchar, klen: c_int, callback: Option, user_data: *mut c_void) -> c_int; + pub fn PEM_write_bio_PKCS8PrivateKey(bio: *mut BIO, pkey: *mut EVP_PKEY, cipher: *const EVP_CIPHER, + kstr: *mut c_char, klen: c_int, + callback: Option, + user_data: *mut c_void) -> c_int; pub fn PEM_write_bio_PUBKEY(bp: *mut BIO, x: *mut EVP_PKEY) -> c_int; pub fn PEM_write_bio_RSAPrivateKey(bp: *mut BIO, rsa: *mut RSA, cipher: *const EVP_CIPHER, kstr: *mut c_uchar, klen: c_int, diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs index 86476aac..39f98475 100644 --- a/openssl/src/dsa.rs +++ b/openssl/src/dsa.rs @@ -207,6 +207,8 @@ mod compat { #[cfg(test)] mod test { + use symm::Cipher; + use super::*; #[test] @@ -220,6 +222,14 @@ mod test { Dsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); } + #[test] + fn test_to_password() { + let key = Dsa::generate(2048).unwrap(); + let pem = key.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap(); + Dsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(Dsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + #[test] pub fn test_password_callback() { let mut password_queried = false; diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index 9f1d7746..6c37f7b0 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -113,5 +113,25 @@ macro_rules! private_key_to_pem { Ok(bio.get_buf().to_owned()) } } + + /// Serializes the private key to PEM, encrypting it with the specified symmetric cipher and + /// passphrase. + pub fn private_key_to_pem_passphrase(&self, + cipher: ::symm::Cipher, + passphrase: &[u8]) + -> Result, ::error::ErrorStack> { + unsafe { + let bio = try!(::bio::MemBio::new()); + assert!(passphrase.len() <= ::libc::c_int::max_value() as usize); + try!(cvt($f(bio.as_ptr(), + self.as_ptr(), + cipher.as_ptr(), + passphrase.as_ptr() as *const _ as *mut _, + passphrase.len() as ::libc::c_int, + None, + ptr::null_mut()))); + Ok(bio.get_buf().to_owned()) + } + } } } diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 27b36c4b..079a04cc 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -48,7 +48,7 @@ impl PKeyRef { } } - private_key_to_pem!(ffi::PEM_write_bio_PrivateKey); + private_key_to_pem!(ffi::PEM_write_bio_PKCS8PrivateKey); /// Encodes the public key in the PEM format. pub fn public_key_to_pem(&self) -> Result, ErrorStack> { @@ -185,6 +185,7 @@ impl PKey { #[cfg(test)] mod tests { + use symm::Cipher; use dh::Dh; use dsa::Dsa; use ec_key::EcKey; @@ -193,6 +194,15 @@ mod tests { use super::*; + #[test] + fn test_to_password() { + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + let pem = pkey.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap(); + PKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + #[test] fn test_private_key_from_pem() { let key = include_bytes!("../test/key.pem"); diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index bf127abe..5f94fd82 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -404,16 +404,18 @@ mod compat { #[cfg(test)] mod test { + use symm::Cipher; + use super::*; #[test] - pub fn test_password() { + fn test_from_password() { let key = include_bytes!("../test/rsa-encrypted.pem"); Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); } #[test] - pub fn test_password_callback() { + fn test_from_password_callback() { let mut password_queried = false; let key = include_bytes!("../test/rsa-encrypted.pem"); Rsa::private_key_from_pem_callback(key, |password| { @@ -427,7 +429,15 @@ mod test { } #[test] - pub fn test_public_encrypt_private_decrypt_with_padding() { + fn test_to_password() { + let key = Rsa::generate(2048).unwrap(); + let pem = key.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap(); + Rsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(Rsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + + #[test] + fn test_public_encrypt_private_decrypt_with_padding() { let key = include_bytes!("../test/rsa.pem.pub"); let public_key = Rsa::public_key_from_pem(key).unwrap();