pkcs8/traits.rs
1//! Traits for parsing objects from PKCS#8 encoded documents
2
3use crate::{Error, PrivateKeyInfoRef, Result};
4
5#[cfg(feature = "alloc")]
6use der::SecretDocument;
7
8#[cfg(feature = "encryption")]
9use crate::EncryptedPrivateKeyInfoRef;
10
11#[cfg(feature = "pem")]
12use {
13 crate::LineEnding,
14 alloc::string::String,
15 der::{
16 pem::{self, PemLabel},
17 zeroize::Zeroizing,
18 },
19};
20
21#[cfg(feature = "std")]
22use std::path::Path;
23
24/// Parse a private key object from a PKCS#8 encoded document.
25pub trait DecodePrivateKey: Sized {
26 /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format).
27 ///
28 /// # Errors
29 /// Returns format-specific errors in the event the document failed to parse.
30 fn from_pkcs8_der(bytes: &[u8]) -> Result<Self>;
31
32 /// Deserialize encrypted PKCS#8 private key from ASN.1 DER-encoded data (binary format) and
33 /// attempt to decrypt it using the provided password.
34 ///
35 /// # Errors
36 /// - Returns errors if the DER failed to decode
37 /// - Returns errors if the ciphertext failed to decrypt under the given password
38 #[cfg(feature = "encryption")]
39 fn from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self> {
40 let doc = EncryptedPrivateKeyInfoRef::try_from(bytes)?.decrypt(password)?;
41 Self::from_pkcs8_der(doc.as_bytes())
42 }
43
44 /// Deserialize PKCS#8-encoded private key from PEM.
45 ///
46 /// Keys in this format begin with the following delimiter:
47 ///
48 /// ```text
49 /// -----BEGIN PRIVATE KEY-----
50 /// ```
51 ///
52 /// # Errors
53 /// - Returns [`Error::Asn1`] in the event of a decoding error (PEM or DER).
54 /// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_der`].
55 #[cfg(feature = "pem")]
56 fn from_pkcs8_pem(s: &str) -> Result<Self> {
57 // Validate PEM label
58 let label = pem::decode_label(s.as_bytes())?;
59 PrivateKeyInfoRef::validate_pem_label(label)?;
60
61 let doc = SecretDocument::from_pem(s)?.1;
62 Self::from_pkcs8_der(doc.as_bytes())
63 }
64
65 /// Deserialize encrypted PKCS#8-encoded private key from PEM and attempt to decrypt it using
66 /// the provided password.
67 ///
68 /// Keys in this format begin with the following delimiter:
69 ///
70 /// ```text
71 /// -----BEGIN ENCRYPTED PRIVATE KEY-----
72 /// ```
73 ///
74 /// # Errors
75 /// - Returns [`Error::Asn1`] in the event of a decoding error (PEM or DER).
76 /// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_encrypted_der`].
77 #[cfg(all(feature = "encryption", feature = "pem"))]
78 fn from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self> {
79 let (label, doc) = SecretDocument::from_pem(s)?;
80 EncryptedPrivateKeyInfoRef::validate_pem_label(label)?;
81 Self::from_pkcs8_encrypted_der(doc.as_bytes(), password)
82 }
83
84 /// Load PKCS#8 private key from an ASN.1 DER-encoded file (binary format) on the local
85 /// filesystem.
86 ///
87 /// # Errors
88 /// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_der`].
89 /// - Returns errors in event the file cannot be read from the filesystem.
90 #[cfg(feature = "std")]
91 fn read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self> {
92 Self::from_pkcs8_der(SecretDocument::read_der_file(path)?.as_bytes())
93 }
94
95 /// Load PKCS#8 private key from a PEM-encoded file on the local filesystem.
96 ///
97 /// # Errors
98 /// - Returns the same errors as [`SecretDocument::read_pem_file`].
99 /// - Returns the same errors as [`DecodePrivateKey::from_pkcs8_der`].
100 /// - Returns errors in event the file cannot be read from the filesystem.
101 #[cfg(all(feature = "pem", feature = "std"))]
102 fn read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self> {
103 let (label, doc) = SecretDocument::read_pem_file(path)?;
104 PrivateKeyInfoRef::validate_pem_label(&label)?;
105 Self::from_pkcs8_der(doc.as_bytes())
106 }
107}
108
109impl<T> DecodePrivateKey for T
110where
111 T: for<'a> TryFrom<PrivateKeyInfoRef<'a>, Error = Error>,
112{
113 fn from_pkcs8_der(bytes: &[u8]) -> Result<Self> {
114 Self::try_from(PrivateKeyInfoRef::try_from(bytes)?)
115 }
116}
117
118/// Serialize a private key object to a PKCS#8 encoded document.
119#[cfg(feature = "alloc")]
120pub trait EncodePrivateKey {
121 /// Serialize a [`SecretDocument`] containing a PKCS#8-encoded private key.
122 ///
123 /// # Errors
124 /// Returns format-specific errors in the event the document failed to serialize.
125 fn to_pkcs8_der(&self) -> Result<SecretDocument>;
126
127 /// Create an [`SecretDocument`] containing the ciphertext of a PKCS#8 encoded private key
128 /// encrypted under the given `password`.
129 ///
130 /// # Errors
131 /// - Returns format-specific errors in the event the document failed to serialize.
132 /// - Returns algorithm-specific errors in the event the document couldn't be encrypted.
133 #[cfg(feature = "getrandom")]
134 fn to_pkcs8_encrypted_der(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument> {
135 EncryptedPrivateKeyInfoRef::encrypt(password, self.to_pkcs8_der()?.as_bytes())
136 }
137
138 /// Serialize this private key as PEM-encoded PKCS#8 with the given [`LineEnding`].
139 ///
140 /// # Errors
141 /// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_der`].
142 /// - Returns the same errors as [`SecretDocument::to_pem`].
143 #[cfg(feature = "pem")]
144 fn to_pkcs8_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
145 let doc = self.to_pkcs8_der()?;
146 Ok(doc.to_pem(PrivateKeyInfoRef::PEM_LABEL, line_ending)?)
147 }
148
149 /// Serialize this private key as an encrypted PEM-encoded PKCS#8 private key using the
150 /// `provided` to derive an encryption key.
151 ///
152 /// # Errors
153 /// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_encrypted_der`].
154 /// - Returns the same errors as [`SecretDocument::to_pem`].
155 #[cfg(all(feature = "getrandom", feature = "pem"))]
156 fn to_pkcs8_encrypted_pem(
157 &self,
158 password: impl AsRef<[u8]>,
159 line_ending: LineEnding,
160 ) -> Result<Zeroizing<String>> {
161 let doc = self.to_pkcs8_encrypted_der(password)?;
162 Ok(doc.to_pem(EncryptedPrivateKeyInfoRef::PEM_LABEL, line_ending)?)
163 }
164
165 /// Write ASN.1 DER-encoded PKCS#8 private key to the given path.
166 ///
167 /// # Errors
168 /// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_der`].
169 /// - Returns errors in the event the file could not be written to the filesystem.
170 #[cfg(feature = "std")]
171 fn write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
172 Ok(self.to_pkcs8_der()?.write_der_file(path)?)
173 }
174
175 /// Write ASN.1 PEM-encoded PKCS#8 private key to the given path.
176 ///
177 /// # Errors
178 /// - Returns the same errors as [`EncodePrivateKey::to_pkcs8_der`].
179 /// - Returns errors in the event the file could not be written to the filesystem.
180 #[cfg(all(feature = "pem", feature = "std"))]
181 fn write_pkcs8_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> {
182 let doc = self.to_pkcs8_der()?;
183 Ok(doc.write_pem_file(path, PrivateKeyInfoRef::PEM_LABEL, line_ending)?)
184 }
185}