aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2023-03-28 18:48:21 -0700
committerFranck Cuny <franck@fcuny.net>2023-03-28 18:48:21 -0700
commit51640af5aa964b8eafa28e06e49528588b937a36 (patch)
tree9246a48ee3ce60968c02f0923ab4c9fc6705af09
parentdoc: update README (diff)
downloadx-51640af5aa964b8eafa28e06e49528588b937a36.tar.gz
add a flag to check expired certificate
Add the `--insecure` flag so we can check certificates that are expired. When using the short format for the output (the default), if the certificate has expired, it will report how many days ago. For certificates that have not expired, the remaining number of days will be printed.
-rw-r--r--src/x509-info/Cargo.toml2
-rw-r--r--src/x509-info/README.md29
-rw-r--r--src/x509-info/src/client.rs59
-rw-r--r--src/x509-info/src/main.rs6
-rw-r--r--src/x509-info/src/output.rs34
5 files changed, 94 insertions, 36 deletions
diff --git a/src/x509-info/Cargo.toml b/src/x509-info/Cargo.toml
index 4120200..f6b4e3e 100644
--- a/src/x509-info/Cargo.toml
+++ b/src/x509-info/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
chrono = {version = "0.4.20", features = ["clock"], default-features = false }
clap = {version = "4.0.18", features = ["derive", "cargo"]}
-rustls = "0.20.7"
+rustls = {version = "0.20.7", features = ["dangerous_configuration"]}
rustls-native-certs = "0.6.2"
webpki-roots = "0.22.5"
x509-parser = "0.14"
diff --git a/src/x509-info/README.md b/src/x509-info/README.md
index 61d450e..f7e9121 100644
--- a/src/x509-info/README.md
+++ b/src/x509-info/README.md
@@ -7,8 +7,7 @@ At this point it's pretty clear that I'll never remember the syntax for `openssl
## Usage
``` shell
-> x509-info --help
-Usage: x509-info [OPTIONS] <DOMAIN>
+$ Usage: x509-info [OPTIONS] <DOMAIN>
Arguments:
<DOMAIN>
@@ -20,6 +19,9 @@ Options:
[default: 443]
+ -i, --insecure
+ Accept invalid certificate
+
-f, --format <FORMAT>
[default: short]
@@ -37,29 +39,36 @@ Options:
The default format will print a short message:
``` shell
-> x509-info twitter.com
-twitter.com is valid until Mon, 12 Dec 2022 15:59:59 -0800 (29 days left)
+$ x509-info twitter.com
+twitter.com: Mon, 11 Dec 2023 15:59:59 -0800 (257 days left)
```
It's possible to get more details:
``` shell
-> x509-info --format long twitter.com
+$ x509-info --format=long twitter.com
certificate
version: V3
- serial: 0d:e1:52:69:6b:2f:96:70:d6:c7:db:18:ce:1c:71:a0
+ serial: 0a:2c:01:b8:2b:5d:47:73:9a:5a:01:1a:6f:dc:1a:20
subject: C=US, ST=California, L=San Francisco, O=Twitter, Inc., CN=twitter.com
issuer: C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1
validity
- not before : Sun, 12 Dec 2021 16:00:00 -0800
- not after : Mon, 12 Dec 2022 15:59:59 -0800
- validity days : 364
- remaining days: 29
+ not before : Sat, 10 Dec 2022 16:00:00 -0800
+ not after : Mon, 11 Dec 2023 15:59:59 -0800
+ validity days : 365
+ remaining days: 257
SANs:
DNS:twitter.com
DNS:www.twitter.com
```
+You can also check expired certificates:
+
+``` shell
+$ x509-info --insecure expired.badssl.com
+<no name>: Sun, 12 Apr 2015 16:59:59 -0700 (it expired -2907 days ago)
+```
+
## Notes
Could the same be achieved with a wrapper around `openssl` ? yes.
diff --git a/src/x509-info/src/client.rs b/src/x509-info/src/client.rs
index 11c48db..344e300 100644
--- a/src/x509-info/src/client.rs
+++ b/src/x509-info/src/client.rs
@@ -6,22 +6,53 @@ use std::error::Error;
use std::io::Write;
use std::sync::Arc;
-pub fn get_certificates(domain: String, port: u16) -> Result<Vec<Certificate>, Box<dyn Error>> {
+struct SkipServerVerification;
+
+impl SkipServerVerification {
+ fn new() -> Arc<Self> {
+ Arc::new(Self)
+ }
+}
+impl rustls::client::ServerCertVerifier for SkipServerVerification {
+ fn verify_server_cert(
+ &self,
+ _end_entity: &rustls::Certificate,
+ _intermediates: &[rustls::Certificate],
+ _server_name: &rustls::ServerName,
+ _scts: &mut dyn Iterator<Item = &[u8]>,
+ _ocsp_response: &[u8],
+ _now: std::time::SystemTime,
+ ) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
+ Ok(rustls::client::ServerCertVerified::assertion())
+ }
+}
+
+pub fn get_certificates(
+ domain: String,
+ port: u16,
+ insecure: bool,
+) -> Result<Vec<Certificate>, Box<dyn Error>> {
let mut tcp_stream = std::net::TcpStream::connect(format!("{}:{}", domain, port))?;
- let mut root_store = RootCertStore::empty();
- root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
- OwnedTrustAnchor::from_subject_spki_name_constraints(
- ta.subject,
- ta.spki,
- ta.name_constraints,
- )
- }));
-
- let config = ClientConfig::builder()
- .with_safe_defaults()
- .with_root_certificates(root_store)
- .with_no_client_auth();
+ let config = if insecure {
+ ClientConfig::builder()
+ .with_safe_defaults()
+ .with_custom_certificate_verifier(SkipServerVerification::new())
+ .with_no_client_auth()
+ } else {
+ let mut root_store = RootCertStore::empty();
+ root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
+ OwnedTrustAnchor::from_subject_spki_name_constraints(
+ ta.subject,
+ ta.spki,
+ ta.name_constraints,
+ )
+ }));
+ ClientConfig::builder()
+ .with_safe_defaults()
+ .with_root_certificates(root_store)
+ .with_no_client_auth()
+ };
let server_name = ServerName::try_from(domain.as_ref())?;
diff --git a/src/x509-info/src/main.rs b/src/x509-info/src/main.rs
index d9eed13..c318a3d 100644
--- a/src/x509-info/src/main.rs
+++ b/src/x509-info/src/main.rs
@@ -16,6 +16,10 @@ struct Args {
#[clap(short, long, default_value_t = 443)]
port: u16,
+ /// Accept invalid certificate
+ #[clap(short, long, default_value_t = false)]
+ insecure: bool,
+
#[clap(short, long,value_enum, default_value_t = output::OutputFormat::Short)]
format: output::OutputFormat,
}
@@ -25,7 +29,7 @@ fn main() {
let domain = args.domain;
- let certs = client::get_certificates(domain, args.port);
+ let certs = client::get_certificates(domain, args.port, args.insecure);
match certs {
Ok(certs) => {
diff --git a/src/x509-info/src/output.rs b/src/x509-info/src/output.rs
index 7cfa13e..e38aff1 100644
--- a/src/x509-info/src/output.rs
+++ b/src/x509-info/src/output.rs
@@ -28,16 +28,30 @@ impl OutputFormat {
let not_after = chrono::Local.timestamp(cert.validity().not_after.timestamp(), 0);
let now: DateTime<Local> = Local::now();
let remaining = not_after - now;
- println!(
- "{} is valid until {} ({} days left)",
- cert.subject()
- .iter_common_name()
- .next()
- .and_then(|cn| cn.as_str().ok())
- .unwrap(),
- not_after.to_rfc2822(),
- remaining.num_days(),
- );
+
+ if remaining >= chrono::Duration::zero() {
+ println!(
+ "{}: {} ({} days left)",
+ cert.subject()
+ .iter_common_name()
+ .next()
+ .and_then(|cn| cn.as_str().ok())
+ .unwrap_or("<no name>"),
+ not_after.to_rfc2822(),
+ remaining.num_days(),
+ );
+ } else {
+ println!(
+ "{}: {} (it expired {} days ago)",
+ cert.subject()
+ .iter_common_name()
+ .next()
+ .and_then(|cn| cn.as_str().ok())
+ .unwrap_or("<no name>"),
+ not_after.to_rfc2822(),
+ remaining.num_days(),
+ );
+ }
}
fn to_text(self, cert: X509Certificate) {