diff options
| author | Franck Cuny <franck@fcuny.net> | 2025-09-06 13:13:43 -0700 |
|---|---|---|
| committer | Franck Cuny <franck@fcuny.net> | 2025-09-06 13:22:39 -0700 |
| commit | 7aafc722317793f7c12cdeae8d7486ca8b23307b (patch) | |
| tree | 5aac9ed56164836b9f6ff6c965a3d85e546ca599 | |
| parent | ssh-cert-info in go (diff) | |
| download | x-7aafc722317793f7c12cdeae8d7486ca8b23307b.tar.gz | |
certcheck to see x509 certification details
Diffstat (limited to '')
| -rw-r--r-- | Cargo.lock | 4 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | cmd/certcheck/README.org | 15 | ||||
| -rw-r--r-- | cmd/certcheck/main.go | 167 | ||||
| -rw-r--r-- | src/x509-info/Cargo.lock | 1135 | ||||
| -rw-r--r-- | src/x509-info/Cargo.toml | 17 | ||||
| -rw-r--r-- | src/x509-info/README.md | 72 | ||||
| -rw-r--r-- | src/x509-info/deny.toml | 46 | ||||
| -rw-r--r-- | src/x509-info/src/client.rs | 110 | ||||
| -rw-r--r-- | src/x509-info/src/main.rs | 41 | ||||
| -rw-r--r-- | src/x509-info/src/output.rs | 120 |
11 files changed, 183 insertions, 1546 deletions
@@ -847,10 +847,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "ssh-cert-info" -version = "0.1.0" - -[[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,3 +1,3 @@ [workspace] -members = ["src/apple_silicon/", "src/x509-info/"] +members = ["src/apple_silicon/"] resolver = "3" diff --git a/cmd/certcheck/README.org b/cmd/certcheck/README.org new file mode 100644 index 0000000..f1d2eea --- /dev/null +++ b/cmd/certcheck/README.org @@ -0,0 +1,15 @@ +* x509-info + +At this point it's pretty clear that I'll never remember the syntax for =opensll= to show various information about a certificate. At last I will not have to google for that syntax ever again. + +** Usage +#+begin_src go +certcheck example.com + +certcheck go run . -domain example.com -port 443 -format long + +certcheck -domain self-signed.badssl.com -insecure -format long +#+end_src + +** Notes +Could the same be achieved with a wrapper around `openssl` ? yes. diff --git a/cmd/certcheck/main.go b/cmd/certcheck/main.go new file mode 100644 index 0000000..1140ff9 --- /dev/null +++ b/cmd/certcheck/main.go @@ -0,0 +1,167 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "flag" + "fmt" + "os" + "time" +) + +type OutputFormat string + +const ( + FormatShort OutputFormat = "short" + FormatLong OutputFormat = "long" +) + +type Config struct { + Domain string + Port int + Insecure bool + Format OutputFormat +} + +func main() { + var config Config + var formatStr string + + flag.StringVar(&config.Domain, "domain", "", "Domain to check (required)") + flag.IntVar(&config.Port, "port", 443, "Port to check") + flag.BoolVar(&config.Insecure, "insecure", false, "Accept invalid certificate") + flag.StringVar(&formatStr, "format", "short", "Output format (short|long)") + flag.Parse() + + if config.Domain == "" { + if len(flag.Args()) == 0 { + fmt.Fprintf(os.Stderr, "Error: domain is required\n") + flag.Usage() + os.Exit(1) + } + config.Domain = flag.Args()[0] + } + + switch formatStr { + case "short": + config.Format = FormatShort + case "long": + config.Format = FormatLong + default: + fmt.Fprintf(os.Stderr, "Error: invalid format '%s', must be 'short' or 'long'\n", formatStr) + os.Exit(1) + } + + cert, err := getCertificate(config.Domain, config.Port, config.Insecure) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + printCertificate(cert, config.Format) +} + +func getCertificate(domain string, port int, insecure bool) (*x509.Certificate, error) { + address := fmt.Sprintf("%s:%d", domain, port) + + tlsConfig := &tls.Config{ + ServerName: domain, + InsecureSkipVerify: insecure, + } + + conn, err := tls.Dial("tcp", address, tlsConfig) + if err != nil { + return nil, fmt.Errorf("failed to connect to %s: %w", address, err) + } + defer func() { + if closeErr := conn.Close(); closeErr != nil { + // Log the error but don't override the main function's return value + fmt.Fprintf(os.Stderr, "warning: failed to close connection: %v\n", closeErr) + } + }() + + certs := conn.ConnectionState().PeerCertificates + if len(certs) == 0 { + return nil, fmt.Errorf("no certificate found for %s", domain) + } + + return certs[0], nil +} + +func printCertificate(cert *x509.Certificate, format OutputFormat) { + switch format { + case FormatShort: + printShort(cert) + case FormatLong: + printLong(cert) + } +} + +func printShort(cert *x509.Certificate) { + remaining := time.Until(cert.NotAfter) + + commonName := getCommonName(cert) + + if remaining >= 0 { + days := int(remaining.Hours() / 24) + fmt.Printf("%s: %s (%d days left)\n", + commonName, + cert.NotAfter.Format(time.RFC1123Z), + days) + } else { + days := int(-remaining.Hours() / 24) + fmt.Printf("%s: %s (it expired %d days ago)\n", + commonName, + cert.NotAfter.Format(time.RFC1123Z), + days) + } +} + +func printLong(cert *x509.Certificate) { + remaining := time.Until(cert.NotAfter) + validityDuration := cert.NotAfter.Sub(cert.NotBefore) + + fmt.Println("certificate") + fmt.Printf(" version: %d\n", cert.Version) + fmt.Printf(" serial: %s\n", cert.SerialNumber.String()) + fmt.Printf(" subject: %s\n", cert.Subject.String()) + fmt.Printf(" issuer: %s\n", cert.Issuer.String()) + + fmt.Println(" validity") + fmt.Printf(" not before : %s\n", cert.NotBefore.Format(time.RFC1123Z)) + fmt.Printf(" not after : %s\n", cert.NotAfter.Format(time.RFC1123Z)) + fmt.Printf(" validity days : %d\n", int(validityDuration.Hours()/24)) + fmt.Printf(" remaining days: %d\n", int(remaining.Hours()/24)) + + fmt.Println(" SANs:") + printSANs(cert) +} + +func getCommonName(cert *x509.Certificate) string { + if cert.Subject.CommonName != "" { + return cert.Subject.CommonName + } + return "<no name>" +} + +func printSANs(cert *x509.Certificate) { + // DNS names + for _, name := range cert.DNSNames { + fmt.Printf(" DNS:%s\n", name) + } + + // IP addresses + for _, ip := range cert.IPAddresses { + fmt.Printf(" IP address:%s\n", ip.String()) + } + + // Email addresses + for _, email := range cert.EmailAddresses { + fmt.Printf(" Email:%s\n", email) + } + + // URIs + for _, uri := range cert.URIs { + fmt.Printf(" URI:%s\n", uri.String()) + } +} diff --git a/src/x509-info/Cargo.lock b/src/x509-info/Cargo.lock deleted file mode 100644 index 6e8a026..0000000 --- a/src/x509-info/Cargo.lock +++ /dev/null @@ -1,1135 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "anyhow" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" - -[[package]] -name = "asn1-rs" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" - -[[package]] -name = "aws-lc-rs" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5509d663b2c00ee421bda8d6a24d6c42e15970957de1701b8df9f6fbe5707df1" -dependencies = [ - "aws-lc-sys", - "mirai-annotations", - "paste", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5d317212c2a78d86ba6622e969413c38847b62f48111f8b763af3dac2f9840" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", - "libc", - "paste", -] - -[[package]] -name = "base64" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" - -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.5.0", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", - "which", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cc" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" -dependencies = [ - "jobserver", - "libc", - "once_cell", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-targets", -] - -[[package]] -name = "clang-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "der-parser" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "either" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "getrandom" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "jobserver" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "libloading" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" -dependencies = [ - "cfg-if", - "windows-targets", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "memchr" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oid-registry" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "prettyplease" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustls" -version = "0.23.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79adb16721f56eb2d843e67676896a61ce7a0fa622dc18d3e372477a029d2740" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" -dependencies = [ - "base64", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" - -[[package]] -name = "rustls-webpki" -version = "0.102.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "security-framework" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "serde" -version = "1.0.199" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.199" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "2.0.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "webpki-roots" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" - -[[package]] -name = "x509-info" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "clap", - "rustls", - "rustls-native-certs", - "webpki-roots", - "x509-parser", -] - -[[package]] -name = "x509-parser" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/src/x509-info/Cargo.toml b/src/x509-info/Cargo.toml deleted file mode 100644 index 24f0478..0000000 --- a/src/x509-info/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "x509-info" -version = "0.1.0" -edition = "2021" -authors = ["Franck Cuny <franck@fcuny.net>"] -description = "Print summary of x509 certificates." -homepage = "https://github.com/fcuny/x509-info/" -readme = "README.md" - -[dependencies] -anyhow = "1.0.82" -chrono = {version = "0.4.38", features = ["clock"], default-features = false } -clap = {version = "4.5.8", features = ["derive", "cargo"]} -rustls = {version = "0.23.8", features = ["ring"]} -rustls-native-certs = "0.7.0" -webpki-roots = "0.26.3" -x509-parser = "0.16" diff --git a/src/x509-info/README.md b/src/x509-info/README.md deleted file mode 100644 index 0369ba2..0000000 --- a/src/x509-info/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# x509-info - -At this point it's pretty clear that I'll never remember the syntax for `openssl` to show various information about a certificate. At last I will not have to google for that syntax ever again. - -## Usage - -```shell -$ Usage: x509-info [OPTIONS] <DOMAIN> - -Arguments: - <DOMAIN> - Domain to check - -Options: - -p, --port <PORT> - Port to check - - [default: 443] - - -i, --insecure - Accept invalid certificate - - -f, --format <FORMAT> - [default: short] - - Possible values: - - short: Format the output as one line of plain text - - long: Format the output as plain text - - -h, --help - Print help information (use `-h` for a summary) - - -V, --version - Print version information -``` - -The default format will print a short message: - -```shell -$ 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 -certificate - version: V3 - 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 : 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/deny.toml b/src/x509-info/deny.toml deleted file mode 100644 index fd95cdb..0000000 --- a/src/x509-info/deny.toml +++ /dev/null @@ -1,46 +0,0 @@ -[advisories] -db-path = "~/.cargo/advisory-db" -db-urls = ["https://github.com/rustsec/advisory-db"] -vulnerability = "deny" -unmaintained = "warn" -yanked = "warn" -notice = "warn" -ignore = [] - -[licenses] -unlicensed = "deny" -allow = ["MIT", "Apache-2.0", "ISC", "Unicode-DFS-2016", "OpenSSL"] -deny = [] -copyleft = "allow" -default = "deny" -confidence-threshold = 0.8 -exceptions = [] - -[licenses.private] -ignore = false -registries = [] - -# see https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html#example -[[licenses.clarify]] -name = "ring" -expression = "MIT AND ISC AND OpenSSL" -license-files = [ - { path = "LICENSE", hash = 0xbd0eed23 } -] - -[bans] -multiple-versions = "warn" -wildcards = "allow" -highlight = "all" -allow = [] -deny = [] -skip = [] -skip-tree = [] - -[sources] -unknown-registry = "warn" -unknown-git = "warn" -allow-registry = ["https://github.com/rust-lang/crates.io-index"] -allow-git = [] - -[sources.allow-org] diff --git a/src/x509-info/src/client.rs b/src/x509-info/src/client.rs deleted file mode 100644 index 83e010c..0000000 --- a/src/x509-info/src/client.rs +++ /dev/null @@ -1,110 +0,0 @@ -use anyhow::{anyhow, Error}; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; -use rustls::crypto::{ring, verify_tls12_signature, verify_tls13_signature, CryptoProvider}; -use rustls::pki_types::{self, CertificateDer, ServerName, UnixTime}; -use rustls::{ClientConfig, ClientConnection}; -use rustls::{DigitallySignedStruct, SignatureScheme}; - -use std::io::Write; -use std::sync::Arc; - -#[derive(Debug)] -struct NoCertificateVerification {} - -impl ServerCertVerifier for NoCertificateVerification { - fn verify_server_cert( - &self, - _end_entity: &CertificateDer<'_>, - _intermediates: &[CertificateDer<'_>], - _server_name: &pki_types::ServerName<'_>, - _ocsp_response: &[u8], - _now: UnixTime, - ) -> Result<ServerCertVerified, rustls::Error> { - Ok(ServerCertVerified::assertion()) - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result<HandshakeSignatureValid, rustls::Error> { - verify_tls12_signature( - message, - cert, - dss, - &ring::default_provider().signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result<HandshakeSignatureValid, rustls::Error> { - verify_tls13_signature( - message, - cert, - dss, - &ring::default_provider().signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { - ring::default_provider() - .signature_verification_algorithms - .supported_schemes() - } -} - -pub fn get_certificates( - domain: String, - port: u16, - insecure: bool, -) -> Result<Vec<CertificateDer<'static>>, Error> { - let mut tcp_stream = std::net::TcpStream::connect(format!("{}:{}", domain, port))?; - - let config = if insecure { - ClientConfig::builder() - .dangerous() - .with_custom_certificate_verifier(Arc::new(NoCertificateVerification {})) - .with_no_client_auth() - } else { - let root_store = rustls::RootCertStore { - roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), - }; - ClientConfig::builder_with_provider( - CryptoProvider { - cipher_suites: vec![ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256], - kx_groups: vec![ring::kx_group::X25519], - ..ring::default_provider() - } - .into(), - ) - .with_protocol_versions(&[&rustls::version::TLS13]) - .unwrap() - .with_root_certificates(root_store) - .with_no_client_auth() - }; - - let server_name = ServerName::try_from(domain.clone()) - .expect("invalid DNS name") - .to_owned(); - - let mut conn = ClientConnection::new(Arc::new(config), server_name)?; - while conn.wants_write() { - conn.write_tls(&mut tcp_stream)?; - } - - tcp_stream.flush()?; - while conn.is_handshaking() && conn.peer_certificates().is_none() { - conn.read_tls(&mut tcp_stream)?; - conn.process_new_packets()?; - } - - match conn.peer_certificates() { - Some(c) => Ok(c.to_vec()), - None => Err(anyhow!("no certificate found for {}", domain))?, - } -} diff --git a/src/x509-info/src/main.rs b/src/x509-info/src/main.rs deleted file mode 100644 index d37ce0c..0000000 --- a/src/x509-info/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -extern crate webpki_roots; - -mod client; -mod output; - -use clap::Parser; -use x509_parser::prelude::*; - -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -struct Args { - /// Domain to check - domain: String, - - /// Port to check - #[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, -} - -fn main() { - let args = Args::parse(); - - match client::get_certificates(args.domain, args.port, args.insecure) { - Ok(certs) => { - let (_, cert) = - x509_parser::certificate::X509Certificate::from_der(certs[0].as_ref()).unwrap(); - output::OutputFormat::print(args.format, cert); - } - Err(e) => { - println!("error: {}", e); - std::process::exit(1); - } - }; -} diff --git a/src/x509-info/src/output.rs b/src/x509-info/src/output.rs deleted file mode 100644 index 963ef20..0000000 --- a/src/x509-info/src/output.rs +++ /dev/null @@ -1,120 +0,0 @@ -use chrono::prelude::*; -use std::net::{Ipv4Addr, Ipv6Addr}; -use x509_parser::prelude::*; - -/// How to format the output data. -#[derive(PartialEq, Eq, Debug, Copy, Clone, clap::ValueEnum)] -pub enum OutputFormat { - /// Format the output as one line of plain text. - Short, - - /// Format the output as plain text. - Long, -} - -impl OutputFormat { - pub fn print(self, cert: X509Certificate) { - match self { - Self::Short => { - self.short(cert); - } - Self::Long => { - self.to_text(cert); - } - } - } - - fn short(self, cert: X509Certificate) { - let not_after = chrono::Local - .timestamp_opt(cert.validity().not_after.timestamp(), 0) - .unwrap(); - let now: DateTime<Local> = Local::now(); - let remaining = not_after - now; - - 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) { - let not_before = chrono::Local - .timestamp_opt(cert.validity().not_before.timestamp(), 0) - .unwrap(); - let not_after = chrono::Local - .timestamp_opt(cert.validity().not_after.timestamp(), 0) - .unwrap(); - let now: DateTime<Local> = Local::now(); - let remaining = not_after - now; - let validity_duration = not_after - not_before; - - println!("certificate"); - println!(" version: {}", cert.version); - println!(" serial: {}", cert.tbs_certificate.raw_serial_as_string()); - println!(" subject: {}", cert.subject()); - println!(" issuer: {}", cert.issuer()); - - println!(" validity"); - println!(" not before : {}", not_before.to_rfc2822()); - println!(" not after : {}", not_after.to_rfc2822()); - println!(" validity days : {}", validity_duration.num_days()); - println!(" remaining days: {}", remaining.num_days()); - - println!(" SANs:"); - if let Some(subnames) = subject_alternative_name(cert) { - for name in subnames { - println!(" {}", name); - } - } - } -} - -fn subject_alternative_name(cert: X509Certificate) -> Option<Vec<String>> { - let mut subnames = Vec::new(); - if let Ok(Some(san)) = cert.subject_alternative_name() { - let san = san.value; - for name in &san.general_names { - let s = match name { - GeneralName::DNSName(s) => format!("DNS:{}", s), - GeneralName::IPAddress(b) => { - let ip = match b.len() { - 4 => { - let b = <[u8; 4]>::try_from(*b).unwrap(); - let ip = Ipv4Addr::from(b); - format!("{}", ip) - } - 16 => { - let b = <[u8; 16]>::try_from(*b).unwrap(); - let ip = Ipv6Addr::from(b); - format!("{}", ip) - } - l => format!("invalid (len={})", l), - }; - format!("IP address:{}", ip) - } - _ => format!("{:?}", name), - }; - subnames.push(s); - } - } - Some(subnames) -} |
