diff --git a/openssl-sys/src/pem.rs b/openssl-sys/src/pem.rs index a3d9cc6f3c03f7077b3d888e4c414dffd71a9d6e..978ec42da80add279c052f8ce7f4758107847044 100644 --- a/openssl-sys/src/pem.rs +++ b/openssl-sys/src/pem.rs @@ -66,6 +66,15 @@ const_ptr_api! { ) -> c_int; pub fn PEM_write_bio_PKCS7(bp: *mut BIO, x: #[const_ptr_if(ossl300)] PKCS7) -> c_int; pub fn PEM_write_bio_EC_PUBKEY(bp: *mut BIO, ec: #[const_ptr_if(ossl300)] EC_KEY) -> c_int; + pub fn i2d_PKCS8PrivateKey_bio( + bp: *mut BIO, + x: #[const_ptr_if(ossl300)] EVP_PKEY, + enc: *const EVP_CIPHER, + kstr: #[const_ptr_if(ossl300)] c_char, + klen: c_int, + cb: pem_password_cb, + u: *mut c_void, + ) -> c_int; } } diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 2d443823de810f6baed7290c431cabae5fde4f43..581c9d19f3b8b25c6899389bbc7e1be8233fd6a8 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -49,13 +49,12 @@ use std::fmt; use std::mem; use std::ptr; -use crate::bio::MemBioSlice; +use crate::bio::{MemBio, MemBioSlice}; use crate::dh::Dh; use crate::dsa::Dsa; use crate::ec::EcKey; use crate::error::ErrorStack; use crate::rsa::Rsa; -#[cfg(ossl110)] use crate::symm::Cipher; use crate::util::{invoke_passwd_cb, CallbackState}; use crate::{cvt, cvt_p}; @@ -684,6 +683,35 @@ impl PKey { } } + /// Serializes a private key into a DER-formatted PKCS#8, using the supplied password to + /// encrypt the key. + /// + /// # Panics + /// + /// Panics if `passphrase` contains an embedded null. + pub fn private_key_to_pkcs8_passphrase( + &self, + cipher: Cipher, + passphrase: &[u8], + ) -> Result, ErrorStack> { + unsafe { + let bio = MemBio::new()?; + let len = passphrase.len(); + let passphrase = CString::new(passphrase).unwrap(); + cvt(ffi::i2d_PKCS8PrivateKey_bio( + bio.as_ptr(), + self.as_ptr(), + cipher.as_ptr(), + passphrase.as_ptr() as *const _ as *mut _, + len as ::libc::c_int, + None, + ptr::null_mut(), + ))?; + + Ok(bio.get_buf().to_owned()) + } + } + /// Creates a private key from its raw byte representation /// /// Algorithm types that support raw private keys are HMAC, X25519, ED25519, X448 or ED448 @@ -877,6 +905,17 @@ mod tests { fn test_encrypted_pkcs8_passphrase() { let key = include_bytes!("../test/pkcs8.der"); PKey::private_key_from_pkcs8_passphrase(key, b"mypass").unwrap(); + + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + let der = pkey + .private_key_to_pkcs8_passphrase(Cipher::aes_128_cbc(), b"mypass") + .unwrap(); + let pkey2 = PKey::private_key_from_pkcs8_passphrase(&der, b"mypass").unwrap(); + assert_eq!( + pkey.private_key_to_der().unwrap(), + pkey2.private_key_to_der().unwrap() + ); } #[test] diff --git a/systest/build.rs b/systest/build.rs index 5cf20bc24e4768eb69e89c606a3b961b571b7f0d..5c905b428cad26ac959f2aeed644cef352f732ce 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -109,6 +109,7 @@ fn main() { s.starts_with("PEM_read_bio_") || (s.starts_with("PEM_write_bio_") && s.ends_with("PrivateKey")) || s == "d2i_PKCS8PrivateKey_bio" || + s == "i2d_PKCS8PrivateKey_bio" || s == "SSL_get_ex_new_index" || s == "SSL_CTX_get_ex_new_index" || s == "CRYPTO_get_ex_new_index"