• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use der_parser::oid::Oid;
2 use nom::HexDisplay;
3 use std::cmp::min;
4 use std::env;
5 use std::io;
6 use x509_parser::prelude::*;
7 #[cfg(feature = "validate")]
8 use x509_parser::validate::Validate;
9 
10 const PARSE_ERRORS_FATAL: bool = false;
11 #[cfg(feature = "validate")]
12 const VALIDATE_ERRORS_FATAL: bool = false;
13 
print_hex_dump(bytes: &[u8], max_len: usize)14 fn print_hex_dump(bytes: &[u8], max_len: usize) {
15     let m = min(bytes.len(), max_len);
16     print!("{}", &bytes[..m].to_hex(16));
17     if bytes.len() > max_len {
18         println!("... <continued>");
19     }
20 }
21 
format_oid(oid: &Oid) -> String22 fn format_oid(oid: &Oid) -> String {
23     match oid2sn(oid, oid_registry()) {
24         Ok(s) => s.to_owned(),
25         _ => format!("{}", oid),
26     }
27 }
28 
generalname_to_string(gn: &GeneralName) -> String29 fn generalname_to_string(gn: &GeneralName) -> String {
30     match gn {
31         GeneralName::DNSName(name) => format!("DNSName:{}", name),
32         GeneralName::DirectoryName(n) => format!("DirName:{}", n),
33         GeneralName::EDIPartyName(obj) => format!("EDIPartyName:{:?}", obj),
34         GeneralName::IPAddress(n) => format!("IPAddress:{:?}", n),
35         GeneralName::OtherName(oid, n) => format!("OtherName:{}, {:?}", oid, n),
36         GeneralName::RFC822Name(n) => format!("RFC822Name:{}", n),
37         GeneralName::RegisteredID(oid) => format!("RegisteredID:{}", oid),
38         GeneralName::URI(n) => format!("URI:{}", n),
39         GeneralName::X400Address(obj) => format!("X400Address:{:?}", obj),
40     }
41 }
42 
print_x509_extension(oid: &Oid, ext: &X509Extension)43 fn print_x509_extension(oid: &Oid, ext: &X509Extension) {
44     print!("    {}: ", format_oid(oid));
45     print!(" Critical={}", ext.critical);
46     print!(" len={}", ext.value.len());
47     println!();
48     match ext.parsed_extension() {
49         ParsedExtension::BasicConstraints(bc) => {
50             println!("      X509v3 CA: {}", bc.ca);
51         }
52         ParsedExtension::CRLDistributionPoints(points) => {
53             println!("      X509v3 CRL Distribution Points:");
54             for point in points {
55                 if let Some(name) = &point.distribution_point {
56                     println!("        Full Name: {:?}", name);
57                 }
58                 if let Some(reasons) = &point.reasons {
59                     println!("        Reasons: {}", reasons);
60                 }
61                 if let Some(crl_issuer) = &point.crl_issuer {
62                     print!("        CRL Issuer: ");
63                     for gn in crl_issuer {
64                         print!("{} ", generalname_to_string(gn));
65                     }
66                     println!();
67                 }
68                 println!();
69             }
70         }
71         ParsedExtension::KeyUsage(ku) => {
72             println!("      X509v3 Key Usage: {}", ku);
73         }
74         ParsedExtension::NSCertType(ty) => {
75             println!("      Netscape Cert Type: {}", ty);
76         }
77         ParsedExtension::SubjectAlternativeName(san) => {
78             for name in &san.general_names {
79                 println!("      X509v3 SAN: {:?}", name);
80             }
81         }
82         ParsedExtension::SubjectKeyIdentifier(id) => {
83             let mut s =
84                 id.0.iter()
85                     .fold(String::with_capacity(3 * id.0.len()), |a, b| {
86                         a + &format!("{:02x}:", b)
87                     });
88             s.pop();
89             println!("      X509v3 Subject Key Identifier: {}", &s);
90         }
91         x => println!("      {:?}", x),
92     }
93 }
94 
print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize)95 fn print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize) {
96     println!(
97         "{:indent$}Oid: {}",
98         "",
99         format_oid(&alg.algorithm),
100         indent = level
101     );
102     if let Some(parameter) = &alg.parameters {
103         println!(
104             "{:indent$}Parameter: <PRESENT> {:?}",
105             "",
106             parameter.header.tag,
107             indent = level
108         );
109         if let Ok(bytes) = parameter.as_slice() {
110             print_hex_dump(bytes, 32);
111         }
112     } else {
113         println!("{:indent$}Parameter: <ABSENT>", "", indent = level);
114     }
115 }
116 
print_x509_info(x509: &X509Certificate) -> io::Result<()>117 fn print_x509_info(x509: &X509Certificate) -> io::Result<()> {
118     println!("  Subject: {}", x509.subject());
119     println!("  Signature Algorithm:");
120     print_x509_digest_algorithm(&x509.signature_algorithm, 4);
121     println!("  Issuer: {}", x509.issuer());
122     println!("  Serial: {}", x509.tbs_certificate.raw_serial_as_string());
123     println!("  Validity:");
124     println!("    NotBefore: {}", x509.validity().not_before.to_rfc2822());
125     println!("    NotAfter:  {}", x509.validity().not_after.to_rfc2822());
126     println!("    is_valid:  {}", x509.validity().is_valid());
127     println!("  Extensions:");
128     for ext in x509.extensions() {
129         print_x509_extension(&ext.oid, ext);
130     }
131     println!();
132     #[cfg(feature = "validate")]
133     {
134         // structure validation status
135         let (ok, warnings, errors) = x509.validate_to_vec();
136         print!("Structure validation status: ");
137         if ok {
138             println!("Ok");
139         } else {
140             println!("FAIL");
141         }
142         for warning in &warnings {
143             println!("  [W] {}", warning);
144         }
145         for error in &errors {
146             println!("  [E] {}", error);
147         }
148         println!();
149         if VALIDATE_ERRORS_FATAL && !errors.is_empty() {
150             return Err(io::Error::new(io::ErrorKind::Other, "validation failed"));
151         }
152     }
153     Ok(())
154 }
155 
handle_certificate(file_name: &str, data: &[u8]) -> io::Result<()>156 fn handle_certificate(file_name: &str, data: &[u8]) -> io::Result<()> {
157     match parse_x509_certificate(data) {
158         Ok((_, x509)) => {
159             print_x509_info(&x509)?;
160             Ok(())
161         }
162         Err(e) => {
163             let s = format!("Error while parsing {}: {}", file_name, e);
164             if PARSE_ERRORS_FATAL {
165                 Err(io::Error::new(io::ErrorKind::Other, s))
166             } else {
167                 eprintln!("{}", s);
168                 Ok(())
169             }
170         }
171     }
172 }
173 
main() -> io::Result<()>174 pub fn main() -> io::Result<()> {
175     for file_name in env::args().skip(1) {
176         println!("File: {}", file_name);
177         let data = std::fs::read(file_name.clone()).expect("Unable to read file");
178         if matches!((data[0], data[1]), (0x30, 0x81..=0x83)) {
179             // probably DER
180             handle_certificate(&file_name, &data)?;
181         } else {
182             // try as PEM
183             for (n, pem) in Pem::iter_from_buffer(&data).enumerate() {
184                 let pem = pem.expect("Could not decode the PEM file");
185                 let data = &pem.contents;
186                 println!("Certificate [{}]", n);
187                 handle_certificate(&file_name, data)?;
188             }
189         }
190     }
191     Ok(())
192 }
193