aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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) {