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: 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
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")]
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")]
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"))]
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")]
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"))]
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
82impl<T> DecodePrivateKey for T
83where
84    T: for<'a> TryFrom<PrivateKeyInfo<'a>, Error = Error>,
85{
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")]
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    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")]
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"))]
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")]
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"))]
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}