test_cert_gen_2/
cert.rs

1/// DER-encoded X.509 certificate.
2#[derive(Debug, Eq, PartialEq, Clone)]
3pub struct Cert(Vec<u8>);
4
5impl Cert {
6    fn looks_like_der(bytes: &[u8]) -> bool {
7        // Quick check for certificate validity:
8        // https://tools.ietf.org/html/rfc2459#section-4.1
9        // ```
10        //  Certificate  ::=  SEQUENCE  {
11        //       tbsCertificate       TBSCertificate,
12        //       signatureAlgorithm   AlgorithmIdentifier,
13        //       signatureValue       BIT STRING  }
14        // ```
15        // and `SEQUENCE` tag is 0x30
16        bytes.starts_with(b"\x30")
17    }
18
19    /// Construct from DER-encoded.
20    pub fn from_der(cert_der: impl Into<Vec<u8>>) -> Cert {
21        let cert_der = cert_der.into();
22        if !Self::looks_like_der(&cert_der) {
23            panic!("not a DER-encoded certificate");
24        }
25        Cert(cert_der)
26    }
27
28    /// Construct from PEM-DER-encoded.
29    pub fn from_pem(cert_der_pem: impl AsRef<[u8]>) -> Result<Cert, pem::PemError> {
30        let pem = pem::parse_many(cert_der_pem.as_ref())?;
31        let count = pem.len();
32        let mut certs: Vec<Cert> = pem
33            .into_iter()
34            .flat_map(|p| match p.tag() == "CERTIFICATE" {
35                true => Some(Self::from_der(p.contents())),
36                false => None,
37            })
38            .collect();
39        if certs.len() == 1 {
40            Ok(certs.swap_remove(0))
41        } else if certs.len() > 1 {
42            panic!("PEM file contains {} certificates", certs.len());
43        } else if count != 0 {
44            panic!(
45                "PEM file contains {} entries, but no certificates",
46                certs.len(),
47            );
48        } else if Self::looks_like_der(cert_der_pem.as_ref()) {
49            panic!("PEM file looks like a DER file");
50        } else {
51            panic!("no certificates found in a PEM file",);
52        }
53    }
54
55    /// Get certificate as DER.
56    pub fn get_der(&self) -> &[u8] {
57        &self.0
58    }
59
60    /// Convert a certificate to PEM format.
61    pub fn to_pem(&self) -> String {
62        pem::encode(&pem::Pem::new("CERTIFICATE", self.0.to_vec()))
63    }
64}
65
66/// DER-encoded.
67#[derive(Debug, PartialEq, Clone)]
68pub struct PrivateKey(Vec<u8>);
69
70impl PrivateKey {
71    fn looks_like_der(bytes: &[u8]) -> bool {
72        // Some private keys start with a sequence. TODO: what are others
73        bytes.starts_with(b"\x30")
74    }
75
76    /// Construct a private key from DER binary file.
77    pub fn from_der(key_der: impl Into<Vec<u8>>) -> PrivateKey {
78        let key_der = key_der.into();
79        // TODO: better assertion
80        if key_der.is_empty() {
81            panic!("empty private key");
82        }
83        PrivateKey(key_der)
84    }
85
86    /// Construct a private key from PEM text file.
87    ///
88    /// This operation returns an error if PEM file contains zero or more than one certificate.
89    pub fn from_pem(key_pem: impl AsRef<[u8]>) -> Result<PrivateKey, pem::PemError> {
90        let pem = pem::parse_many(key_pem.as_ref())?;
91        let count = pem.len();
92        let mut keys: Vec<PrivateKey> = pem
93            .into_iter()
94            .flat_map(|p| match p.tag() {
95                "PRIVATE KEY" | "RSA PRIVATE KEY" => Some(Self::from_der(p.contents())),
96                _ => None,
97            })
98            .collect();
99        if keys.len() == 1 {
100            Ok(keys.swap_remove(0))
101        } else if keys.len() > 1 {
102            panic!("PEM file contains {} private keys", keys.len());
103        } else if count != 0 {
104            panic!("PEM file contains {} entries, but no private keys", count,);
105        } else if Self::looks_like_der(key_pem.as_ref()) {
106            panic!("PEM file looks like a DER file");
107        } else {
108            panic!("no private keys found in a PEM file",);
109        }
110    }
111
112    /// Get DER.
113    pub fn get_der(&self) -> &[u8] {
114        &self.0
115    }
116
117    /// Incorrect because it assumes it outputs `RSA PRIVATE KEY`
118    /// without verifying that the private key is actually RSA.
119    #[doc(hidden)]
120    pub fn to_pem_incorrect(&self) -> String {
121        pem::encode(&pem::Pem::new("RSA PRIVATE KEY", self.0.clone()))
122    }
123}
124
125/// Parse PEM file into a pair of certificate and private key.
126pub fn pem_to_cert_key_pair(pem: &[u8]) -> Result<(Cert, PrivateKey), pem::PemError> {
127    let entries = pem::parse_many(pem)?;
128    if entries.len() != 2 {
129        panic!(
130            "PEM file should contain certificate and private key entries, got {} entries",
131            entries.len()
132        );
133    }
134    let cert = Cert::from_pem(pem)?;
135    let key = PrivateKey::from_pem(pem)?;
136    Ok((cert, key))
137}
138
139/// DER-encoded
140pub struct Pkcs12(pub Vec<u8>);
141
142/// Pair of PKCS #12 and password.
143pub struct Pkcs12AndPassword {
144    /// PKCS #12 file, typically containing a certificate and private key.
145    pub pkcs12: Pkcs12,
146    /// Password for the file.
147    pub password: String,
148}