1 //! Traits for parsing objects from PKCS#8 encoded documents 2 3 use crate::{Error, PrivateKeyInfo, Result}; 4 5 #[cfg(feature = "alloc")] 6 use der::SecretDocument; 7 8 #[cfg(feature = "encryption")] 9 use { 10 crate::EncryptedPrivateKeyInfo, 11 rand_core::{CryptoRng, RngCore}, 12 }; 13 14 #[cfg(feature = "pem")] 15 use {crate::LineEnding, alloc::string::String, der::zeroize::Zeroizing}; 16 17 #[cfg(feature = "pem")] 18 use der::pem::PemLabel; 19 20 #[cfg(feature = "std")] 21 use std::path::Path; 22 23 /// Parse a private key object from a PKCS#8 encoded document. 24 pub trait DecodePrivateKey: for<'a> TryFrom<PrivateKeyInfo<'a>, Error = Error> + Sized { 25 /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data 26 /// (binary format). from_pkcs8_der(bytes: &[u8]) -> Result<Self>27 fn from_pkcs8_der(bytes: &[u8]) -> Result<Self> { 28 Self::try_from(PrivateKeyInfo::try_from(bytes)?) 29 } 30 31 /// Deserialize encrypted PKCS#8 private key from ASN.1 DER-encoded data 32 /// (binary format) and attempt to decrypt it using the provided password. 33 #[cfg(feature = "encryption")] 34 #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self>35 fn from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self> { 36 let doc = EncryptedPrivateKeyInfo::try_from(bytes)?.decrypt(password)?; 37 Self::from_pkcs8_der(doc.as_bytes()) 38 } 39 40 /// Deserialize PKCS#8-encoded private key from PEM. 41 /// 42 /// Keys in this format begin with the following delimiter: 43 /// 44 /// ```text 45 /// -----BEGIN PRIVATE KEY----- 46 /// ``` 47 #[cfg(feature = "pem")] 48 #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] from_pkcs8_pem(s: &str) -> Result<Self>49 fn from_pkcs8_pem(s: &str) -> Result<Self> { 50 let (label, doc) = SecretDocument::from_pem(s)?; 51 PrivateKeyInfo::validate_pem_label(label)?; 52 Self::from_pkcs8_der(doc.as_bytes()) 53 } 54 55 /// Deserialize encrypted PKCS#8-encoded private key from PEM and attempt 56 /// to decrypt it using the provided password. 57 /// 58 /// Keys in this format begin with the following delimiter: 59 /// 60 /// ```text 61 /// -----BEGIN ENCRYPTED PRIVATE KEY----- 62 /// ``` 63 #[cfg(all(feature = "encryption", feature = "pem"))] 64 #[cfg_attr(docsrs, doc(cfg(all(feature = "encryption", feature = "pem"))))] from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self>65 fn from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self> { 66 let (label, doc) = SecretDocument::from_pem(s)?; 67 EncryptedPrivateKeyInfo::validate_pem_label(label)?; 68 Self::from_pkcs8_encrypted_der(doc.as_bytes(), password) 69 } 70 71 /// Load PKCS#8 private key from an ASN.1 DER-encoded file on the local 72 /// filesystem (binary format). 73 #[cfg(feature = "std")] 74 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self>75 fn read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self> { 76 Self::from_pkcs8_der(SecretDocument::read_der_file(path)?.as_bytes()) 77 } 78 79 /// Load PKCS#8 private key from a PEM-encoded file on the local filesystem. 80 #[cfg(all(feature = "pem", feature = "std"))] 81 #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] 82 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self>83 fn read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self> { 84 let (label, doc) = SecretDocument::read_pem_file(path)?; 85 PrivateKeyInfo::validate_pem_label(&label)?; 86 Self::from_pkcs8_der(doc.as_bytes()) 87 } 88 } 89 90 /// Serialize a private key object to a PKCS#8 encoded document. 91 #[cfg(feature = "alloc")] 92 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] 93 pub trait EncodePrivateKey { 94 /// Serialize a [`SecretDocument`] containing a PKCS#8-encoded private key. to_pkcs8_der(&self) -> Result<SecretDocument>95 fn to_pkcs8_der(&self) -> Result<SecretDocument>; 96 97 /// Create an [`SecretDocument`] containing the ciphertext of 98 /// a PKCS#8 encoded private key encrypted under the given `password`. 99 #[cfg(feature = "encryption")] 100 #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] to_pkcs8_encrypted_der( &self, rng: impl CryptoRng + RngCore, password: impl AsRef<[u8]>, ) -> Result<SecretDocument>101 fn to_pkcs8_encrypted_der( 102 &self, 103 rng: impl CryptoRng + RngCore, 104 password: impl AsRef<[u8]>, 105 ) -> Result<SecretDocument> { 106 EncryptedPrivateKeyInfo::encrypt(rng, password, self.to_pkcs8_der()?.as_bytes()) 107 } 108 109 /// Serialize this private key as PEM-encoded PKCS#8 with the given [`LineEnding`]. 110 #[cfg(feature = "pem")] 111 #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] to_pkcs8_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>>112 fn to_pkcs8_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> { 113 let doc = self.to_pkcs8_der()?; 114 Ok(doc.to_pem(PrivateKeyInfo::PEM_LABEL, line_ending)?) 115 } 116 117 /// Serialize this private key as an encrypted PEM-encoded PKCS#8 private 118 /// key using the `provided` to derive an encryption key. 119 #[cfg(all(feature = "encryption", feature = "pem"))] 120 #[cfg_attr(docsrs, doc(cfg(all(feature = "encryption", feature = "pem"))))] to_pkcs8_encrypted_pem( &self, rng: impl CryptoRng + RngCore, password: impl AsRef<[u8]>, line_ending: LineEnding, ) -> Result<Zeroizing<String>>121 fn to_pkcs8_encrypted_pem( 122 &self, 123 rng: impl CryptoRng + RngCore, 124 password: impl AsRef<[u8]>, 125 line_ending: LineEnding, 126 ) -> Result<Zeroizing<String>> { 127 let doc = self.to_pkcs8_encrypted_der(rng, password)?; 128 Ok(doc.to_pem(EncryptedPrivateKeyInfo::PEM_LABEL, line_ending)?) 129 } 130 131 /// Write ASN.1 DER-encoded PKCS#8 private key to the given path 132 #[cfg(feature = "std")] 133 #[cfg_attr(docsrs, doc(cfg(feature = "std")))] write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()>134 fn write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()> { 135 Ok(self.to_pkcs8_der()?.write_der_file(path)?) 136 } 137 138 /// Write ASN.1 DER-encoded PKCS#8 private key to the given path 139 #[cfg(all(feature = "pem", feature = "std"))] 140 #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] write_pkcs8_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()>141 fn write_pkcs8_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> { 142 let doc = self.to_pkcs8_der()?; 143 Ok(doc.write_pem_file(path, PrivateKeyInfo::PEM_LABEL, line_ending)?) 144 } 145 } 146