test_cert_gen/
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]>) -> Cert {
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            return 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 {
63            tag: "CERTIFICATE".to_owned(),
64            contents: self.0.clone(),
65        })
66    }
67}
68
69/// DER-encoded.
70#[derive(Debug, PartialEq, Clone)]
71pub struct PrivateKey(Vec<u8>);
72
73impl PrivateKey {
74    fn looks_like_der(bytes: &[u8]) -> bool {
75        // Some private keys start with a sequence. TODO: what are others
76        bytes.starts_with(b"\x30")
77    }
78
79    /// Construct a private key from DER binary file.
80    pub fn from_der(key_der: impl Into<Vec<u8>>) -> PrivateKey {
81        let key_der = key_der.into();
82        // TODO: better assertion
83        if key_der.is_empty() {
84            panic!("empty private key");
85        }
86        PrivateKey(key_der)
87    }
88
89    /// Construct a private key from PEM text file.
90    ///
91    /// This operation returns an error if PEM file contains zero or more than one certificate.
92    pub fn from_pem(key_pem: impl AsRef<[u8]>) -> PrivateKey {
93        let pem = pem::parse_many(key_pem.as_ref());
94        let count = pem.len();
95        let mut keys: Vec<PrivateKey> = pem
96            .into_iter()
97            .flat_map(|p| match p.tag.as_ref() {
98                "PRIVATE KEY" | "RSA PRIVATE KEY" => Some(Self::from_der(p.contents)),
99                _ => None,
100            })
101            .collect();
102        if keys.len() == 1 {
103            return keys.swap_remove(0);
104        } else if keys.len() > 1 {
105            panic!("PEM file contains {} private keys", keys.len());
106        } else if count != 0 {
107            panic!("PEM file contains {} entries, but no private keys", count,);
108        } else if Self::looks_like_der(key_pem.as_ref()) {
109            panic!("PEM file looks like a DER file");
110        } else {
111            panic!("no private keys found in a PEM file",);
112        }
113    }
114
115    /// Get DER.
116    pub fn get_der(&self) -> &[u8] {
117        &self.0
118    }
119
120    /// Incorrect because it assumes it outputs `RSA PRIVATE KEY`
121    /// without verifying that the private key is actually RSA.
122    #[doc(hidden)]
123    pub fn to_pem_incorrect(&self) -> String {
124        pem::encode(&pem::Pem {
125            tag: "RSA PRIVATE KEY".to_owned(),
126            contents: self.0.clone(),
127        })
128    }
129}
130
131/// Parse PEM file into a pair of certificate and private key.
132pub fn pem_to_cert_key_pair(pem: &[u8]) -> (Cert, PrivateKey) {
133    let entries = pem::parse_many(pem);
134    if entries.len() != 2 {
135        panic!(
136            "PEM file should contain certificate and private key entries, got {} entries",
137            entries.len()
138        );
139    }
140    let cert = Cert::from_pem(pem);
141    let key = PrivateKey::from_pem(pem);
142    (cert, key)
143}
144
145/// DER-encoded
146pub struct Pkcs12(pub Vec<u8>);
147
148/// Pair of PKCS #12 and password.
149pub struct Pkcs12AndPassword {
150    /// PKCS #12 file, typically containing a certificate and private key.
151    pub pkcs12: Pkcs12,
152    /// Password for the file.
153    pub password: String,
154}