1 //! Traits for parsing objects from PKCS#8 encoded documents
2 
3 use crate::{PrivateKeyInfo, Result, SubjectPublicKeyInfo};
4 use core::convert::TryFrom;
5 
6 #[cfg(feature = "alloc")]
7 use crate::{PrivateKeyDocument, PublicKeyDocument};
8 
9 #[cfg(feature = "encryption")]
10 use {
11     crate::{EncryptedPrivateKeyDocument, EncryptedPrivateKeyInfo},
12     rand_core::{CryptoRng, RngCore},
13 };
14 
15 #[cfg(feature = "pem")]
16 use {crate::LineEnding, alloc::string::String};
17 
18 #[cfg(feature = "pkcs1")]
19 use crate::{Error, ObjectIdentifier};
20 
21 #[cfg(feature = "std")]
22 use std::path::Path;
23 
24 #[cfg(any(feature = "pem", feature = "std"))]
25 use zeroize::Zeroizing;
26 
27 #[cfg(all(feature = "alloc", feature = "pkcs1"))]
28 use crate::AlgorithmIdentifier;
29 
30 /// PKCS#1 RSA Algorithm [`ObjectIdentifier`].
31 ///
32 /// <http://oid-info.com/get/1.2.840.113549.1.1.1>
33 #[cfg(feature = "pkcs1")]
34 const PKCS1_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.1");
35 
36 /// Parse a private key object from a PKCS#8 encoded document.
37 pub trait FromPrivateKey: Sized {
38     /// Parse the [`PrivateKeyInfo`] from a PKCS#8-encoded document.
from_pkcs8_private_key_info(private_key_info: PrivateKeyInfo<'_>) -> Result<Self>39     fn from_pkcs8_private_key_info(private_key_info: PrivateKeyInfo<'_>) -> Result<Self>;
40 
41     /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data
42     /// (binary format).
from_pkcs8_der(bytes: &[u8]) -> Result<Self>43     fn from_pkcs8_der(bytes: &[u8]) -> Result<Self> {
44         Self::from_pkcs8_private_key_info(PrivateKeyInfo::try_from(bytes)?)
45     }
46 
47     /// Deserialize encrypted PKCS#8 private key from ASN.1 DER-encoded data
48     /// (binary format) and attempt to decrypt it using the provided password.
49     #[cfg(feature = "encryption")]
50     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self>51     fn from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result<Self> {
52         EncryptedPrivateKeyInfo::try_from(bytes)?
53             .decrypt(password)
54             .and_then(|doc| Self::from_pkcs8_doc(&doc))
55     }
56 
57     /// Deserialize PKCS#8 private key from a [`PrivateKeyDocument`].
58     #[cfg(feature = "alloc")]
59     #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
from_pkcs8_doc(doc: &PrivateKeyDocument) -> Result<Self>60     fn from_pkcs8_doc(doc: &PrivateKeyDocument) -> Result<Self> {
61         Self::from_pkcs8_private_key_info(doc.private_key_info())
62     }
63 
64     /// Deserialize PKCS#8-encoded private key from PEM.
65     ///
66     /// Keys in this format begin with the following delimiter:
67     ///
68     /// ```text
69     /// -----BEGIN PRIVATE KEY-----
70     /// ```
71     #[cfg(feature = "pem")]
72     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
from_pkcs8_pem(s: &str) -> Result<Self>73     fn from_pkcs8_pem(s: &str) -> Result<Self> {
74         PrivateKeyDocument::from_pem(s).and_then(|doc| Self::from_pkcs8_doc(&doc))
75     }
76 
77     /// Deserialize encrypted PKCS#8-encoded private key from PEM and attempt
78     /// to decrypt it using the provided password.
79     ///
80     /// Keys in this format begin with the following delimiter:
81     ///
82     /// ```text
83     /// -----BEGIN ENCRYPTED PRIVATE KEY-----
84     /// ```
85     #[cfg(all(feature = "encryption", feature = "pem"))]
86     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
87     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self>88     fn from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result<Self> {
89         EncryptedPrivateKeyDocument::from_pem(s)?
90             .decrypt(password)
91             .and_then(|doc| Self::from_pkcs8_doc(&doc))
92     }
93 
94     /// Load PKCS#8 private key from an ASN.1 DER-encoded file on the local
95     /// filesystem (binary format).
96     #[cfg(feature = "std")]
97     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self>98     fn read_pkcs8_der_file(path: impl AsRef<Path>) -> Result<Self> {
99         PrivateKeyDocument::read_der_file(path).and_then(|doc| Self::from_pkcs8_doc(&doc))
100     }
101 
102     /// Load PKCS#8 private key from a PEM-encoded file on the local filesystem.
103     #[cfg(all(feature = "pem", feature = "std"))]
104     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
105     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self>106     fn read_pkcs8_pem_file(path: impl AsRef<Path>) -> Result<Self> {
107         PrivateKeyDocument::read_pem_file(path).and_then(|doc| Self::from_pkcs8_doc(&doc))
108     }
109 }
110 
111 /// Parse a public key object from an encoded SPKI document.
112 pub trait FromPublicKey: Sized {
113     /// Parse [`SubjectPublicKeyInfo`] into a public key object.
from_spki(spki: SubjectPublicKeyInfo<'_>) -> Result<Self>114     fn from_spki(spki: SubjectPublicKeyInfo<'_>) -> Result<Self>;
115 
116     /// Deserialize object from ASN.1 DER-encoded [`SubjectPublicKeyInfo`]
117     /// (binary format).
from_public_key_der(bytes: &[u8]) -> Result<Self>118     fn from_public_key_der(bytes: &[u8]) -> Result<Self> {
119         Self::from_spki(SubjectPublicKeyInfo::try_from(bytes)?)
120     }
121 
122     /// Deserialize PKCS#8 private key from a [`PrivateKeyDocument`].
123     #[cfg(feature = "alloc")]
124     #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
from_public_key_doc(doc: &PublicKeyDocument) -> Result<Self>125     fn from_public_key_doc(doc: &PublicKeyDocument) -> Result<Self> {
126         Self::from_spki(doc.spki())
127     }
128 
129     /// Deserialize PEM-encoded [`SubjectPublicKeyInfo`].
130     ///
131     /// Keys in this format begin with the following delimiter:
132     ///
133     /// ```text
134     /// -----BEGIN PUBLIC KEY-----
135     /// ```
136     #[cfg(feature = "pem")]
137     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
from_public_key_pem(s: &str) -> Result<Self>138     fn from_public_key_pem(s: &str) -> Result<Self> {
139         PublicKeyDocument::from_pem(s).and_then(|doc| Self::from_public_key_doc(&doc))
140     }
141 
142     /// Load public key object from an ASN.1 DER-encoded file on the local
143     /// filesystem (binary format).
144     #[cfg(feature = "std")]
145     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read_public_key_der_file(path: impl AsRef<Path>) -> Result<Self>146     fn read_public_key_der_file(path: impl AsRef<Path>) -> Result<Self> {
147         PublicKeyDocument::read_der_file(path).and_then(|doc| Self::from_public_key_doc(&doc))
148     }
149 
150     /// Load public key object from a PEM-encoded file on the local filesystem.
151     #[cfg(all(feature = "pem", feature = "std"))]
152     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
153     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read_public_key_pem_file(path: impl AsRef<Path>) -> Result<Self>154     fn read_public_key_pem_file(path: impl AsRef<Path>) -> Result<Self> {
155         PublicKeyDocument::read_pem_file(path).and_then(|doc| Self::from_public_key_doc(&doc))
156     }
157 }
158 
159 /// Serialize a private key object to a PKCS#8 encoded document.
160 #[cfg(feature = "alloc")]
161 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
162 pub trait ToPrivateKey {
163     /// Serialize a [`PrivateKeyDocument`] containing a PKCS#8-encoded private key.
to_pkcs8_der(&self) -> Result<PrivateKeyDocument>164     fn to_pkcs8_der(&self) -> Result<PrivateKeyDocument>;
165 
166     /// Create an [`EncryptedPrivateKeyDocument`] containing the ciphertext of
167     /// a PKCS#8 encoded private key encrypted under the given `password`.
168     #[cfg(feature = "encryption")]
169     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
to_pkcs8_encrypted_der( &self, rng: impl CryptoRng + RngCore, password: impl AsRef<[u8]>, ) -> Result<EncryptedPrivateKeyDocument>170     fn to_pkcs8_encrypted_der(
171         &self,
172         rng: impl CryptoRng + RngCore,
173         password: impl AsRef<[u8]>,
174     ) -> Result<EncryptedPrivateKeyDocument> {
175         self.to_pkcs8_der()?.encrypt(rng, password)
176     }
177 
178     /// Serialize this private key as PEM-encoded PKCS#8.
179     #[cfg(feature = "pem")]
180     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
to_pkcs8_pem(&self) -> Result<Zeroizing<String>>181     fn to_pkcs8_pem(&self) -> Result<Zeroizing<String>> {
182         self.to_pkcs8_pem_with_le(LineEnding::default())
183     }
184 
185     /// Serialize this private key as PEM-encoded PKCS#8 with the given [`LineEnding`].
186     #[cfg(feature = "pem")]
187     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
to_pkcs8_pem_with_le(&self, line_ending: LineEnding) -> Result<Zeroizing<String>>188     fn to_pkcs8_pem_with_le(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
189         Ok(self.to_pkcs8_der()?.to_pem_with_le(line_ending))
190     }
191 
192     /// Serialize this private key as an encrypted PEM-encoded PKCS#8 private
193     /// key using the `provided` to derive an encryption key.
194     #[cfg(all(feature = "encryption", feature = "pem"))]
195     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
196     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
to_pkcs8_encrypted_pem( &self, rng: impl CryptoRng + RngCore, password: impl AsRef<[u8]>, ) -> Result<Zeroizing<String>>197     fn to_pkcs8_encrypted_pem(
198         &self,
199         rng: impl CryptoRng + RngCore,
200         password: impl AsRef<[u8]>,
201     ) -> Result<Zeroizing<String>> {
202         self.to_pkcs8_encrypted_der(rng, password)
203             .map(|key| key.to_pem())
204     }
205 
206     /// Write ASN.1 DER-encoded PKCS#8 private key to the given path
207     #[cfg(feature = "std")]
208     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()>209     fn write_pkcs8_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
210         self.to_pkcs8_der()?.write_der_file(path)
211     }
212 
213     /// Write ASN.1 DER-encoded PKCS#8 private key to the given path
214     #[cfg(all(feature = "pem", feature = "std"))]
215     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
216     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
write_pkcs8_pem_file(&self, path: impl AsRef<Path>) -> Result<()>217     fn write_pkcs8_pem_file(&self, path: impl AsRef<Path>) -> Result<()> {
218         self.to_pkcs8_der()?.write_pem_file(path)
219     }
220 }
221 
222 /// Serialize a public key object to a SPKI-encoded document.
223 #[cfg(feature = "alloc")]
224 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
225 pub trait ToPublicKey {
226     /// Serialize a [`PublicKeyDocument`] containing a SPKI-encoded public key.
to_public_key_der(&self) -> Result<PublicKeyDocument>227     fn to_public_key_der(&self) -> Result<PublicKeyDocument>;
228 
229     /// Serialize this public key as PEM-encoded SPKI.
230     #[cfg(feature = "pem")]
231     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
to_public_key_pem(&self) -> Result<String>232     fn to_public_key_pem(&self) -> Result<String> {
233         self.to_public_key_pem_with_le(LineEnding::default())
234     }
235 
236     /// Serialize this public key as PEM-encoded SPKI with the given [`LineEnding`].
237     #[cfg(feature = "pem")]
238     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
to_public_key_pem_with_le(&self, line_ending: LineEnding) -> Result<String>239     fn to_public_key_pem_with_le(&self, line_ending: LineEnding) -> Result<String> {
240         Ok(self.to_public_key_der()?.to_pem_with_le(line_ending))
241     }
242 
243     /// Write ASN.1 DER-encoded public key to the given path
244     #[cfg(feature = "std")]
245     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
write_public_key_der_file(&self, path: impl AsRef<Path>) -> Result<()>246     fn write_public_key_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
247         self.to_public_key_der()?.write_der_file(path)
248     }
249 
250     /// Write ASN.1 DER-encoded public key to the given path
251     #[cfg(all(feature = "pem", feature = "std"))]
252     #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
253     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
write_public_key_pem_file(&self, path: impl AsRef<Path>) -> Result<()>254     fn write_public_key_pem_file(&self, path: impl AsRef<Path>) -> Result<()> {
255         self.to_public_key_der()?.write_pem_file(path)
256     }
257 }
258 
259 #[cfg(feature = "pkcs1")]
260 #[cfg_attr(docsrs, doc(cfg(feature = "pkcs1")))]
261 impl<K: pkcs1::FromRsaPrivateKey> FromPrivateKey for K {
from_pkcs8_private_key_info(pkcs8_key: PrivateKeyInfo<'_>) -> Result<Self>262     fn from_pkcs8_private_key_info(pkcs8_key: PrivateKeyInfo<'_>) -> Result<Self> {
263         pkcs8_key.algorithm.assert_algorithm_oid(PKCS1_OID)?;
264 
265         if pkcs8_key.algorithm.parameters != Some(der::asn1::Null.into()) {
266             return Err(Error::ParametersMalformed);
267         }
268 
269         let pkcs1_key = pkcs1::RsaPrivateKey::try_from(pkcs8_key.private_key)?;
270         Ok(K::from_pkcs1_private_key(pkcs1_key)?)
271     }
272 }
273 
274 #[cfg(feature = "pkcs1")]
275 #[cfg_attr(docsrs, doc(cfg(feature = "pkcs1")))]
276 impl<K: pkcs1::FromRsaPublicKey> FromPublicKey for K {
from_spki(pkcs8_key: SubjectPublicKeyInfo<'_>) -> Result<Self>277     fn from_spki(pkcs8_key: SubjectPublicKeyInfo<'_>) -> Result<Self> {
278         pkcs8_key.algorithm.assert_algorithm_oid(PKCS1_OID)?;
279 
280         if pkcs8_key.algorithm.parameters != Some(der::asn1::Null.into()) {
281             return Err(Error::ParametersMalformed);
282         }
283 
284         let pkcs1_key = pkcs1::RsaPublicKey::try_from(pkcs8_key.subject_public_key)?;
285         Ok(K::from_pkcs1_public_key(pkcs1_key)?)
286     }
287 }
288 
289 #[cfg(all(feature = "alloc", feature = "pkcs1"))]
290 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
291 #[cfg_attr(docsrs, doc(cfg(feature = "pkcs1")))]
292 impl<K: pkcs1::ToRsaPrivateKey> ToPrivateKey for K {
to_pkcs8_der(&self) -> Result<PrivateKeyDocument>293     fn to_pkcs8_der(&self) -> Result<PrivateKeyDocument> {
294         let pkcs1_der = self.to_pkcs1_der()?;
295 
296         let algorithm = AlgorithmIdentifier {
297             oid: PKCS1_OID,
298             parameters: Some(der::asn1::Null.into()),
299         };
300 
301         Ok(PrivateKeyInfo::new(algorithm, pkcs1_der.as_ref()).to_der())
302     }
303 }
304 
305 #[cfg(all(feature = "alloc", feature = "pkcs1"))]
306 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
307 #[cfg_attr(docsrs, doc(cfg(feature = "pkcs1")))]
308 impl<K: pkcs1::ToRsaPublicKey> ToPublicKey for K {
to_public_key_der(&self) -> Result<PublicKeyDocument>309     fn to_public_key_der(&self) -> Result<PublicKeyDocument> {
310         let pkcs1_der = self.to_pkcs1_der()?;
311 
312         let algorithm = AlgorithmIdentifier {
313             oid: PKCS1_OID,
314             parameters: Some(der::asn1::Null.into()),
315         };
316 
317         Ok(SubjectPublicKeyInfo {
318             algorithm,
319             subject_public_key: pkcs1_der.as_ref(),
320         }
321         .into())
322     }
323 }
324