summaryrefslogtreecommitdiff
path: root/cert-info
blob: 28865bda8aa28ce7983ca078762a75a70e917262 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/bin/bash
# see also Docs/ssl-infos/tools/
# i= &&tail -n+$((i*5000)) /tmp/top-1m.csv | head -5000 |~/scripts/cert-info csv $i
# killall -v cert-info -s 0
usage() {
	[ -z "$1" ] || echo "$1" >&2
	echo "Usage: $0 [options] hostname[:port] [hostname[:port]].." >&2
	echo "       $0 csv out-dir <file [options]" >&2
	echo "options (may be interleaved between hosts):"
	awk '/^parse_arg\(/{p=1}
	/}/{if(p)exit}
	/#/{if(p){sub(/\).*# */,"\t");print}}' "$0"
	echo "csv file from http://s3.amazonaws.com/alexa-static/top-1m.csv.zip"
	exit 1
}

auto_less() {
	[ ! -t 1 ] || { "$0" "$@" | less; exit $?; }
}

main() {
if [ "$1" = "csv" ]; then
	[ $# -ge 2 ] || usage "Missing outdir"
	outdir="${2:-.}/"
	mkdir -p "$outdir"
	tmp=$(mktemp); trap "rm -f $tmp" EXIT
	shift 2
	for arg; do parse_arg "$arg" || usage "Unrecognized option $arg"; done
	i=0
	while IFS=,; read -r no host; do
		# strip path if any
		host="${host%%/*}"
		[ -n "$host" ] || continue
		outfile="$outdir$host.txt"
		if [ -e "$outfile" ]; then
			echo "Skipping existing $outfile"
		else
			printf "%5d %s %6d %s\n" $((++i)) "$(date -R)" "$no" "$host"
			get_cert "$host" 443 | parse_cert > "$tmp" 2>&1
			mv "$tmp" "$outfile"
			#[ -s "$outfile" ] || echo "# no cert for $host"
		fi
	done
elif [ ! -t 0 ]; then
	auto_less "$@"
	for arg; do
		parse_arg "$arg" || :
	done
	parse_cert
elif [ $# -gt 0 ]; then
	auto_less "$@"
	for arg; do
	 	! parse_arg "$arg" || continue
		host="${arg%%:*}"
		port="${arg##*:}"
		[ "$port" != "$arg" -a -n "$port" ] || port=443
		echo "# === $host:$port ==="
		get_cert "$host" "$port" | parse_cert
	done
else
	usage
fi 
}

get_cert() {
	local host="$1" port="$2"
	if ! nc -z -w 2 "$host" "$port"; then
		echo "# conn timeout for $host:$port!" >&2
		return 1
	fi
	</dev/null 2>/dev/null \
	timeout 5 openssl s_client \
		-connect "$host:$port" -servername "$host" -showcerts
}

parse_arg() {
	case "$1" in
	-) depth_1=1 ;; # less certificates [def]
	+) depth_1=0 ;; # more certificates
	-=) cert_only=1 ;; # less output (just raw cert)
	+=) cert_only=0 ;; # more output (verbose details) [def]
	-cert) include_cert=0 ;; # hide raw cert (-noout)
	+cert) include_cert=1 ;; # include raw cert [def]
	*) return 1 ;;
	esac
}
parse_arg -
parse_arg +=
parse_arg +cert

parse_cert() {
	infocmd="openssl x509 -text -nameopt sep_comma_plus_space"
	[ $include_cert -eq 1 ] || infocmd="$infocmd -noout"
	awk -vOneOnly="$depth_1" -vCertOnly="$cert_only" \
		-vinfocmd="$infocmd" '
BEGIN {
	sep="# ";
	for (i=0; i<77; i++) {
		sep=sep"-";
	}
}
/^-----BEGIN (TRUSTED )?CERTIFICATE-----$/ {
	in_cert=1;
}
{
	if (in_cert) {
		cert = cert $0 "\n";
	}
}
/^-----END (TRUSTED )?CERTIFICATE-----$/ {
	in_cert = 0;
}
{
	if (!in_cert && cert) {
		if (CertOnly) {
			print cert;
		} else {
			print cert | infocmd;
			close(infocmd);
			print sep;
		}
		cert="";
		if (OneOnly) exit;
	}
}
'
}

main "$@"