1 use crate::{
2     AlgorithmIdentifier, AuthorityKeyIdentifier, BasicConstraints, Extension, ExtensionView, Extensions, Name,
3     SubjectPublicKeyInfo, Validity, Version,
4 };
5 use picky_asn1::wrapper::{ApplicationTag0, ApplicationTag3, BitStringAsn1, IntegerAsn1};
6 use serde::{de, Deserialize, Serialize};
7 use std::fmt;
8 
9 /// [RFC 5280 #4.1](https://tools.ietf.org/html/rfc5280#section-4.1)
10 ///
11 /// ```not_rust
12 /// Certificate  ::=  SEQUENCE  {
13 ///      tbsCertificate       TBSCertificate,
14 ///      signatureAlgorithm   AlgorithmIdentifier,
15 ///      signatureValue       BIT STRING  }
16 /// ```
17 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
18 pub struct Certificate {
19     pub tbs_certificate: TBSCertificate,
20     pub signature_algorithm: AlgorithmIdentifier,
21     pub signature_value: BitStringAsn1,
22 }
23 
24 impl Certificate {
h_find_extension(&self, key_identifier_oid: &oid::ObjectIdentifier) -> Option<&Extension>25     fn h_find_extension(&self, key_identifier_oid: &oid::ObjectIdentifier) -> Option<&Extension> {
26         (self.tbs_certificate.extensions.0)
27             .0
28             .iter()
29             .find(|ext| ext.extn_id() == key_identifier_oid)
30     }
31 
subject_key_identifier(&self) -> Option<&[u8]>32     pub fn subject_key_identifier(&self) -> Option<&[u8]> {
33         let ext = self.h_find_extension(&crate::oids::subject_key_identifier())?;
34         match ext.extn_value() {
35             ExtensionView::SubjectKeyIdentifier(ski) => Some(&ski.0),
36             _ => None,
37         }
38     }
39 
authority_key_identifier(&self) -> Option<&AuthorityKeyIdentifier>40     pub fn authority_key_identifier(&self) -> Option<&AuthorityKeyIdentifier> {
41         let ext = self.h_find_extension(&crate::oids::authority_key_identifier())?;
42         match ext.extn_value() {
43             ExtensionView::AuthorityKeyIdentifier(aki) => Some(aki),
44             _ => None,
45         }
46     }
47 
basic_constraints(&self) -> Option<&BasicConstraints>48     pub fn basic_constraints(&self) -> Option<&BasicConstraints> {
49         let ext = self.h_find_extension(&crate::oids::basic_constraints())?;
50         match ext.extn_value() {
51             ExtensionView::BasicConstraints(bc) => Some(bc),
52             _ => None,
53         }
54     }
55 
extensions(&self) -> &[Extension]56     pub fn extensions(&self) -> &[Extension] {
57         (self.tbs_certificate.extensions.0).0.as_slice()
58     }
59 }
60 
61 /// [RFC 5280 #4.1](https://tools.ietf.org/html/rfc5280#section-4.1)
62 ///
63 /// ```not_rust
64 /// TBSCertificate  ::=  SEQUENCE  {
65 ///      version         [0]  EXPLICIT Version DEFAULT v1,
66 ///      serialNumber         CertificateSerialNumber,
67 ///      signature            AlgorithmIdentifier,
68 ///      issuer               Name,
69 ///      validity             Validity,
70 ///      subject              Name,
71 ///      subjectPublicKeyInfo SubjectPublicKeyInfo,
72 ///      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
73 ///                           -- If present, version MUST be v2 or v3
74 ///      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
75 ///                           -- If present, version MUST be v2 or v3
76 ///      extensions      [3]  EXPLICIT Extensions OPTIONAL
77 ///                           -- If present, version MUST be v3
78 ///      }
79 /// ```
80 #[derive(Serialize, Clone, Debug, PartialEq)]
81 pub struct TBSCertificate {
82     pub version: ApplicationTag0<Version>,
83     pub serial_number: IntegerAsn1,
84     pub signature: AlgorithmIdentifier,
85     pub issuer: Name,
86     pub validity: Validity,
87     pub subject: Name,
88     pub subject_public_key_info: SubjectPublicKeyInfo,
89     // issuer_unique_id
90     // subject_unique_id
91     pub extensions: ApplicationTag3<Extensions>,
92 }
93 
94 // Implement Deserialize manually to return an easy to understand error on V1 certificates
95 // (aka ApplicationTag0 not present).
96 impl<'de> de::Deserialize<'de> for TBSCertificate {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: de::Deserializer<'de>,97     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98     where
99         D: de::Deserializer<'de>,
100     {
101         struct Visitor;
102 
103         impl<'de> de::Visitor<'de> for Visitor {
104             type Value = TBSCertificate;
105 
106             fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
107                 formatter.write_str("struct TBSCertificate")
108             }
109 
110             fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
111             where
112                 V: de::SeqAccess<'de>,
113             {
114                 Ok(TBSCertificate {
115                     version: seq
116                         .next_element()
117                         .map_err(|_| {
118                             de::Error::invalid_value(
119                                 de::Unexpected::Other(
120                                     "[TBSCertificate] V1 certificates unsupported. Only V3 certificates \
121                                      are supported",
122                                 ),
123                                 &"a supported certificate",
124                             )
125                         })?
126                         .ok_or_else(|| de::Error::invalid_length(0, &self))?,
127                     serial_number: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?,
128                     signature: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
129                     issuer: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?,
130                     validity: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?,
131                     subject: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?,
132                     subject_public_key_info: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(6, &self))?,
133                     extensions: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(7, &self))?,
134                 })
135             }
136         }
137 
138         deserializer.deserialize_seq(Visitor)
139     }
140 }
141 
142 #[cfg(test)]
143 mod tests {
144     use super::*;
145     use crate::{DirectoryName, Extension, KeyIdentifier, KeyUsage};
146     use num_bigint_dig::BigInt;
147     use picky_asn1::bit_string::BitString;
148     use picky_asn1::date::UTCTime;
149 
150     #[test]
x509_v3_certificate()151     fn x509_v3_certificate() {
152         let encoded = base64::decode(
153             "MIIEGjCCAgKgAwIBAgIEN8NXxDANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQ\
154              DDBdjb250b3NvLmxvY2FsIEF1dGhvcml0eTAeFw0xOTEwMTcxNzQxMjhaFw0yMjEwM\
155              TYxNzQxMjhaMB0xGzAZBgNVBAMMEnRlc3QuY29udG9zby5sb2NhbDCCASIwDQYJKoZ\
156              IhvcNAQEBBQADggEPADCCAQoCggEBAMptALdk7xKj9JmFSycxlaTV47oLv5Aabir17\
157              f1WseAcZ492Mx0wqcJMmT8rVAusyfqvrhodHu4GELGBySo4KChLEuoEOGTNw/wEMtM\
158              6j1E9K7kig1iiuH9nf9oow7OUdix4+w7TWQWpwl1NekKdTtvLLtEGSjmG187CUqR6f\
159              NHYag+iVMV5Umc5VQadvAgva8qxOsPpDkN/E2df5gST7H5g3igaZtxUa3x7VreN3qJ\
160              P0+hYQiyM7KsgmdFAkKpHC6/k36H7SXtpzh0NbH5OJHifYsAP34WL+a6lAd0VM7UiI\
161              RMcLWA8HfmKL3p4bC+LFv5I0dvUUy1BTz1wHpRvVz8CAwEAAaNdMFswCQYDVR0TBAI\
162              wADAOBgNVHQ8BAf8EBAMCAaAwHQYDVR0OBBYEFCMimIgHf5c00sI9jZzeWoMLsR60M\
163              B8GA1UdIwQYMBaAFBbHC24DEnsUFLz/zmqB5cMCHo9OMA0GCSqGSIb3DQEBCwUAA4I\
164              CAQA1ehZTTBbes2DgGXwQugoV9PdOGMFEVT4dzrrluo/4exSfqLrNuY2NXVuNBKW4n\
165              DA5aD71Q/KUZ8Y8cV9qa8OBJQvQ0dd0qeHmeEYdDsj5YD4ECycKx9U1ZX5fi6tpSIX\
166              6DsietpCnrw4aTgbEOvMeQcuYCTP30Vpt+mYEKBlR/E2Vcl2zUD+67gqppSaC1RceL\
167              /8Cy6ZXlPqwmS2zqK9UhYVRKlEww8xSh/9CR9MmIDc4pHtCpMawcn6Dmo+A+LcKi5v\
168              /NIwvSJTei+h1gvRhvEOPcf4VZJMHXquNrxkMsKpuu7g/AYH7wl2MBaNaxyNlXY5e5\
169              OjxslrbRCfDab11YaJEONcBnapl/+Ajr70uVFN09tDXyk0EHYf75NiRztgVKclna26\
170              zP5qRb0JSYNQJW2kIIBX6DhU7kt6RcauF2hJ+jLWOF2vsAS8PdEr7vnR1EGOrrcQ3V\
171              UgMscNsDqf50YMi2Inu1Kt2t+QSvYs61ON39aVpqR67nskdUWzFCVgWQVezM1ZagoO\
172              yNp7WjRYl8hJ0YVZ7TRtP8nJOkZ6s046YHVWxMuGdqZfd/AUFb9xzzXjGRuuZ1JmSf\
173              +VBOFEe2MaPMyMQBeIs3Othz6Fcy6Am5F6c3It31WYJwiCa/NdbMIvGy1xvAN5kzR/\
174              Y6hkoQljoSr1rVuszJ9dtvuTccA==",
175         )
176         .expect("invalid base64");
177 
178         // Issuer
179 
180         let issuer: Name = DirectoryName::new_common_name("contoso.local Authority").into();
181         check_serde!(issuer: Name in encoded[34..70]);
182 
183         // Validity
184 
185         let validity = Validity {
186             not_before: UTCTime::new(2019, 10, 17, 17, 41, 28).unwrap().into(),
187             not_after: UTCTime::new(2022, 10, 16, 17, 41, 28).unwrap().into(),
188         };
189         check_serde!(validity: Validity in encoded[70..102]);
190 
191         // Subject
192 
193         let subject: Name = DirectoryName::new_common_name("test.contoso.local").into();
194         check_serde!(subject: Name in encoded[102..133]);
195 
196         // SubjectPublicKeyInfo
197 
198         let subject_public_key_info = SubjectPublicKeyInfo::new_rsa_key(
199             IntegerAsn1::from(encoded[165..422].to_vec()),
200             BigInt::from(65537).to_signed_bytes_be().into(),
201         );
202         check_serde!(subject_public_key_info: SubjectPublicKeyInfo in encoded[133..427]);
203 
204         // Extensions
205 
206         let mut key_usage = KeyUsage::new(7);
207         key_usage.set_digital_signature(true);
208         key_usage.set_key_encipherment(true);
209 
210         let extensions = Extensions(vec![
211             Extension::new_basic_constraints(None, None).into_non_critical(),
212             Extension::new_key_usage(key_usage),
213             Extension::new_subject_key_identifier(&encoded[469..489]),
214             Extension::new_authority_key_identifier(KeyIdentifier::from(encoded[502..522].to_vec()), None, None),
215         ]);
216         check_serde!(extensions: Extensions in encoded[429..522]);
217 
218         // SignatureAlgorithm
219 
220         let signature_algorithm = AlgorithmIdentifier::new_sha256_with_rsa_encryption();
221         check_serde!(signature_algorithm: AlgorithmIdentifier in encoded[522..537]);
222 
223         // TBSCertificate
224 
225         let tbs_certificate = TBSCertificate {
226             version: ApplicationTag0(Version::V3).into(),
227             serial_number: BigInt::from(935548868).to_signed_bytes_be().into(),
228             signature: signature_algorithm.clone(),
229             issuer,
230             validity,
231             subject,
232             subject_public_key_info,
233             extensions: extensions.into(),
234         };
235         check_serde!(tbs_certificate: TBSCertificate in encoded[4..522]);
236 
237         // Full certificate
238 
239         let certificate = Certificate {
240             tbs_certificate,
241             signature_algorithm,
242             signature_value: BitString::with_bytes(&encoded[542..1054]).into(),
243         };
244         check_serde!(certificate: Certificate in encoded);
245     }
246 
247     #[test]
key_id()248     fn key_id() {
249         let encoded = base64::decode(
250             "MIIDPzCCAiegAwIBAgIBATANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\
251                 MA8GA1UECgwIUG9sYXJTU0wxGTAXBgNVBAMMEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\
252                 MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G\
253                 A1UECgwIUG9sYXJTU0wxGjAYBgNVBAMMEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN\
254                 BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/\
255                 uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD\
256                 d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf\
257                 CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr\
258                 lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w\
259                 bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB\
260                 o00wSzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQfdNY/KcF0dEU7BRIsPai9Q1kCpjAf\
261                 BgNVHSMEGDAWgBS0WuSls97SUva51aaVD+s+vMf9/zANBgkqhkiG9w0BAQUFAAOC\
262                 AQEAm9GKWy4Z6eS483GoR5omwx32meCStm/vFuW+nozRwqwTG5d2Etx4TPnz73s8\
263                 fMtM1QB0QbfBDDHxfGymEsKwICmCkJszKE7c03j3mkddrrvN2eIYiL6358S3yHMj\
264                 iLVCraRUoEm01k7iytjxrcKb//hxFvHoxD1tdMqbuvjMlTS86kJSrkUMDw68UzfL\
265                 jvo3oVjiexfasjsICXFNoncjthKtS7v4zrsgXNPz92h58NgXnDtQU+Eb9tVA9kUs\
266                 Ln/az3v5DdgrNoAO60zK1zYAmekLil7pgba/jBLPeAQ2fZVgFxttKv33nUnUBzKA\
267                 Od8i323fM5dQS1qQpBjBc/5fPw==",
268         )
269         .expect("invalid base64");
270 
271         let cert: Certificate = picky_asn1_der::from_bytes(&encoded).expect("intermediate cert");
272 
273         pretty_assertions::assert_eq!(
274             hex::encode(&cert.subject_key_identifier().unwrap()),
275             "1f74d63f29c17474453b05122c3da8bd435902a6"
276         );
277         pretty_assertions::assert_eq!(
278             hex::encode(&cert.authority_key_identifier().unwrap().key_identifier().unwrap()),
279             "b45ae4a5b3ded252f6b9d5a6950feb3ebcc7fdff"
280         );
281     }
282 }
283