1use crate::x509::Certificate;
2use const_oid::AssociatedOid;
3use std::fmt;
4use x509_cert::der::referenced::OwnedToRef;
5use x509_cert::der::Decode;
6use x509_cert::ext::pkix::name::GeneralName;
7use x509_cert::ext::pkix::AuthorityKeyIdentifier;
8use x509_cert::ext::pkix::BasicConstraints;
9use x509_cert::ext::pkix::ExtendedKeyUsage;
10use x509_cert::ext::pkix::KeyUsage;
11use x509_cert::ext::pkix::SubjectAltName;
12use x509_cert::ext::pkix::SubjectKeyIdentifier;
13use x509_cert::spki::SubjectPublicKeyInfoRef;
14
15const INDENT: usize = 2;
16const INDENT_TWO: usize = INDENT * 2;
17const INDENT_THREE: usize = INDENT * 3;
18const INDENT_FOUR: usize = INDENT * 4;
19
20pub struct X509Display<'a> {
21 cert: &'a Certificate,
22}
23
24impl<'a> From<&'a Certificate> for X509Display<'a> {
25 fn from(cert: &'a Certificate) -> Self {
26 X509Display { cert }
27 }
28}
29
30impl fmt::Display for X509Display<'_> {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 writeln!(f, "--")?;
33 writeln!(f, "Certificate:")?;
34
35 writeln!(f, "{:indent$}Data:", "", indent = INDENT)?;
37
38 writeln!(
39 f,
40 "{:indent$}Version: {:?}",
41 "",
42 self.cert.tbs_certificate.version,
43 indent = INDENT_TWO,
44 )?;
45
46 writeln!(f, "{:indent$}Serial:", "", indent = INDENT_TWO)?;
47 writeln!(
48 f,
49 "{}",
50 BytesDisplay {
51 bytes: self.cert.tbs_certificate.serial_number.as_bytes(),
52 indent: INDENT_THREE
53 }
54 )?;
55
56 writeln!(
57 f,
58 "{:indent$}Subject: {}",
59 "",
60 self.cert.tbs_certificate.subject,
61 indent = INDENT_TWO
62 )?;
63
64 writeln!(
65 f,
66 "{:indent$}Issuer: {}",
67 "",
68 self.cert.tbs_certificate.issuer,
69 indent = INDENT_TWO
70 )?;
71
72 if let Some(issuer_unique_id) = &self.cert.tbs_certificate.issuer_unique_id {
73 writeln!(f, "{:indent$}Issuer Unique ID:", "", indent = INDENT_TWO)?;
74 if let Some(bytes) = issuer_unique_id.as_bytes() {
75 writeln!(
76 f,
77 "{}",
78 BytesDisplay {
79 bytes,
80 indent: INDENT_THREE
81 }
82 )?;
83 } else {
84 writeln!(f, "{:indent$}INVALID", "", indent = INDENT_THREE)?;
85 }
86 }
87
88 if let Some(subject_unique_id) = &self.cert.tbs_certificate.subject_unique_id {
89 writeln!(f, "{:indent$}Subject Unique ID:", "", indent = INDENT_TWO)?;
90 if let Some(bytes) = subject_unique_id.as_bytes() {
91 writeln!(
92 f,
93 "{}",
94 BytesDisplay {
95 bytes,
96 indent: INDENT_THREE
97 }
98 )?;
99 } else {
100 writeln!(f, "{:indent$}INVALID", "", indent = INDENT_THREE)?;
101 }
102 }
103
104 writeln!(f, "{:indent$}Validity", "", indent = INDENT_TWO)?;
105 writeln!(
106 f,
107 "{:indent$}Not Before: {}",
108 "",
109 self.cert.tbs_certificate.validity.not_before,
110 indent = INDENT_THREE
111 )?;
112 writeln!(
113 f,
114 "{:indent$}Not After: {}",
115 "",
116 self.cert.tbs_certificate.validity.not_after,
117 indent = INDENT_THREE
118 )?;
119
120 write!(
121 f,
122 "{:indent$}Signature Algorithm: ",
123 "",
124 indent = INDENT_TWO
125 )?;
126 match &self.cert.tbs_certificate.signature.oid {
127 &const_oid::db::rfc5912::ECDSA_WITH_SHA_256 => writeln!(f, "ecdsa-with-sha256")?,
128 &const_oid::db::rfc5912::ECDSA_WITH_SHA_384 => writeln!(f, "ecdsa-with-sha384")?,
129 &const_oid::db::rfc5912::ECDSA_WITH_SHA_512 => writeln!(f, "ecdsa-with-sha512")?,
130 &const_oid::db::rfc5912::SHA_256_WITH_RSA_ENCRYPTION => {
131 writeln!(f, "sha256-with-rsa-encryption")?
132 }
133 oid => writeln!(f, "OID= {oid:?}")?,
134 }
135
136 writeln!(
138 f,
139 "{}",
140 SubjectPublicKeyDisplay {
141 spki: self
142 .cert
143 .tbs_certificate
144 .subject_public_key_info
145 .owned_to_ref(),
146 indent: INDENT_TWO
147 }
148 )?;
149
150 for extension in self
151 .cert
152 .tbs_certificate
153 .extensions
154 .iter()
155 .flat_map(|exts| exts.iter())
156 {
157 match extension.extn_id {
158 BasicConstraints::OID => {
159 write!(f, "{:indent$}Basic Constraints:", "", indent = INDENT_TWO)?;
160 if let Ok(bc) = BasicConstraints::from_der(extension.extn_value.as_bytes()) {
161 if extension.critical {
162 write!(f, " critical")?;
163 }
164 writeln!(f)?;
165
166 writeln!(f, "{:indent$}CA: {}", "", bc.ca, indent = INDENT_THREE)?;
167 if let Some(path_len_constraint) = bc.path_len_constraint {
168 writeln!(
169 f,
170 "{:indent$}Path Length: {}",
171 "",
172 path_len_constraint,
173 indent = INDENT_THREE
174 )?;
175 }
176 } else {
177 writeln!(f, "INVALID")?
178 }
179 }
180 SubjectKeyIdentifier::OID => {
181 write!(
182 f,
183 "{:indent$}Subject Key Identifier:",
184 "",
185 indent = INDENT_TWO
186 )?;
187 if let Ok(ski) = SubjectKeyIdentifier::from_der(extension.extn_value.as_bytes())
188 {
189 if extension.critical {
190 write!(f, " critical")?;
191 }
192 writeln!(f)?;
193 writeln!(
194 f,
195 "{}",
196 BytesDisplay {
197 bytes: ski.as_ref().as_bytes(),
198 indent: INDENT_THREE
199 }
200 )?;
201 } else {
202 writeln!(f, "INVALID")?
203 }
204 }
205 AuthorityKeyIdentifier::OID => {
206 write!(
207 f,
208 "{:indent$}Authority Key Identifier:",
209 "",
210 indent = INDENT_TWO
211 )?;
212 if let Ok(aki) =
213 AuthorityKeyIdentifier::from_der(extension.extn_value.as_bytes())
214 {
215 if extension.critical {
216 write!(f, " critical")?;
217 }
218 writeln!(f)?;
219
220 if let Some(bytes) = &aki.key_identifier {
221 writeln!(
222 f,
223 "{}",
224 BytesDisplay {
225 bytes: bytes.as_bytes(),
226 indent: INDENT_THREE
227 }
228 )?;
229 }
230
231 if let Some(aki_cert_issuer) = &aki.authority_cert_issuer {
232 for name in aki_cert_issuer.iter() {
233 writeln!(
234 f,
235 "{}",
236 GeneralNameDisplay {
237 name,
238 indent: INDENT_THREE
239 }
240 )?;
241 }
242 }
243
244 if let Some(bytes) = &aki.authority_cert_serial_number {
245 writeln!(
246 f,
247 "{}",
248 BytesDisplay {
249 bytes: bytes.as_bytes(),
250 indent: INDENT_THREE
251 }
252 )?;
253 }
254 } else {
255 writeln!(f, "INVALID")?
256 }
257 }
258 KeyUsage::OID => {
259 write!(f, "{:indent$}Key Usage:", "", indent = INDENT_TWO)?;
260 let ku = if let Ok(ku) = KeyUsage::from_der(extension.extn_value.as_bytes()) {
261 if extension.critical {
262 write!(f, " critical")?;
263 }
264 writeln!(f)?;
265 ku
266 } else {
267 writeln!(f, "INVALID")?;
268 continue;
269 };
270
271 if ku.digital_signature() {
272 writeln!(f, "{:indent$}Digital Signature", "", indent = INDENT_THREE)?;
273 }
274
275 if ku.non_repudiation() {
276 writeln!(f, "{:indent$}Non Repudiation", "", indent = INDENT_THREE)?;
277 }
278
279 if ku.key_encipherment() {
280 writeln!(f, "{:indent$}Key Encipherment", "", indent = INDENT_THREE)?;
281 }
282
283 if ku.data_encipherment() {
284 writeln!(f, "{:indent$}Data Encipherment", "", indent = INDENT_THREE)?;
285 }
286
287 if ku.key_agreement() {
288 writeln!(f, "{:indent$}Key Agreement", "", indent = INDENT_THREE)?;
289 }
290
291 if ku.key_cert_sign() {
292 writeln!(f, "{:indent$}Key Cert Sign", "", indent = INDENT_THREE)?;
293 }
294
295 if ku.crl_sign() {
296 writeln!(f, "{:indent$}CRL Sign", "", indent = INDENT_THREE)?;
297 }
298
299 if ku.encipher_only() {
300 writeln!(f, "{:indent$}Encipher Only", "", indent = INDENT_THREE)?;
301 }
302
303 if ku.decipher_only() {
304 writeln!(f, "{:indent$}Decipher Only", "", indent = INDENT_THREE)?;
305 }
306 }
307
308 ExtendedKeyUsage::OID => {
309 write!(f, "{:indent$}Extended Key Usage:", "", indent = INDENT_TWO)?;
310 let eku = if let Ok(eku) =
311 ExtendedKeyUsage::from_der(extension.extn_value.as_bytes())
312 {
313 if extension.critical {
314 write!(f, " critical")?;
315 }
316 writeln!(f)?;
317 eku
318 } else {
319 writeln!(f, "INVALID")?;
320 continue;
321 };
322
323 for oid in eku.as_ref().iter() {
324 write!(f, "{:indent$}", "", indent = INDENT_THREE)?;
325 match oid {
326 &const_oid::db::rfc5280::ID_KP_SERVER_AUTH => {
327 writeln!(f, "Server Authentication")?
328 }
329 &const_oid::db::rfc5280::ID_KP_CLIENT_AUTH => {
330 writeln!(f, "Client Authentication")?
331 }
332 &const_oid::db::rfc5280::ID_KP_CODE_SIGNING => {
333 writeln!(f, "Code Signing")?
334 }
335 oid => writeln!(f, "OID= {oid:?}")?,
336 }
337 }
338 }
339 SubjectAltName::OID => {
340 write!(f, "{:indent$}Subject Alt Name:", "", indent = INDENT_TWO)?;
341 let san = if let Ok(san) =
342 SubjectAltName::from_der(extension.extn_value.as_bytes())
343 {
344 if extension.critical {
345 write!(f, " critical")?;
346 }
347 writeln!(f)?;
348 san
349 } else {
350 writeln!(f, "INVALID")?;
351 continue;
352 };
353
354 for name in san.0.iter() {
355 writeln!(
356 f,
357 "{}",
358 GeneralNameDisplay {
359 name,
360 indent: INDENT_THREE
361 }
362 )?;
363 }
364 }
365
366 oid => {
367 writeln!(
369 f,
370 "{:indent$}Unknown Extension: OID= {oid:?}",
371 "",
372 indent = INDENT_TWO
373 )?
374 }
375 }
376 }
377
378 write!(f, "{:indent$}Signature Algorithm: ", "", indent = INDENT)?;
379 match &self.cert.signature_algorithm.oid {
380 &const_oid::db::rfc5912::ECDSA_WITH_SHA_256 => writeln!(f, "ecdsa-with-sha256")?,
381 &const_oid::db::rfc5912::ECDSA_WITH_SHA_384 => writeln!(f, "ecdsa-with-sha384")?,
382 &const_oid::db::rfc5912::ECDSA_WITH_SHA_512 => writeln!(f, "ecdsa-with-sha512")?,
383 &const_oid::db::rfc5912::SHA_256_WITH_RSA_ENCRYPTION => {
384 writeln!(f, "sha256-with-rsa-encryption")?
385 }
386 oid => writeln!(f, "OID= {oid:?}")?,
387 }
388
389 writeln!(f, "{:indent$}Signature:", "", indent = INDENT)?;
390 if let Some(bytes) = self.cert.signature.as_bytes() {
391 writeln!(
392 f,
393 "{}",
394 BytesDisplay {
395 bytes,
396 indent: INDENT_TWO
397 }
398 )?;
399 } else {
400 writeln!(f, "{:indent$}INVALID", "", indent = INDENT_TWO)?;
401 }
402
403 Ok(())
404 }
405}
406
407struct BytesDisplay<'a> {
408 bytes: &'a [u8],
409 indent: usize,
410}
411
412impl fmt::Display for BytesDisplay<'_> {
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 write!(f, "{:indent$}", "", indent = self.indent)?;
415
416 let mut iter = self.bytes.iter().peekable();
417
418 let mut count = 0;
419 while let Some(byte) = iter.next() {
420 write!(f, "{byte:02x}")?;
421
422 count += 1;
423
424 if count % 18 == 0 {
425 write!(f, "\n{:indent$}", "", indent = self.indent)?;
426 } else if iter.peek().is_some() {
427 write!(f, ":")?;
428 }
429 }
430
431 Ok(())
432 }
433}
434
435struct GeneralNameDisplay<'a> {
436 name: &'a GeneralName,
437 indent: usize,
438}
439
440impl fmt::Display for GeneralNameDisplay<'_> {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 write!(f, "{:indent$}", "", indent = self.indent)?;
443 match self.name {
444 GeneralName::OtherName(_othername) => {
445 write!(f, "Other: TODO")
446 }
447 GeneralName::Rfc822Name(ia5str) => {
448 write!(f, "Email: {ia5str}")
449 }
450 GeneralName::DnsName(ia5str) => {
451 write!(f, "DNS: {ia5str}")
452 }
453 GeneralName::DirectoryName(name) => {
454 write!(f, "DN: {name}")
455 }
456 GeneralName::EdiPartyName(_edi_party_name) => {
457 write!(f, "EdiParty: TODO")
458 }
459 GeneralName::UniformResourceIdentifier(ia5str) => {
460 write!(f, "URI: {ia5str}")
461 }
462 GeneralName::IpAddress(_octet_str) => {
463 write!(f, "IpAddress: TODO")
464 }
465 GeneralName::RegisteredId(oid) => {
466 write!(f, "OID: {oid}")
467 }
468 }
469 }
470}
471
472pub struct SubjectPublicKeyDisplay<'a> {
473 spki: SubjectPublicKeyInfoRef<'a>,
474 indent: usize,
475}
476
477impl<'a> From<SubjectPublicKeyInfoRef<'a>> for SubjectPublicKeyDisplay<'a> {
478 fn from(spki: SubjectPublicKeyInfoRef<'a>) -> Self {
479 Self { spki, indent: 0 }
480 }
481}
482
483impl fmt::Display for SubjectPublicKeyDisplay<'_> {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 writeln!(
486 f,
487 "{:indent$}Subject Public Key Info:",
488 "",
489 indent = self.indent
490 )?;
491
492 let indent = self.indent + INDENT;
493
494 write!(f, "{:indent$}Algorithm: ", "", indent = indent)?;
495
496 let alg_oid = &self.spki.algorithm.oid;
497
498 match alg_oid {
499 &const_oid::db::rfc5912::ID_EC_PUBLIC_KEY => writeln!(f, "id-ec-public-key")?,
500 oid => writeln!(f, "OID= {oid:?}")?,
501 }
502
503 if let Ok(param_oid) = self.spki.algorithm.parameters_oid() {
504 write!(f, "{:indent$}Parameters: ", "", indent = indent)?;
505 match param_oid {
506 const_oid::db::rfc5912::SECP_384_R_1 => writeln!(f, "SEC P384 R1")?,
507 const_oid::db::rfc5912::SECP_256_R_1 => writeln!(f, "SEC P256 R1")?,
508 oid => writeln!(f, "OID= {oid:?}")?,
509 }
510 }
511
512 writeln!(f, "{:indent$}Public Key:", "", indent = indent)?;
513 let indent = self.indent + INDENT_TWO;
514
515 if let Some(sk_bytes) = self.spki.subject_public_key.as_bytes() {
516 write!(
517 f,
518 "{}",
519 BytesDisplay {
520 bytes: sk_bytes,
521 indent,
522 }
523 )?;
524 } else {
525 write!(f, "{:indent$}INVALID", "", indent = indent)?;
526 };
527
528 Ok(())
529 }
530}