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= {:?}",
371 "",
372 oid,
373 indent = INDENT_TWO
374 )?
375 }
376 }
377 }
378
379 write!(f, "{:indent$}Signature Algorithm: ", "", indent = INDENT)?;
380 match &self.cert.signature_algorithm.oid {
381 &const_oid::db::rfc5912::ECDSA_WITH_SHA_256 => writeln!(f, "ecdsa-with-sha256")?,
382 &const_oid::db::rfc5912::ECDSA_WITH_SHA_384 => writeln!(f, "ecdsa-with-sha384")?,
383 &const_oid::db::rfc5912::ECDSA_WITH_SHA_512 => writeln!(f, "ecdsa-with-sha512")?,
384 &const_oid::db::rfc5912::SHA_256_WITH_RSA_ENCRYPTION => {
385 writeln!(f, "sha256-with-rsa-encryption")?
386 }
387 oid => writeln!(f, "OID= {:?}", oid)?,
388 }
389
390 writeln!(f, "{:indent$}Signature:", "", indent = INDENT)?;
391 if let Some(bytes) = self.cert.signature.as_bytes() {
392 writeln!(
393 f,
394 "{}",
395 BytesDisplay {
396 bytes,
397 indent: INDENT_TWO
398 }
399 )?;
400 } else {
401 writeln!(f, "{:indent$}INVALID", "", indent = INDENT_TWO)?;
402 }
403
404 Ok(())
405 }
406}
407
408struct BytesDisplay<'a> {
409 bytes: &'a [u8],
410 indent: usize,
411}
412
413impl fmt::Display for BytesDisplay<'_> {
414 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415 write!(f, "{:indent$}", "", indent = self.indent)?;
416
417 let mut iter = self.bytes.iter().peekable();
418
419 let mut count = 0;
420 while let Some(byte) = iter.next() {
421 write!(f, "{:02x}", byte)?;
422
423 count += 1;
424
425 if count % 18 == 0 {
426 write!(f, "\n{:indent$}", "", indent = self.indent)?;
427 } else if iter.peek().is_some() {
428 write!(f, ":")?;
429 }
430 }
431
432 Ok(())
433 }
434}
435
436struct GeneralNameDisplay<'a> {
437 name: &'a GeneralName,
438 indent: usize,
439}
440
441impl fmt::Display for GeneralNameDisplay<'_> {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 write!(f, "{:indent$}", "", indent = self.indent)?;
444 match self.name {
445 GeneralName::OtherName(_othername) => {
446 write!(f, "Other: TODO")
447 }
448 GeneralName::Rfc822Name(ia5str) => {
449 write!(f, "Email: {ia5str}")
450 }
451 GeneralName::DnsName(ia5str) => {
452 write!(f, "DNS: {ia5str}")
453 }
454 GeneralName::DirectoryName(name) => {
455 write!(f, "DN: {name}")
456 }
457 GeneralName::EdiPartyName(_edi_party_name) => {
458 write!(f, "EdiParty: TODO")
459 }
460 GeneralName::UniformResourceIdentifier(ia5str) => {
461 write!(f, "URI: {ia5str}")
462 }
463 GeneralName::IpAddress(_octet_str) => {
464 write!(f, "IpAddress: TODO")
465 }
466 GeneralName::RegisteredId(oid) => {
467 write!(f, "OID: {oid}")
468 }
469 }
470 }
471}
472
473pub struct SubjectPublicKeyDisplay<'a> {
474 spki: SubjectPublicKeyInfoRef<'a>,
475 indent: usize,
476}
477
478impl<'a> From<SubjectPublicKeyInfoRef<'a>> for SubjectPublicKeyDisplay<'a> {
479 fn from(spki: SubjectPublicKeyInfoRef<'a>) -> Self {
480 Self { spki, indent: 0 }
481 }
482}
483
484impl fmt::Display for SubjectPublicKeyDisplay<'_> {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486 writeln!(
487 f,
488 "{:indent$}Subject Public Key Info:",
489 "",
490 indent = self.indent
491 )?;
492
493 let indent = self.indent + INDENT;
494
495 write!(f, "{:indent$}Algorithm: ", "", indent = indent)?;
496
497 let alg_oid = &self.spki.algorithm.oid;
498
499 match alg_oid {
500 &const_oid::db::rfc5912::ID_EC_PUBLIC_KEY => writeln!(f, "id-ec-public-key")?,
501 oid => writeln!(f, "OID= {:?}", oid)?,
502 }
503
504 if let Ok(param_oid) = self.spki.algorithm.parameters_oid() {
505 write!(f, "{:indent$}Parameters: ", "", indent = indent)?;
506 match param_oid {
507 const_oid::db::rfc5912::SECP_384_R_1 => writeln!(f, "SEC P384 R1")?,
508 const_oid::db::rfc5912::SECP_256_R_1 => writeln!(f, "SEC P256 R1")?,
509 oid => writeln!(f, "OID= {:?}", oid)?,
510 }
511 }
512
513 writeln!(f, "{:indent$}Public Key:", "", indent = indent)?;
514 let indent = self.indent + INDENT_TWO;
515
516 if let Some(sk_bytes) = self.spki.subject_public_key.as_bytes() {
517 write!(
518 f,
519 "{}",
520 BytesDisplay {
521 bytes: sk_bytes,
522 indent,
523 }
524 )?;
525 } else {
526 write!(f, "{:indent$}INVALID", "", indent = indent)?;
527 };
528
529 Ok(())
530 }
531}