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