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