print_cert/
print-cert.rs

1use der_parser::der::Tag;
2use der_parser::oid::Oid;
3use nom::HexDisplay;
4use std::cmp::min;
5use std::convert::TryFrom;
6use std::env;
7use std::io;
8use std::net::{Ipv4Addr, Ipv6Addr};
9use x509_parser::prelude::*;
10use x509_parser::public_key::PublicKey;
11use x509_parser::signature_algorithm::SignatureAlgorithm;
12
13const PARSE_ERRORS_FATAL: bool = false;
14#[cfg(feature = "validate")]
15const VALIDATE_ERRORS_FATAL: bool = false;
16
17fn print_hex_dump(bytes: &[u8], max_len: usize) {
18    let m = min(bytes.len(), max_len);
19    print!("{}", &bytes[..m].to_hex(16));
20    if bytes.len() > max_len {
21        println!("... <continued>");
22    }
23}
24
25fn format_oid(oid: &Oid) -> String {
26    match oid2sn(oid, oid_registry()) {
27        Ok(s) => s.to_owned(),
28        _ => format!("{oid}"),
29    }
30}
31
32fn generalname_to_string(gn: &GeneralName) -> String {
33    match gn {
34        GeneralName::DNSName(name) => format!("DNSName:{name}"),
35        GeneralName::DirectoryName(n) => format!("DirName:{n}"),
36        GeneralName::EDIPartyName(obj) => format!("EDIPartyName:{obj:?}"),
37        GeneralName::IPAddress(n) => format!("IPAddress:{n:?}"),
38        GeneralName::OtherName(oid, n) => format!("OtherName:{oid}, {n:?}"),
39        GeneralName::RFC822Name(n) => format!("RFC822Name:{n}"),
40        GeneralName::RegisteredID(oid) => format!("RegisteredID:{oid}"),
41        GeneralName::URI(n) => format!("URI:{n}"),
42        GeneralName::X400Address(obj) => format!("X400Address:{obj:?}"),
43        GeneralName::Invalid(tag, b) => format!("Invalid:tag={tag},data={b:?}"),
44    }
45}
46
47fn print_x509_extension(oid: &Oid, ext: &X509Extension) {
48    println!(
49        "    [crit:{} l:{}] {}: ",
50        ext.critical,
51        ext.value.len(),
52        format_oid(oid)
53    );
54    match ext.parsed_extension() {
55        ParsedExtension::AuthorityKeyIdentifier(aki) => {
56            println!("      X509v3 Authority Key Identifier");
57            if let Some(key_id) = &aki.key_identifier {
58                println!("        Key Identifier: {key_id:x}");
59            }
60            if let Some(issuer) = &aki.authority_cert_issuer {
61                for name in issuer {
62                    println!("        Cert Issuer: {name}");
63                }
64            }
65            if let Some(serial) = aki.authority_cert_serial {
66                println!("        Cert Serial: {}", format_serial(serial));
67            }
68        }
69        ParsedExtension::BasicConstraints(bc) => {
70            println!("      X509v3 CA: {}", bc.ca);
71        }
72        ParsedExtension::CRLDistributionPoints(points) => {
73            println!("      X509v3 CRL Distribution Points:");
74            for point in points.iter() {
75                if let Some(name) = &point.distribution_point {
76                    println!("        Full Name: {name:?}");
77                }
78                if let Some(reasons) = &point.reasons {
79                    println!("        Reasons: {reasons}");
80                }
81                if let Some(crl_issuer) = &point.crl_issuer {
82                    print!("        CRL Issuer: ");
83                    for gn in crl_issuer {
84                        print!("{} ", generalname_to_string(gn));
85                    }
86                    println!();
87                }
88                println!();
89            }
90        }
91        ParsedExtension::KeyUsage(ku) => {
92            println!("      X509v3 Key Usage: {ku}");
93        }
94        ParsedExtension::NSCertType(ty) => {
95            println!("      Netscape Cert Type: {ty}");
96        }
97        ParsedExtension::SubjectAlternativeName(san) => {
98            for name in &san.general_names {
99                let s = match name {
100                    GeneralName::DNSName(s) => {
101                        format!("DNS:{s}")
102                    }
103                    GeneralName::IPAddress(b) => {
104                        let ip = match b.len() {
105                            4 => {
106                                let b = <[u8; 4]>::try_from(*b).unwrap();
107                                let ip = Ipv4Addr::from(b);
108                                format!("{ip}")
109                            }
110                            16 => {
111                                let b = <[u8; 16]>::try_from(*b).unwrap();
112                                let ip = Ipv6Addr::from(b);
113                                format!("{ip}")
114                            }
115                            l => format!("invalid (len={l})"),
116                        };
117                        format!("IP Address:{ip}")
118                    }
119                    _ => {
120                        format!("{name:?}")
121                    }
122                };
123                println!("      X509v3 SAN: {s}");
124            }
125        }
126        ParsedExtension::SubjectKeyIdentifier(id) => {
127            println!("      X509v3 Subject Key Identifier: {id:x}");
128        }
129        x => println!("      {x:?}"),
130    }
131}
132
133fn print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize) {
134    println!(
135        "{:indent$}Oid: {}",
136        "",
137        format_oid(&alg.algorithm),
138        indent = level
139    );
140    if let Some(parameter) = &alg.parameters {
141        let s = match parameter.tag() {
142            Tag::Oid => {
143                let oid = parameter.as_oid().unwrap();
144                format_oid(&oid)
145            }
146            _ => format!("{}", parameter.tag()),
147        };
148        println!("{:indent$}Parameter: <PRESENT> {}", "", s, indent = level);
149        let bytes = parameter.as_bytes();
150        print_hex_dump(bytes, 32);
151    } else {
152        println!("{:indent$}Parameter: <ABSENT>", "", indent = level);
153    }
154}
155
156fn print_x509_info(x509: &X509Certificate) -> io::Result<()> {
157    let version = x509.version();
158    if version.0 < 3 {
159        println!("  Version: {version}");
160    } else {
161        println!("  Version: INVALID({})", version.0);
162    }
163    println!("  Serial: {}", x509.tbs_certificate.raw_serial_as_string());
164    println!("  Subject: {}", x509.subject());
165    println!("  Issuer: {}", x509.issuer());
166    println!("  Validity:");
167    println!("    NotBefore: {}", x509.validity().not_before);
168    println!("    NotAfter:  {}", x509.validity().not_after);
169    println!("    is_valid:  {}", x509.validity().is_valid());
170    println!("  Subject Public Key Info:");
171    print_x509_ski(x509.public_key());
172    print_x509_signature_algorithm(&x509.signature_algorithm, 4);
173
174    println!("  Signature Value:");
175    for l in format_number_to_hex_with_colon(&x509.signature_value.data, 16) {
176        println!("      {l}");
177    }
178    println!("  Extensions:");
179    for ext in x509.extensions() {
180        print_x509_extension(&ext.oid, ext);
181    }
182    println!();
183    print!("Structure validation status: ");
184    #[cfg(feature = "validate")]
185    {
186        let mut logger = VecLogger::default();
187        // structure validation status
188        let ok = X509StructureValidator
189            .chain(X509CertificateValidator)
190            .validate(x509, &mut logger);
191        if ok {
192            println!("Ok");
193        } else {
194            println!("FAIL");
195        }
196        for warning in logger.warnings() {
197            println!("  [W] {warning}");
198        }
199        for error in logger.errors() {
200            println!("  [E] {error}");
201        }
202        println!();
203        if VALIDATE_ERRORS_FATAL && !logger.errors().is_empty() {
204            return Err(io::Error::new(io::ErrorKind::Other, "validation failed"));
205        }
206    }
207    #[cfg(not(feature = "validate"))]
208    {
209        println!("Unknown (feature 'validate' not enabled)");
210    }
211    #[cfg(feature = "verify")]
212    {
213        print!("Signature verification: ");
214        if x509.subject() == x509.issuer() {
215            if x509.verify_signature(None).is_ok() {
216                println!("OK");
217                println!("  [I] certificate is self-signed");
218            } else if x509.subject() == x509.issuer() {
219                println!("FAIL");
220                println!("  [W] certificate looks self-signed, but signature verification failed");
221            }
222        } else {
223            // if subject is different from issuer, we cannot verify certificate without the public key of the issuer
224            println!("N/A");
225        }
226    }
227    Ok(())
228}
229
230fn print_x509_signature_algorithm(signature_algorithm: &AlgorithmIdentifier, indent: usize) {
231    match SignatureAlgorithm::try_from(signature_algorithm) {
232        Ok(sig_alg) => {
233            print!("  Signature Algorithm: ");
234            match sig_alg {
235                SignatureAlgorithm::DSA => println!("DSA"),
236                SignatureAlgorithm::ECDSA => println!("ECDSA"),
237                SignatureAlgorithm::ED25519 => println!("ED25519"),
238                SignatureAlgorithm::RSA => println!("RSA"),
239                SignatureAlgorithm::RSASSA_PSS(params) => {
240                    println!("RSASSA-PSS");
241                    let indent_s = format!("{:indent$}", "", indent = indent + 2);
242                    println!(
243                        "{}Hash Algorithm: {}",
244                        indent_s,
245                        format_oid(params.hash_algorithm_oid()),
246                    );
247                    print!("{indent_s}Mask Generation Function: ");
248                    if let Ok(mask_gen) = params.mask_gen_algorithm() {
249                        println!(
250                            "{}/{}",
251                            format_oid(&mask_gen.mgf),
252                            format_oid(&mask_gen.hash),
253                        );
254                    } else {
255                        println!("INVALID");
256                    }
257                    println!("{}Salt Length: {}", indent_s, params.salt_length());
258                }
259                SignatureAlgorithm::RSAAES_OAEP(params) => {
260                    println!("RSAAES-OAEP");
261                    let indent_s = format!("{:indent$}", "", indent = indent + 2);
262                    println!(
263                        "{}Hash Algorithm: {}",
264                        indent_s,
265                        format_oid(params.hash_algorithm_oid()),
266                    );
267                    print!("{indent_s}Mask Generation Function: ");
268                    if let Ok(mask_gen) = params.mask_gen_algorithm() {
269                        println!(
270                            "{}/{}",
271                            format_oid(&mask_gen.mgf),
272                            format_oid(&mask_gen.hash),
273                        );
274                    } else {
275                        println!("INVALID");
276                    }
277                    println!(
278                        "{}pSourceFunc: {}",
279                        indent_s,
280                        format_oid(&params.p_source_alg().algorithm),
281                    );
282                }
283            }
284        }
285        Err(e) => {
286            eprintln!("Could not parse signature algorithm: {e}");
287            println!("  Signature Algorithm:");
288            print_x509_digest_algorithm(signature_algorithm, indent);
289        }
290    }
291}
292
293fn print_x509_ski(public_key: &SubjectPublicKeyInfo) {
294    println!("    Public Key Algorithm:");
295    print_x509_digest_algorithm(&public_key.algorithm, 6);
296    match public_key.parsed() {
297        Ok(PublicKey::RSA(rsa)) => {
298            println!("    RSA Public Key: ({} bit)", rsa.key_size());
299            // print_hex_dump(rsa.modulus, 1024);
300            for l in format_number_to_hex_with_colon(rsa.modulus, 16) {
301                println!("        {l}");
302            }
303            if let Ok(e) = rsa.try_exponent() {
304                println!("    exponent: 0x{e:x} ({e})");
305            } else {
306                println!("    exponent: <INVALID>:");
307                print_hex_dump(rsa.exponent, 32);
308            }
309        }
310        Ok(PublicKey::EC(ec)) => {
311            println!("    EC Public Key: ({} bit)", ec.key_size());
312            for l in format_number_to_hex_with_colon(ec.data(), 16) {
313                println!("        {l}");
314            }
315            // // identify curve
316            // if let Some(params) = &public_key.algorithm.parameters {
317            //     let curve_oid = params.as_oid();
318            //     let curve = curve_oid
319            //         .map(|oid| {
320            //             oid_registry()
321            //                 .get(oid)
322            //                 .map(|entry| entry.sn())
323            //                 .unwrap_or("<UNKNOWN>")
324            //         })
325            //         .unwrap_or("<ERROR: NOT AN OID>");
326            //     println!("    Curve: {}", curve);
327            // }
328        }
329        Ok(PublicKey::DSA(y)) => {
330            println!("    DSA Public Key: ({} bit)", 8 * y.len());
331            for l in format_number_to_hex_with_colon(y, 16) {
332                println!("        {l}");
333            }
334        }
335        Ok(PublicKey::GostR3410(y)) => {
336            println!("    GOST R 34.10-94 Public Key: ({} bit)", 8 * y.len());
337            for l in format_number_to_hex_with_colon(y, 16) {
338                println!("        {l}");
339            }
340        }
341        Ok(PublicKey::GostR3410_2012(y)) => {
342            println!("    GOST R 34.10-2012 Public Key: ({} bit)", 8 * y.len());
343            for l in format_number_to_hex_with_colon(y, 16) {
344                println!("        {l}");
345            }
346        }
347        Ok(PublicKey::Unknown(b)) => {
348            println!("    Unknown key type");
349            print_hex_dump(b, 256);
350            if let Ok((rem, res)) = der_parser::parse_der(b) {
351                eprintln!("rem: {} bytes", rem.len());
352                eprintln!("{res:?}");
353            } else {
354                eprintln!("      <Could not parse key as DER>");
355            }
356        }
357        Err(_) => {
358            println!("    INVALID PUBLIC KEY");
359        }
360    }
361    // dbg!(&public_key);
362    // todo!();
363}
364
365fn format_number_to_hex_with_colon(b: &[u8], row_size: usize) -> Vec<String> {
366    let mut v = Vec::with_capacity(1 + b.len() / row_size);
367    for r in b.chunks(row_size) {
368        let s = r.iter().fold(String::with_capacity(3 * r.len()), |a, b| {
369            a + &format!("{b:02x}:")
370        });
371        v.push(s)
372    }
373    v
374}
375
376fn handle_certificate(file_name: &str, data: &[u8]) -> io::Result<()> {
377    match parse_x509_certificate(data) {
378        Ok((_, x509)) => {
379            print_x509_info(&x509)?;
380            Ok(())
381        }
382        Err(e) => {
383            let s = format!("Error while parsing {file_name}: {e}");
384            if PARSE_ERRORS_FATAL {
385                Err(io::Error::new(io::ErrorKind::Other, s))
386            } else {
387                eprintln!("{s}");
388                Ok(())
389            }
390        }
391    }
392}
393
394pub fn main() -> io::Result<()> {
395    for file_name in env::args().skip(1) {
396        println!("File: {file_name}");
397        let data = std::fs::read(file_name.clone()).expect("Unable to read file");
398        if matches!((data[0], data[1]), (0x30, 0x81..=0x83)) {
399            // probably DER
400            handle_certificate(&file_name, &data)?;
401        } else {
402            // try as PEM
403            for (n, pem) in Pem::iter_from_buffer(&data).enumerate() {
404                match pem {
405                    Ok(pem) => {
406                        let data = &pem.contents;
407                        println!("Certificate [{n}]");
408                        handle_certificate(&file_name, data)?;
409                    }
410                    Err(e) => {
411                        eprintln!("Error while decoding PEM entry {n}: {e}");
412                    }
413                }
414            }
415        }
416    }
417    Ok(())
418}