blob: 73a91492a18dbca71e2b8fd98048fc370b205c61 [file] [log] [blame]
use der_parser::oid::Oid;
use nom::HexDisplay;
use std::cmp::min;
use std::env;
use std::io;
use x509_parser::prelude::*;
#[cfg(feature = "validate")]
use x509_parser::validate::Validate;
const PARSE_ERRORS_FATAL: bool = false;
#[cfg(feature = "validate")]
const VALIDATE_ERRORS_FATAL: bool = false;
fn print_hex_dump(bytes: &[u8], max_len: usize) {
let m = min(bytes.len(), max_len);
print!("{}", &bytes[..m].to_hex(16));
if bytes.len() > max_len {
println!("... <continued>");
}
}
fn format_oid(oid: &Oid) -> String {
match oid2sn(oid, oid_registry()) {
Ok(s) => s.to_owned(),
_ => format!("{}", oid),
}
}
fn generalname_to_string(gn: &GeneralName) -> String {
match gn {
GeneralName::DNSName(name) => format!("DNSName:{}", name),
GeneralName::DirectoryName(n) => format!("DirName:{}", n),
GeneralName::EDIPartyName(obj) => format!("EDIPartyName:{:?}", obj),
GeneralName::IPAddress(n) => format!("IPAddress:{:?}", n),
GeneralName::OtherName(oid, n) => format!("OtherName:{}, {:?}", oid, n),
GeneralName::RFC822Name(n) => format!("RFC822Name:{}", n),
GeneralName::RegisteredID(oid) => format!("RegisteredID:{}", oid),
GeneralName::URI(n) => format!("URI:{}", n),
GeneralName::X400Address(obj) => format!("X400Address:{:?}", obj),
}
}
fn print_x509_extension(oid: &Oid, ext: &X509Extension) {
print!(" {}: ", format_oid(oid));
print!(" Critical={}", ext.critical);
print!(" len={}", ext.value.len());
println!();
match ext.parsed_extension() {
ParsedExtension::BasicConstraints(bc) => {
println!(" X509v3 CA: {}", bc.ca);
}
ParsedExtension::CRLDistributionPoints(points) => {
println!(" X509v3 CRL Distribution Points:");
for point in points {
if let Some(name) = &point.distribution_point {
println!(" Full Name: {:?}", name);
}
if let Some(reasons) = &point.reasons {
println!(" Reasons: {}", reasons);
}
if let Some(crl_issuer) = &point.crl_issuer {
print!(" CRL Issuer: ");
for gn in crl_issuer {
print!("{} ", generalname_to_string(gn));
}
println!();
}
println!();
}
}
ParsedExtension::KeyUsage(ku) => {
println!(" X509v3 Key Usage: {}", ku);
}
ParsedExtension::NSCertType(ty) => {
println!(" Netscape Cert Type: {}", ty);
}
ParsedExtension::SubjectAlternativeName(san) => {
for name in &san.general_names {
println!(" X509v3 SAN: {:?}", name);
}
}
ParsedExtension::SubjectKeyIdentifier(id) => {
let mut s =
id.0.iter()
.fold(String::with_capacity(3 * id.0.len()), |a, b| {
a + &format!("{:02x}:", b)
});
s.pop();
println!(" X509v3 Subject Key Identifier: {}", &s);
}
x => println!(" {:?}", x),
}
}
fn print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize) {
println!(
"{:indent$}Oid: {}",
"",
format_oid(&alg.algorithm),
indent = level
);
if let Some(parameter) = &alg.parameters {
println!(
"{:indent$}Parameter: <PRESENT> {:?}",
"",
parameter.header.tag,
indent = level
);
if let Ok(bytes) = parameter.as_slice() {
print_hex_dump(bytes, 32);
}
} else {
println!("{:indent$}Parameter: <ABSENT>", "", indent = level);
}
}
fn print_x509_info(x509: &X509Certificate) -> io::Result<()> {
println!(" Subject: {}", x509.subject());
println!(" Signature Algorithm:");
print_x509_digest_algorithm(&x509.signature_algorithm, 4);
println!(" Issuer: {}", x509.issuer());
println!(" Serial: {}", x509.tbs_certificate.raw_serial_as_string());
println!(" Validity:");
println!(" NotBefore: {}", x509.validity().not_before.to_rfc2822());
println!(" NotAfter: {}", x509.validity().not_after.to_rfc2822());
println!(" is_valid: {}", x509.validity().is_valid());
println!(" Extensions:");
for ext in x509.extensions() {
print_x509_extension(&ext.oid, ext);
}
println!();
#[cfg(feature = "validate")]
{
// structure validation status
let (ok, warnings, errors) = x509.validate_to_vec();
print!("Structure validation status: ");
if ok {
println!("Ok");
} else {
println!("FAIL");
}
for warning in &warnings {
println!(" [W] {}", warning);
}
for error in &errors {
println!(" [E] {}", error);
}
println!();
if VALIDATE_ERRORS_FATAL && !errors.is_empty() {
return Err(io::Error::new(io::ErrorKind::Other, "validation failed"));
}
}
Ok(())
}
fn handle_certificate(file_name: &str, data: &[u8]) -> io::Result<()> {
match parse_x509_certificate(data) {
Ok((_, x509)) => {
print_x509_info(&x509)?;
Ok(())
}
Err(e) => {
let s = format!("Error while parsing {}: {}", file_name, e);
if PARSE_ERRORS_FATAL {
Err(io::Error::new(io::ErrorKind::Other, s))
} else {
eprintln!("{}", s);
Ok(())
}
}
}
}
pub fn main() -> io::Result<()> {
for file_name in env::args().skip(1) {
println!("File: {}", file_name);
let data = std::fs::read(file_name.clone()).expect("Unable to read file");
if matches!((data[0], data[1]), (0x30, 0x81..=0x83)) {
// probably DER
handle_certificate(&file_name, &data)?;
} else {
// try as PEM
for (n, pem) in Pem::iter_from_buffer(&data).enumerate() {
let pem = pem.expect("Could not decode the PEM file");
let data = &pem.contents;
println!("Certificate [{}]", n);
handle_certificate(&file_name, data)?;
}
}
}
Ok(())
}