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