Skip to main content

tsumiki_pkcs/
private_key.rs

1//! Unified private key type supporting multiple formats.
2//!
3//! This module provides [`PrivateKey`], an enum that can represent private keys
4//! in any of the common formats:
5//! - PKCS#1 (RSA keys only)
6//! - SEC1 (EC keys only)
7//! - PKCS#8 (generic format for any key type)
8//!
9//! The primary use case is automatic format detection when loading keys from
10//! DER or PEM data.
11//!
12//! # PrivateKeyExt Trait
13//!
14//! The [`PrivateKeyExt`] trait provides a unified interface for accessing
15//! common private key properties across different formats:
16//!
17//! ```no_run
18//! use tsumiki::decoder::Decoder;
19//! use tsumiki_pem::Pem;
20//! use tsumiki_pkcs::{PrivateKey, PrivateKeyExt};
21//!
22//! let pem: Pem = "-----BEGIN PRIVATE KEY-----...".parse().unwrap();
23//! let key: PrivateKey = pem.decode().unwrap();
24//! println!("Algorithm: {:?}", key.algorithm());
25//! println!("Key size: {} bits", key.key_size());
26//! if let Some(pubkey) = key.public_key_bytes() {
27//!     println!("Public key available");
28//! }
29//! ```
30
31use serde::{Deserialize, Serialize};
32use tsumiki::decoder::{DecodableFrom, Decoder};
33use tsumiki_asn1::{ASN1Object, Element};
34use tsumiki_der::Der;
35use tsumiki_pem::{Label, Pem, ToPem};
36use tsumiki_pkix_types::algorithm::AlgorithmIdentifier;
37
38use crate::error::{Error, Result};
39use crate::pkcs1::RSAPrivateKey;
40use crate::pkcs8::{OID_ED448, OID_ED25519, OneAsymmetricKey};
41use crate::sec1::ECPrivateKey;
42
43/// Key algorithm type.
44///
45/// Represents the cryptographic algorithm used by a private key.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
47#[non_exhaustive]
48pub enum KeyAlgorithm {
49    /// RSA encryption
50    Rsa,
51    /// Elliptic Curve (ECDSA/ECDH)
52    Ec,
53    /// Ed25519 (EdDSA)
54    Ed25519,
55    /// Ed448 (EdDSA)
56    Ed448,
57    /// Unknown or unsupported algorithm
58    Unknown,
59}
60
61impl KeyAlgorithm {
62    /// Returns the OID string for this algorithm, if known.
63    #[must_use]
64    pub fn oid(&self) -> Option<&'static str> {
65        match self {
66            KeyAlgorithm::Rsa => Some(AlgorithmIdentifier::OID_RSA_ENCRYPTION),
67            KeyAlgorithm::Ec => Some(AlgorithmIdentifier::OID_EC_PUBLIC_KEY),
68            KeyAlgorithm::Ed25519 => Some(OID_ED25519),
69            KeyAlgorithm::Ed448 => Some(OID_ED448),
70            KeyAlgorithm::Unknown => None,
71        }
72    }
73
74    /// Returns a human-readable name for this algorithm.
75    #[must_use]
76    pub fn name(&self) -> &'static str {
77        match self {
78            KeyAlgorithm::Rsa => "RSA",
79            KeyAlgorithm::Ec => "EC",
80            KeyAlgorithm::Ed25519 => "Ed25519",
81            KeyAlgorithm::Ed448 => "Ed448",
82            KeyAlgorithm::Unknown => "Unknown",
83        }
84    }
85}
86
87impl std::fmt::Display for KeyAlgorithm {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        write!(f, "{}", self.name())
90    }
91}
92
93/// Trait for common private key operations.
94///
95/// This trait provides a unified interface for accessing properties
96/// common to all private key formats (PKCS#1, SEC1, PKCS#8).
97///
98/// # Implementors
99///
100/// - [`RSAPrivateKey`](crate::pkcs1::RSAPrivateKey) - PKCS#1 RSA private keys
101/// - [`ECPrivateKey`](crate::sec1::ECPrivateKey) - SEC1 EC private keys
102/// - [`OneAsymmetricKey`](crate::pkcs8::OneAsymmetricKey) - PKCS#8 generic private keys
103/// - [`PrivateKey`] - Unified enum for all formats
104///
105/// # Examples
106///
107/// ```no_run
108/// use tsumiki::decoder::Decoder;
109/// use tsumiki_pem::Pem;
110/// use tsumiki_pkcs::{PrivateKey, PrivateKeyExt};
111///
112/// let pem: Pem = "-----BEGIN RSA PRIVATE KEY-----...".parse().unwrap();
113/// let key: PrivateKey = pem.decode().unwrap();
114///
115/// println!("Algorithm: {}", key.algorithm());
116/// println!("Key size: {} bits", key.key_size());
117///
118/// if let Some(pubkey) = key.public_key() {
119///     println!("Public key extracted successfully");
120/// }
121/// ```
122pub trait PrivateKeyExt {
123    /// Returns the key size in bits.
124    ///
125    /// For RSA keys, this is the modulus bit length.
126    /// For EC keys, this is determined by the curve (e.g., 256 for P-256).
127    /// For Ed25519, this returns 256 bits.
128    /// For Ed448, this returns 448 bits.
129    ///
130    /// Returns 0 if the key size cannot be determined (e.g., PKCS#8 v1 keys
131    /// without embedded public key information).
132    ///
133    /// # Examples
134    ///
135    /// ```no_run
136    /// use tsumiki::decoder::Decoder;
137    /// use tsumiki_pem::Pem;
138    /// use tsumiki_pkcs::{PrivateKey, PrivateKeyExt};
139    ///
140    /// let pem: Pem = "-----BEGIN PRIVATE KEY-----...".parse().unwrap();
141    /// let key: PrivateKey = pem.decode().unwrap();
142    /// match key.key_size() {
143    ///     0 => println!("Key size unknown"),
144    ///     bits => println!("Key size: {} bits", bits),
145    /// }
146    /// ```
147    fn key_size(&self) -> u32;
148
149    /// Returns the algorithm type of this key.
150    ///
151    /// # Examples
152    ///
153    /// ```no_run
154    /// use tsumiki::decoder::Decoder;
155    /// use tsumiki_pem::Pem;
156    /// use tsumiki_pkcs::{PrivateKey, PrivateKeyExt, KeyAlgorithm};
157    ///
158    /// let pem: Pem = "-----BEGIN PRIVATE KEY-----...".parse().unwrap();
159    /// let key: PrivateKey = pem.decode().unwrap();
160    /// match key.algorithm() {
161    ///     KeyAlgorithm::Rsa => println!("RSA key"),
162    ///     KeyAlgorithm::Ec => println!("Elliptic curve key"),
163    ///     KeyAlgorithm::Ed25519 => println!("Ed25519 key"),
164    ///     KeyAlgorithm::Ed448 => println!("Ed448 key"),
165    ///     KeyAlgorithm::Unknown => println!("Unknown algorithm"),
166    ///     _ => println!("Other algorithm"),
167    /// }
168    /// ```
169    fn algorithm(&self) -> KeyAlgorithm;
170
171    /// Returns the raw public key bytes, if available.
172    ///
173    /// This method returns the public key as raw bytes when available in the
174    /// key structure. For structured access to the public key, use [`public_key()`](Self::public_key)
175    /// instead.
176    ///
177    /// # Availability
178    ///
179    /// - **PKCS#1 RSA keys**: Returns `None` (use [`public_key()`](Self::public_key) for structured access)
180    /// - **SEC1 EC keys**: Returns the uncompressed point bytes if present
181    /// - **PKCS#8 v2 keys**: Returns the public key bytes if present
182    /// - **PKCS#8 v1 keys**: Returns `None` (no public key embedded)
183    ///
184    /// # Examples
185    ///
186    /// ```no_run
187    /// use tsumiki::decoder::Decoder;
188    /// use tsumiki_pem::Pem;
189    /// use tsumiki_pkcs::{PrivateKey, PrivateKeyExt};
190    ///
191    /// let pem: Pem = "-----BEGIN PRIVATE KEY-----...".parse().unwrap();
192    /// let key: PrivateKey = pem.decode().unwrap();
193    /// if let Some(bytes) = key.public_key_bytes() {
194    ///     println!("Public key: {} bytes", bytes.len());
195    /// }
196    /// ```
197    fn public_key_bytes(&self) -> Option<&[u8]>;
198
199    /// Extracts the public key from this private key, if available.
200    ///
201    /// Returns a [`PublicKey`](crate::PublicKey) enum that can represent the public key
202    /// in either PKCS#1 or X.509/SPKI format.
203    ///
204    /// # Return Format
205    ///
206    /// The format of the returned public key depends on the input format:
207    ///
208    /// | Input Format | Output Format | Condition |
209    /// |--------------|---------------|-----------|
210    /// | PKCS#1 RSA   | `PublicKey::Pkcs1` | Always available |
211    /// | SEC1 EC      | `PublicKey::Spki` | If public key present |
212    /// | PKCS#8 v1    | `None` | No public key embedded |
213    /// | PKCS#8 v2    | `PublicKey::Spki` | If public key present |
214    ///
215    /// # Examples
216    ///
217    /// ```no_run
218    /// use tsumiki::decoder::Decoder;
219    /// use tsumiki_pem::Pem;
220    /// use tsumiki_pkcs::{PrivateKey, PrivateKeyExt, PublicKey};
221    /// use tsumiki_pem::ToPem;
222    ///
223    /// let pem: Pem = "-----BEGIN PRIVATE KEY-----...".parse().unwrap();
224    /// let key: PrivateKey = pem.decode().unwrap();
225    /// if let Some(pubkey) = key.public_key() {
226    ///     // Export to PEM format
227    ///     let pem = pubkey.to_pem().unwrap();
228    ///     println!("{}", pem);
229    ///
230    ///     // Check the format
231    ///     match pubkey {
232    ///         PublicKey::Pkcs1(_) => println!("PKCS#1 RSA public key"),
233    ///         PublicKey::Spki(_) => println!("X.509/SPKI public key"),
234    ///     }
235    /// }
236    /// ```
237    fn public_key(&self) -> Option<crate::PublicKey>;
238}
239
240/// A private key in one of the supported formats.
241///
242/// This enum allows handling private keys without knowing their exact format
243/// ahead of time. Use [`PrivateKey::from_der`] or the `Decoder` implementation
244/// for `Pem` to automatically detect the format.
245#[derive(Debug, Clone)]
246pub enum PrivateKey {
247    /// PKCS#1 RSA private key (RSA keys only)
248    Pkcs1(RSAPrivateKey),
249    /// SEC1 EC private key (elliptic curve keys only)
250    Sec1(ECPrivateKey),
251    /// PKCS#8 private key (generic format)
252    Pkcs8(OneAsymmetricKey),
253}
254
255impl PrivateKey {
256    /// Attempt to parse a private key from DER-encoded bytes.
257    ///
258    /// This method tries each format in order:
259    /// 1. PKCS#8 (most common for modern keys)
260    /// 2. SEC1 (EC keys)
261    /// 3. PKCS#1 (RSA keys)
262    ///
263    /// # Errors
264    ///
265    /// Returns an error if the data cannot be parsed as any known format.
266    pub fn from_der(bytes: &[u8]) -> Result<Self> {
267        let der: Der = bytes.to_vec().decode()?;
268        let asn1_obj: ASN1Object = der.decode()?;
269
270        let element = asn1_obj.elements().first().ok_or(Error::EmptyAsn1Object)?;
271
272        element.decode()
273    }
274
275    /// Get the key size in bits.
276    ///
277    /// This is a convenience method that delegates to [`PrivateKeyExt::key_size`].
278    pub fn key_size(&self) -> u32 {
279        <Self as PrivateKeyExt>::key_size(self)
280    }
281
282    /// Get the algorithm type of this key.
283    ///
284    /// This is a convenience method that delegates to [`PrivateKeyExt::algorithm`].
285    pub fn algorithm(&self) -> KeyAlgorithm {
286        <Self as PrivateKeyExt>::algorithm(self)
287    }
288
289    /// Get the public key bytes, if available.
290    ///
291    /// This is a convenience method that delegates to [`PrivateKeyExt::public_key_bytes`].
292    pub fn public_key_bytes(&self) -> Option<&[u8]> {
293        <Self as PrivateKeyExt>::public_key_bytes(self)
294    }
295
296    /// Extract the public key from this private key, if available.
297    ///
298    /// This is a convenience method that delegates to [`PrivateKeyExt::public_key`].
299    pub fn public_key(&self) -> Option<crate::PublicKey> {
300        <Self as PrivateKeyExt>::public_key(self)
301    }
302
303    /// Returns `true` if this is a PKCS#1 RSA key.
304    pub fn is_pkcs1(&self) -> bool {
305        matches!(self, PrivateKey::Pkcs1(_))
306    }
307
308    /// Returns `true` if this is a SEC1 EC key.
309    pub fn is_sec1(&self) -> bool {
310        matches!(self, PrivateKey::Sec1(_))
311    }
312
313    /// Returns `true` if this is a PKCS#8 key.
314    pub fn is_pkcs8(&self) -> bool {
315        matches!(self, PrivateKey::Pkcs8(_))
316    }
317
318    /// Try to get the inner PKCS#1 RSA key.
319    pub fn as_pkcs1(&self) -> Option<&RSAPrivateKey> {
320        match self {
321            PrivateKey::Pkcs1(key) => Some(key),
322            _ => None,
323        }
324    }
325
326    /// Try to get the inner SEC1 EC key.
327    pub fn as_sec1(&self) -> Option<&ECPrivateKey> {
328        match self {
329            PrivateKey::Sec1(key) => Some(key),
330            _ => None,
331        }
332    }
333
334    /// Try to get the inner PKCS#8 key.
335    pub fn as_pkcs8(&self) -> Option<&OneAsymmetricKey> {
336        match self {
337            PrivateKey::Pkcs8(key) => Some(key),
338            _ => None,
339        }
340    }
341
342    /// Consume and return the inner PKCS#1 RSA key.
343    pub fn into_pkcs1(self) -> Option<RSAPrivateKey> {
344        match self {
345            PrivateKey::Pkcs1(key) => Some(key),
346            _ => None,
347        }
348    }
349
350    /// Consume and return the inner SEC1 EC key.
351    pub fn into_sec1(self) -> Option<ECPrivateKey> {
352        match self {
353            PrivateKey::Sec1(key) => Some(key),
354            _ => None,
355        }
356    }
357
358    /// Consume and return the inner PKCS#8 key.
359    pub fn into_pkcs8(self) -> Option<OneAsymmetricKey> {
360        match self {
361            PrivateKey::Pkcs8(key) => Some(key),
362            _ => None,
363        }
364    }
365}
366
367// Element -> PrivateKey decoder
368impl DecodableFrom<Element> for PrivateKey {}
369
370impl Decoder<Element, PrivateKey> for Element {
371    type Error = Error;
372
373    /// Attempt to parse a private key from an ASN.1 element.
374    ///
375    /// This method tries each format in order:
376    /// 1. PKCS#8 (most common for modern keys)
377    /// 2. SEC1 (EC keys)
378    /// 3. PKCS#1 (RSA keys)
379    fn decode(&self) -> Result<PrivateKey> {
380        // Try PKCS#8 first (most common modern format)
381        let pkcs8_err = match self.decode() {
382            Ok(key) => return Ok(PrivateKey::Pkcs8(key)),
383            Err(e) => e,
384        };
385
386        // Try SEC1 (EC private key)
387        let sec1_err = match self.decode() {
388            Ok(key) => return Ok(PrivateKey::Sec1(key)),
389            Err(e) => e,
390        };
391
392        // Try PKCS#1 (RSA private key)
393        let pkcs1_err = match self.decode() {
394            Ok(key) => return Ok(PrivateKey::Pkcs1(key)),
395            Err(e) => e,
396        };
397
398        Err(Error::UnrecognizedPrivateKeyFormat {
399            pkcs8: Box::new(pkcs8_err),
400            sec1: Box::new(sec1_err),
401            pkcs1: Box::new(pkcs1_err),
402        })
403    }
404}
405
406// Pem -> PrivateKey decoder
407impl DecodableFrom<Pem> for PrivateKey {}
408
409impl Decoder<Pem, PrivateKey> for Pem {
410    type Error = Error;
411
412    fn decode(&self) -> Result<PrivateKey> {
413        // Use label to determine format when available
414        match self.label() {
415            Label::RSAPrivateKey => Ok(PrivateKey::Pkcs1(self.decode()?)),
416            Label::ECPrivateKey => Ok(PrivateKey::Sec1(self.decode()?)),
417            Label::PrivateKey => Ok(PrivateKey::Pkcs8(self.decode()?)),
418            _ => {
419                // Try to auto-detect from content
420                let der: Der = self.decode()?;
421                let asn1_obj: ASN1Object = der.decode()?;
422
423                let element = asn1_obj.elements().first().ok_or(Error::EmptyAsn1Object)?;
424
425                element.decode()
426            }
427        }
428    }
429}
430
431impl From<RSAPrivateKey> for PrivateKey {
432    fn from(key: RSAPrivateKey) -> Self {
433        PrivateKey::Pkcs1(key)
434    }
435}
436
437impl From<ECPrivateKey> for PrivateKey {
438    fn from(key: ECPrivateKey) -> Self {
439        PrivateKey::Sec1(key)
440    }
441}
442
443impl From<OneAsymmetricKey> for PrivateKey {
444    fn from(key: OneAsymmetricKey) -> Self {
445        PrivateKey::Pkcs8(key)
446    }
447}
448
449impl ToPem for PrivateKey {
450    type Error = Error;
451
452    fn pem_label(&self) -> Label {
453        match self {
454            PrivateKey::Pkcs1(_) => Label::RSAPrivateKey,
455            PrivateKey::Sec1(_) => Label::ECPrivateKey,
456            PrivateKey::Pkcs8(_) => Label::PrivateKey,
457        }
458    }
459
460    fn to_pem(&self) -> Result<Pem> {
461        match self {
462            PrivateKey::Pkcs1(key) => key.to_pem().map_err(Error::from),
463            PrivateKey::Sec1(key) => key.to_pem().map_err(Error::from),
464            PrivateKey::Pkcs8(key) => key.to_pem().map_err(Error::from),
465        }
466    }
467}
468
469impl PrivateKeyExt for PrivateKey {
470    fn key_size(&self) -> u32 {
471        match self {
472            PrivateKey::Pkcs1(key) => key.key_size(),
473            PrivateKey::Sec1(key) => key.key_size(),
474            PrivateKey::Pkcs8(key) => key.key_size(),
475        }
476    }
477
478    fn algorithm(&self) -> KeyAlgorithm {
479        match self {
480            PrivateKey::Pkcs1(key) => key.algorithm(),
481            PrivateKey::Sec1(key) => key.algorithm(),
482            PrivateKey::Pkcs8(key) => key.algorithm(),
483        }
484    }
485
486    fn public_key_bytes(&self) -> Option<&[u8]> {
487        match self {
488            PrivateKey::Pkcs1(key) => key.public_key_bytes(),
489            PrivateKey::Sec1(key) => key.public_key_bytes(),
490            PrivateKey::Pkcs8(key) => key.public_key_bytes(),
491        }
492    }
493
494    fn public_key(&self) -> Option<crate::PublicKey> {
495        match self {
496            PrivateKey::Pkcs1(key) => PrivateKeyExt::public_key(key),
497            PrivateKey::Sec1(key) => PrivateKeyExt::public_key(key),
498            PrivateKey::Pkcs8(key) => PrivateKeyExt::public_key(key),
499        }
500    }
501}
502
503#[cfg(test)]
504mod tests {
505    use super::*;
506    use rstest::rstest;
507    use std::str::FromStr;
508
509    const RSA_2048_PKCS1: &str = r#"-----BEGIN RSA PRIVATE KEY-----
510MIIEpAIBAAKCAQEAvf4anqhlMYhVhpOv8XK/ygPFUxkNa8Rh9NNTVlqiWuPgD4Lj
5117YCsa31kQwYgOKADsG5ROApHSjKsWrKQ70DSpxZmPiO8j7jFQdUJLbe/hfiFskoM
512Ur+V5imxrkJB5cnBgIw49ykn0mVtyLRG9RS8Xv+XqNEHFnugS7z2cFQqKYI8oq2L
513yLxSbMzDlzkB1p64u5p6Gy0W3KQZt42/sompo+swMslw+XN2rSNFfUWfJWGdEFJc
514Sl+9oOz7y9ZGv56uC3VdGnU9u6MmC3iMZ/Vf9qQIHOr6KE6IaJNvHPSAET7qnBWJ
515q+x0UrsMJmGdkjGvE3MgIjgaLxjgn/sfO1++vwIDAQABAoIBAEp5BUQ1q9zbnPKw
516h2H0Yds02S82fb1FcERAZcVOp59K/XP3EZLyQiOsNhXTm+O2TVvmEi4OUV1zOX4f
517ypIN7cSTEia/aVVIzwF8GSnzgb5o6Tc2sVfqQz7CDyTIUf5ZtGDIFjhDyJk/KuZm
518S/4bT69JLtB8hvO4J+AoRM1JIHG+Lpe1p+Vsudk3+/AKiyx4tU1Z/zR3Rm9GxUd0
519XHZAUhnYumrczJeq9XS9ufvgJUZ0q+qdAuG4PL4+0KAblS+biad0mv32ibkGsiXt
520CvcZwIMlzQvt+Ai6Oa9GK6lfgrpYYKwZry6pnzI4/j6db4fnWXcNnkHDir7YjsZK
5218QTlfOkCgYEA8cilQsTcF2GRC4CMwGpz/7rZAgjLn7ucscqVhzQIFrZNpMtq2LEL
522/QNMa7dayDryr2b4RAcA2ns5WCRRCSslpVcXwrPDyxzhKdmnCTbu8nLTwtuRYzMU
523s/Oeex7o37aKwpiNQzfqqGTZy0xMulma//M6mX5D14bN4oVt43zx25UCgYEAySnk
524afMoZaLoW3rzDqiq8G3+M8tnFjhs7/r8Bz1BUuOfMjfK8ZFYWLseC8DaiOGLdJl8
5254P98R81xZp4KlYMqbLeIM1f/uo3um7a8AiD2ueuW8qe2xB+5vbiNpJU/fruOU+Bk
526FAZmaIGk8DdUom7SPktKTREYwiZ4o0BF/On2fAMCgYEAietymcvB4HR/UJhbsccH
527tHDZKRfrT4qtr51n/l/n3UzQrZh7snAL7p/bD/bfiihWF0gdhnCYRAjWhTjyINDE
528ALTVkPMKVOp8ZmsJpW/4jcSClzy4imWxAZWOaZ0QKczvCmIK8rUK3lPpCNbVTdef
529WzFb1AL6oA79kqGaNZIoRKECgYA2HVzi25S8cqyLH3IPOXRypURC7q7WnWtAy4XM
5309L+D6tPCkJu5jF310LBufPzM4c/AGCIt7MykDDI7Zrx2KAjboiuzlDKpHtFXdjrx
531X6i/rw62TEOwUtCGpwUDh1rDXvUUv0Js2KPn7ShPrrLH14QbWems/bJpWCwPzpSF
532SvMRvQKBgQDUNNVtpsS/4GwAmKwmLaHrbCn8oBlWBjpSS8NGbyQfA9ErllMLz3OO
533s2qerzz5oOlJm54dGAWRm1e7wTqUdeVOmCCceEvztVUsPfjPUgk7x4pfiFVUaltS
534t1uLx7BFNLk8mjqiaognIGpAlEtRJi+LPZQmIOzmPd0eZKAHNozgwQ==
535-----END RSA PRIVATE KEY-----"#;
536
537    // P-256 (prime256v1) EC private key
538    const EC_P256_SEC1: &str = r#"-----BEGIN EC PRIVATE KEY-----
539MHcCAQEEIIfdTjEBvN2/AupnhPeL8585jLgieLQmi4SfX/FVrTxZoAoGCCqGSM49
540AwEHoUQDQgAEmvfw1VdwIlsJHfbHLhHXrO3Wq/0LBCduo6Nb96AiLGUxkn/OWt1I
5419STYYNw8e/Xuzsy9j5joSxQDwmCWSGPGWw==
542-----END EC PRIVATE KEY-----"#;
543
544    const RSA_2048_PKCS8: &str = r#"-----BEGIN PRIVATE KEY-----
545MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDmv7EEQO9B/tSS
546jlFB5L79XppctPwwSfjTb5QzvemWzHkG4PZG79WkNMj8UPcrixTIkZpf32y5WEGX
547QXArkFRUmboasfRQaleLEPeOPCBibIrZkGXokhidm4A8ZeqU92rkwMYC5C8+4Pdd
5484Kpzm/R7+IYXXXu9u1BVSg95z5RPSzcPTx0BDhgPZC7fIwkZwJmicv8zaIXKBddI
549Jm8YLrmjAwxft21NxcrSbCT8DWVHX+75xye6IGAsTt2fBn05BiYnjkK6ZwBwccdo
55030fmtmfcFsC8xOIXPNxOQPcLnFWZZcMkQLCHUybd2+mOFEWsghHYlQ6LyAo/66FV
551He+lH4mjAgMBAAECggEADLiSrLZbulqvI2k/J4/Ry6wUfHnp0UuysQ1csUGOCCc7
552oNp0GVMNhyD115srFTZ0rd4BEboCh3FLJGiSI4SwcX2MGf6nhmtmah9EVo4QBv0O
5535pGkXJ75Rm8VMb84oH/HX9cU04H67M+AM6e4HemCH/eChPU9ZidWdW1AzylXdsuG
5546gySsjkd47zDeNDVhK5fBfH7kzogNlh9RdzDmkrpYm5F4hkgus8xWKpPUBpdquSh
555/dBF5OW8gEuA6kYASzIcAYZK2TZuQHHGRpJkBkwbte61BwWZEGodYiXYESWNHfPA
5561UkwQdf0zzMO0BHynmkGsoBElvtWbmT6sqwLr/vH0QKBgQD9iXwBBdN0z+1T3Jy2
557UlYwET/yPZzmkFnHxkpZi5/jBxK5nCJO6wNXcEJfYtlDDO8mleJkAPfy06AEL1YQ
558T5Df/4PnSmLNUYz4QO6qLxj9pvuOfAyPqSxKmjrvqyJGHw79N50DPh80Pap5bJ1v
559XmB8iwS/jVbwphxKm3h4cNywqwKBgQDo/YkVaAFOzH2kjU72NJyHKYmrcs4kQg3e
560KsanJw6K1zKxQjM1fTGuswiK1IhBUL0aICMjS4AL/TVjemTspmaFmQiPMmxlFR0o
561sUfwNwDS/91Fi22QSSLvWvFAxTBsVVyZNkGlRuuhD3H8fGNx4MF+8jvXuhJWV75l
56215DAHLQ66QKBgQCPqSqhrbpu0y7IORZ3XNpHbE7OpUjVgG/O+jXA3ZPgYW6jy6vJ
563CfOfxRVm1S0EiDyuoXlhbwcQCgf+tw/OODeAJVmJYiXv70iwlqJlvkAr4kViLDo1
5644Qce0puYmGDYWNr2cl++qaGmyVZibUAcDd8gUumC3MSpoYYgZE3z+Qej9wKBgEuo
5652XVMGvCd00c2ZCfrmdECmiRE2dBIavx0Y6IwOra3f0y0tLBwAUw781AyCDU9pMrx
566GLgDcodyKH4vZsq6lpxXv8HQnAaPPrLSLwxAsFHUqORGjMPIHEIiBCoGXt0vMyzF
567w7eKOkZJH7jgI+L9G5i/zNMXJ5FGWRv1Tpo0OArRAoGBAOlRIE7hsCpEUtpbRMIl
568B26vMthQdq8njgnpL9bubV82MXcTqzxe6mwHezLMEB0BYmb+lX5ktZOonqOgQWsj
569rLdkb1HDq7D30YEoDvwfuTAoewGO/QBf+jXMHWx5TRUopcU/61bCI4D1zp/urrXo
570JAOJrxibNzk6iWT9+VFcxO3m
571-----END PRIVATE KEY-----"#;
572
573    #[rstest]
574    #[case(RSA_2048_PKCS1, true, false, false, Some(2048))]
575    #[case(EC_P256_SEC1, false, true, false, Some(256))]
576    // PKCS#8 v1 keys don't have public key, so key_size returns 0
577    #[case(RSA_2048_PKCS8, false, false, true, None)]
578    fn test_private_key_from_pem(
579        #[case] pem_str: &str,
580        #[case] is_pkcs1: bool,
581        #[case] is_sec1: bool,
582        #[case] is_pkcs8: bool,
583        #[case] expected_bits: Option<u32>,
584    ) {
585        let pem = Pem::from_str(pem_str).expect("Failed to parse PEM");
586        let key: PrivateKey = pem.decode().expect("Failed to decode PrivateKey");
587
588        assert_eq!(key.is_pkcs1(), is_pkcs1);
589        assert_eq!(key.is_sec1(), is_sec1);
590        assert_eq!(key.is_pkcs8(), is_pkcs8);
591        if let Some(expected) = expected_bits {
592            assert_eq!(key.key_size(), expected);
593        }
594    }
595
596    #[test]
597    fn test_private_key_accessors() {
598        let pem = Pem::from_str(RSA_2048_PKCS1).expect("Failed to parse PEM");
599        let key: PrivateKey = pem.decode().expect("Failed to decode PrivateKey");
600
601        assert!(key.as_pkcs1().is_some());
602        assert!(key.as_sec1().is_none());
603        assert!(key.as_pkcs8().is_none());
604
605        let inner = key.into_pkcs1();
606        assert!(inner.is_some());
607    }
608
609    #[derive(Debug, Clone, Copy)]
610    enum KeyFormat {
611        Pkcs1,
612        Sec1,
613        Pkcs8,
614    }
615
616    #[rstest]
617    #[case::pkcs1(RSA_2048_PKCS1, KeyFormat::Pkcs1)]
618    #[case::sec1(EC_P256_SEC1, KeyFormat::Sec1)]
619    #[case::pkcs8(RSA_2048_PKCS8, KeyFormat::Pkcs8)]
620    fn test_from_conversions(#[case] pem_str: &str, #[case] format: KeyFormat) {
621        let pem = Pem::from_str(pem_str).expect("Failed to parse PEM");
622
623        let key = match format {
624            KeyFormat::Pkcs1 => {
625                let rsa_key: RSAPrivateKey = pem.decode().expect("Failed to decode RSAPrivateKey");
626                PrivateKey::from(rsa_key)
627            }
628            KeyFormat::Sec1 => {
629                let ec_key: ECPrivateKey = pem.decode().expect("Failed to decode ECPrivateKey");
630                PrivateKey::from(ec_key)
631            }
632            KeyFormat::Pkcs8 => {
633                let pkcs8_key: OneAsymmetricKey =
634                    pem.decode().expect("Failed to decode OneAsymmetricKey");
635                PrivateKey::from(pkcs8_key)
636            }
637        };
638
639        match format {
640            KeyFormat::Pkcs1 => assert!(key.is_pkcs1()),
641            KeyFormat::Sec1 => assert!(key.is_sec1()),
642            KeyFormat::Pkcs8 => assert!(key.is_pkcs8()),
643        }
644    }
645
646    #[rstest]
647    #[case::pkcs1_rsa(RSA_2048_PKCS1, true, KeyAlgorithm::Rsa, 2048, 2048)]
648    #[case::sec1_ec(EC_P256_SEC1, true, KeyAlgorithm::Ec, 256, 520)]
649    #[case::pkcs8_rsa_v1(RSA_2048_PKCS8, false, KeyAlgorithm::Rsa, 0, 0)]
650    fn test_public_key_extraction(
651        #[case] pem_str: &str,
652        #[case] has_public_key: bool,
653        #[case] expected_algorithm: KeyAlgorithm,
654        #[case] expected_private_key_size: u32,
655        #[case] expected_public_key_size: u32,
656    ) {
657        let pem = Pem::from_str(pem_str).expect("Failed to parse PEM");
658        let key: PrivateKey = pem.decode().expect("Failed to decode PrivateKey");
659
660        // Verify algorithm
661        assert_eq!(key.algorithm(), expected_algorithm);
662
663        // Verify key size
664        assert_eq!(key.key_size(), expected_private_key_size);
665
666        // Verify public key extraction
667        let pub_key = key.public_key();
668        assert_eq!(pub_key.is_some(), has_public_key);
669
670        if let Some(pub_key) = pub_key {
671            // Public key should have the same algorithm
672            assert_eq!(pub_key.algorithm(), expected_algorithm);
673
674            // Verify public key size
675            assert_eq!(pub_key.key_size(), expected_public_key_size);
676        }
677    }
678
679    #[test]
680    fn test_public_key_extraction_pkcs1_returns_pkcs1_format() {
681        let pem = Pem::from_str(RSA_2048_PKCS1).expect("Failed to parse PEM");
682        let key: PrivateKey = pem.decode().expect("Failed to decode PrivateKey");
683
684        let pub_key = key.public_key().expect("Should have public key");
685        assert!(pub_key.is_pkcs1());
686        assert!(!pub_key.is_spki());
687    }
688
689    #[test]
690    fn test_public_key_extraction_sec1_returns_spki_format() {
691        let pem = Pem::from_str(EC_P256_SEC1).expect("Failed to parse PEM");
692        let key: PrivateKey = pem.decode().expect("Failed to decode PrivateKey");
693
694        let pub_key = key.public_key().expect("Should have public key");
695        assert!(!pub_key.is_pkcs1());
696        assert!(pub_key.is_spki());
697    }
698
699    #[rstest]
700    #[case::pkcs1_rsa(RSA_2048_PKCS1, Label::RSAPrivateKey)]
701    #[case::sec1_ec(EC_P256_SEC1, Label::ECPrivateKey)]
702    #[case::pkcs8_rsa(RSA_2048_PKCS8, Label::PrivateKey)]
703    fn test_to_pem_roundtrip(#[case] pem_str: &str, #[case] expected_label: Label) {
704        let original_pem = Pem::from_str(pem_str).expect("Failed to parse PEM");
705        let key: PrivateKey = original_pem.decode().expect("Failed to decode PrivateKey");
706
707        // Verify PEM label
708        assert_eq!(key.pem_label(), expected_label);
709
710        // Convert to PEM
711        let exported_pem = key.to_pem().expect("Failed to export to PEM");
712        assert_eq!(exported_pem.label(), expected_label);
713
714        // Decode back and verify
715        let decoded_key: PrivateKey = exported_pem
716            .decode()
717            .expect("Failed to decode exported PEM");
718        assert_eq!(decoded_key.algorithm(), key.algorithm());
719        assert_eq!(decoded_key.key_size(), key.key_size());
720    }
721}