Skip to main content

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}