1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/// DER-encoded X.509 certificate.
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Cert(Vec<u8>);

impl Cert {
    fn looks_like_der(bytes: &[u8]) -> bool {
        // Quick check for certificate validity:
        // https://tools.ietf.org/html/rfc2459#section-4.1
        // ```
        //  Certificate  ::=  SEQUENCE  {
        //       tbsCertificate       TBSCertificate,
        //       signatureAlgorithm   AlgorithmIdentifier,
        //       signatureValue       BIT STRING  }
        // ```
        // and `SEQUENCE` tag is 0x30
        bytes.starts_with(b"\x30")
    }

    /// Construct from DER-encoded.
    pub fn from_der(cert_der: impl Into<Vec<u8>>) -> Cert {
        let cert_der = cert_der.into();
        if !Self::looks_like_der(&cert_der) {
            panic!("not a DER-encoded certificate");
        }
        Cert(cert_der)
    }

    /// Construct from PEM-DER-encoded.
    pub fn from_pem(cert_der_pem: impl AsRef<[u8]>) -> Cert {
        let pem = pem::parse_many(cert_der_pem.as_ref());
        let count = pem.len();
        let mut certs: Vec<Cert> = pem
            .into_iter()
            .flat_map(|p| match p.tag == "CERTIFICATE" {
                true => Some(Self::from_der(p.contents)),
                false => None,
            })
            .collect();
        if certs.len() == 1 {
            return certs.swap_remove(0);
        } else if certs.len() > 1 {
            panic!("PEM file contains {} certificates", certs.len());
        } else if count != 0 {
            panic!(
                "PEM file contains {} entries, but no certificates",
                certs.len(),
            );
        } else if Self::looks_like_der(cert_der_pem.as_ref()) {
            panic!("PEM file looks like a DER file");
        } else {
            panic!("no certificates found in a PEM file",);
        }
    }

    /// Get certificate as DER.
    pub fn get_der(&self) -> &[u8] {
        &self.0
    }

    /// Convert a certificate to PEM format.
    pub fn to_pem(&self) -> String {
        pem::encode(&pem::Pem {
            tag: "CERTIFICATE".to_owned(),
            contents: self.0.clone(),
        })
    }
}

/// DER-encoded.
#[derive(Debug, PartialEq, Clone)]
pub struct PrivateKey(Vec<u8>);

impl PrivateKey {
    fn looks_like_der(bytes: &[u8]) -> bool {
        // Some private keys start with a sequence. TODO: what are others
        bytes.starts_with(b"\x30")
    }

    /// Construct a private key from DER binary file.
    pub fn from_der(key_der: impl Into<Vec<u8>>) -> PrivateKey {
        let key_der = key_der.into();
        // TODO: better assertion
        if key_der.is_empty() {
            panic!("empty private key");
        }
        PrivateKey(key_der)
    }

    /// Construct a private key from PEM text file.
    ///
    /// This operation returns an error if PEM file contains zero or more than one certificate.
    pub fn from_pem(key_pem: impl AsRef<[u8]>) -> PrivateKey {
        let pem = pem::parse_many(key_pem.as_ref());
        let count = pem.len();
        let mut keys: Vec<PrivateKey> = pem
            .into_iter()
            .flat_map(|p| match p.tag.as_ref() {
                "PRIVATE KEY" | "RSA PRIVATE KEY" => Some(Self::from_der(p.contents)),
                _ => None,
            })
            .collect();
        if keys.len() == 1 {
            return keys.swap_remove(0);
        } else if keys.len() > 1 {
            panic!("PEM file contains {} private keys", keys.len());
        } else if count != 0 {
            panic!("PEM file contains {} entries, but no private keys", count,);
        } else if Self::looks_like_der(key_pem.as_ref()) {
            panic!("PEM file looks like a DER file");
        } else {
            panic!("no private keys found in a PEM file",);
        }
    }

    /// Get DER.
    pub fn get_der(&self) -> &[u8] {
        &self.0
    }

    /// Incorrect because it assumes it outputs `RSA PRIVATE KEY`
    /// without verifying that the private key is actually RSA.
    #[doc(hidden)]
    pub fn to_pem_incorrect(&self) -> String {
        pem::encode(&pem::Pem {
            tag: "RSA PRIVATE KEY".to_owned(),
            contents: self.0.clone(),
        })
    }
}

/// Parse PEM file into a pair of certificate and private key.
pub fn pem_to_cert_key_pair(pem: &[u8]) -> (Cert, PrivateKey) {
    let entries = pem::parse_many(pem);
    if entries.len() != 2 {
        panic!(
            "PEM file should contain certificate and private key entries, got {} entries",
            entries.len()
        );
    }
    let cert = Cert::from_pem(pem);
    let key = PrivateKey::from_pem(pem);
    (cert, key)
}

/// DER-encoded
pub struct Pkcs12(pub Vec<u8>);

/// Pair of PKCS #12 and password.
pub struct Pkcs12AndPassword {
    /// PKCS #12 file, typically containing a certificate and private key.
    pub pkcs12: Pkcs12,
    /// Password for the file.
    pub password: String,
}