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: 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 29 /// Deserialize encrypted PKCS#8 private key from ASN.1 DER-encoded data 30 /// (binary format) and attempt to decrypt it using the provided password. 31 #[cfg(feature = "encryption")] from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self>32 fn from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self> { 33 let doc = EncryptedPrivateKeyInfo::try_from(bytes)?.decrypt(password)?; 34 Self::from_pkcs8_der(doc.as_bytes()) 35 } 36 37 /// Deserialize PKCS#8-encoded private key from PEM. 38 /// 39 /// Keys in this format begin with the following delimiter: 40 /// 41 /// ```text 42 /// -----BEGIN PRIVATE KEY----- 43 /// ``` 44 #[cfg(feature = "pem")] from_pkcs8_pem(s: &str) -> Result<Self>45 fn from_pkcs8_pem(s: &str) -> Result<Self> { 46 let (label, doc) = SecretDocument::from_pem(s)?; 47 PrivateKeyInfo::validate_pem_label(label)?; 48 Self::from_pkcs8_der(doc.as_bytes()) 49 } 50 51 /// Deserialize encrypted PKCS#8-encoded private key from PEM and attempt 52 /// to decrypt it using the provided password. 53 /// 54 /// Keys in this format begin with the following delimiter: 55 /// 56 /// ```text 57 /// -----BEGIN ENCRYPTED PRIVATE KEY----- 58 /// ``` 59 #[cfg(all(feature = "encryption", feature = "pem"))] from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self>60 fn from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self> { 61 let (label, doc) = SecretDocument::from_pem(s)?; 62 EncryptedPrivateKeyInfo::validate_pem_label(label)?; 63 Self::from_pkcs8_encrypted_der(doc.as_bytes(), password) 64 } 65 66 /// Load PKCS#8 private key from an ASN.1 DER-encoded file on the local 67 /// filesystem (binary format). 68 #[cfg(feature = "std")] read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self>69 fn read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self> { 70 Self::from_pkcs8_der(SecretDocument::read_der_file(path)?.as_bytes()) 71 } 72 73 /// Load PKCS#8 private key from a PEM-encoded file on the local filesystem. 74 #[cfg(all(feature = "pem", feature = "std"))] read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self>75 fn read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self> { 76 let (label, doc) = SecretDocument::read_pem_file(path)?; 77 PrivateKeyInfo::validate_pem_label(&label)?; 78 Self::from_pkcs8_der(doc.as_bytes()) 79 } 80 } 81 82 impl<T> DecodePrivateKey for T 83 where 84 T: for<'a> TryFrom<PrivateKeyInfo<'a>, Error = Error>, 85 { from_pkcs8_der(bytes: &[u8]) -> Result<Self>86 fn from_pkcs8_der(bytes: &[u8]) -> Result<Self> { 87 Self::try_from(PrivateKeyInfo::try_from(bytes)?) 88 } 89 } 90 91 /// Serialize a private key object to a PKCS#8 encoded document. 92 #[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")] to_pkcs8_encrypted_der( &self, rng: impl CryptoRng + RngCore, password: impl AsRef<[u8]>, ) -> Result<SecretDocument>100 fn to_pkcs8_encrypted_der( 101 &self, 102 rng: impl CryptoRng + RngCore, 103 password: impl AsRef<[u8]>, 104 ) -> Result<SecretDocument> { 105 EncryptedPrivateKeyInfo::encrypt(rng, password, self.to_pkcs8_der()?.as_bytes()) 106 } 107 108 /// Serialize this private key as PEM-encoded PKCS#8 with the given [`LineEnding`]. 109 #[cfg(feature = "pem")] to_pkcs8_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>>110 fn to_pkcs8_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> { 111 let doc = self.to_pkcs8_der()?; 112 Ok(doc.to_pem(PrivateKeyInfo::PEM_LABEL, line_ending)?) 113 } 114 115 /// Serialize this private key as an encrypted PEM-encoded PKCS#8 private 116 /// key using the `provided` to derive an encryption key. 117 #[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>>118 fn to_pkcs8_encrypted_pem( 119 &self, 120 rng: impl CryptoRng + RngCore, 121 password: impl AsRef<[u8]>, 122 line_ending: LineEnding, 123 ) -> Result<Zeroizing<String>> { 124 let doc = self.to_pkcs8_encrypted_der(rng, password)?; 125 Ok(doc.to_pem(EncryptedPrivateKeyInfo::PEM_LABEL, line_ending)?) 126 } 127 128 /// Write ASN.1 DER-encoded PKCS#8 private key to the given path 129 #[cfg(feature = "std")] write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()>130 fn write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()> { 131 Ok(self.to_pkcs8_der()?.write_der_file(path)?) 132 } 133 134 /// Write ASN.1 DER-encoded PKCS#8 private key to the given path 135 #[cfg(all(feature = "pem", feature = "std"))] write_pkcs8_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()>136 fn write_pkcs8_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> { 137 let doc = self.to_pkcs8_der()?; 138 Ok(doc.write_pem_file(path, PrivateKeyInfo::PEM_LABEL, line_ending)?) 139 } 140 } 141