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