pkcs8/
traits.rs

1//! Traits for parsing objects from PKCS#8 encoded documents
2
3use crate::{Error, PrivateKeyInfo, Result};
4
5#[cfg(feature = "alloc")]
6use der::SecretDocument;
7
8#[cfg(feature = "encryption")]
9use {
10    crate::EncryptedPrivateKeyInfo,
11    rand_core::{CryptoRng, RngCore},
12};
13
14#[cfg(feature = "pem")]
15use {crate::LineEnding, alloc::string::String, der::zeroize::Zeroizing};
16
17#[cfg(feature = "pem")]
18use der::pem::PemLabel;
19
20#[cfg(feature = "std")]
21use std::path::Path;
22
23/// Parse a private key object from a PKCS#8 encoded document.
24pub 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).
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")))]
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")))]
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"))))]
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")))]
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")))]
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")))]
93pub trait EncodePrivateKey {
94    /// Serialize a [`SecretDocument`] containing a PKCS#8-encoded private key.
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")))]
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")))]
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"))))]
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")))]
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"))))]
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}