如果我在 Mac 或 Linux 中拥有实际文件和 Bash shell,我如何查询证书文件的过期时间?不是一个网站,但实际上是证书文件本身,假设我有 csr、密钥、pem 和链文件。
与
openssl
:
openssl x509 -enddate -noout -in file.pem
输出的形式为:
notAfter=Nov 3 22:23:50 2014 GMT
另请参阅 MikeW 的回答,了解如何轻松检查证书是否已过期,或者是否在特定时间段内过期,而无需解析上面的日期。
如果您只是想知道证书是否已过期(或将在接下来的 N 秒内过期),
-checkend <seconds>
的 openssl x509
选项会告诉您:
if openssl x509 -checkend 86400 -noout -in file.pem
then
echo "Certificate is good for another day!"
else
echo "Certificate has expired or will do so within 24 hours!"
echo "(or is invalid/not found)"
fi
这样就不用自己进行日期/时间比较了。
如果证书尚未过期,openssl
将返回退出代码 0
(零),并且在接下来的 86400 秒内不会这样做,如上例所示。如果证书已过期或已经过期 - 或出现其他错误,例如无效/不存在的文件 - 返回代码为 1
。
(当然,前提是时间/日期设置正确)
请注意,旧版本的 openssl 有一个 bug,这意味着如果
checkend
中指定的时间太大,则始终返回 0 (https://github.com/openssl/openssl/issues/6180)。
这是我的 bash 命令行,用于按过期顺序列出多个证书,最近过期的最先。
for pem in /etc/ssl/certs/*.pem; do
printf '%s: %s\n' \
"$(date --date="$(openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" --iso-8601)" \
"$pem"
done | sort
输出示例:
2015-12-16: /etc/ssl/certs/Staat_der_Nederlanden_Root_CA.pem
2016-03-22: /etc/ssl/certs/CA_Disig.pem
2016-08-14: /etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_S.pem
命令:
# cat {key_name} | openssl x509 -noout -enddate
Example: # cat tower.cert | openssl x509 -noout -enddate
结果:
notAfter=Dec 7 04:03:32 2023 GMT
这里有一个 bash 函数,它会检查所有服务器(假设您使用的是 DNS 循环)。请注意,这需要 GNU 日期,并且不适用于 Mac OS
function check_certs () {
if [ -z "$1" ]
then
echo "domain name missing"
exit 1
fi
name="$1"
shift
now_epoch=$( date +%s )
dig +noall +answer $name | while read _ _ _ _ ip;
do
echo -n "$ip:"
expiry_date=$( echo | openssl s_client -showcerts -servername $name -connect $ip:443 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2 )
echo -n " $expiry_date";
expiry_epoch=$( date -d "$expiry_date" +%s )
expiry_days="$(( ($expiry_epoch - $now_epoch) / (3600 * 24) ))"
echo " $expiry_days days"
done
}
输出示例:
$ check_certs stackoverflow.com
151.101.1.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.65.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.129.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.193.69: Aug 14 12:00:00 2019 GMT 603 days
与接受的答案相同,但请注意,它甚至适用于
.crt
文件而不仅仅是 .pem
文件,以防万一您无法找到 .pem
文件位置。
openssl x509 -enddate -noout -in e71c8ea7fa97ad6c.crt
结果:
notAfter=Mar 29 06:15:00 2020 GMT
一行检查真/假,域名证书是否会在一段时间后(例如 15 天)过期:
openssl x509 -checkend $(( 24*3600*15 )) -noout -in <(openssl s_client -showcerts -connect my.domain.com:443 </dev/null 2>/dev/null | openssl x509 -outform PEM)
if [ $? -eq 0 ]; then
echo 'good'
else
echo 'bad'
fi
openssl
字段存储到 bash 变量中由于这个问题被标记为 bash,我经常使用
UNIX EPOCH
来存储日期,这对于计算 $EPOCHSECONDS
剩余的时间并通过 printf '%(dateFmt)T
bashism: 格式化输出非常有用
{ read -r certStart;read -r certEnd;}< <(date -f <(cut -d = -f 2 <(
openssl x509 -dates -noout -in "$file")) +%s)
然后
printf '%-6s %(%a %d %b %Y, %H %Z)T\n' start $certStart end $certEnd
start Mon 01 Nov 2004, 17 UTC
end Mon 01 Jan 2035, 05 UTC
示例,列出
/etc/ssl/certs
的内容并计算剩余天数:
for file in /etc/ssl/certs/*pem;do
{ read -r certStart;read -r certEnd;}< <(
date -f <(cut -d = -f 2 <(
openssl x509 -dates -noout -in "$file")) +%s)
printf "%(%d %b %Y %T)T - %(%d %b %Y %T)T: %6d %s\n" \
$certStart $certEnd $(( (certEnd - EPOCHSECONDS)/86400 )) ${file##*/}
done
05 May 2011 09:37:37 - 31 Dec 2030 09:37:37: 3034 ACCVRAIZ1.pem
26 Oct 2010 08:38:03 - 26 Oct 2040 08:38:03: 6620 Buypass_Class_2_Root_CA.pem
19 Jan 2010 00:00:00 - 18 Jan 2038 23:59:59: 5609 COMODO_RSA_Certification_Authority.pem
13 Nov 2012 00:00:00 - 19 Jan 2038 03:14:07: 5609 GlobalSign_ECC_Root_CA_-_R4.pem
06 Apr 2001 07:29:40 - 06 Apr 2021 07:29:40: -522 Sonera_Class_2_Root_CA.pem
29 Jun 2004 17:39:16 - 29 Jun 2034 17:39:16: 4310 Starfield_Class_2_CA.pem
04 Feb 2016 12:32:16 - 31 Dec 2029 17:23:16: 2669 TrustCor_RootCert_CA-1.pem
01 Nov 2004 17:14:04 - 01 Jan 2035 05:37:19: 4495 XRamp_Global_CA_Root.pem
...
for file in /etc/ssl/certs/*pem;do
mapfile -t x509 < <(openssl x509 -noout -dates -subject -in "$file")
x509=("${x509[@]#*=}")
mapfile -t dates < <(IFS=$'\n';date -f - <<<"${x509[*]::2}" +%s)
str="${x509[-1]}"
declare -A Subj='([CN]="${file##*/}")'
while [[ "$str" ]] ;do
lhs=${str%%=*} rhs=${str#$lhs= } rhs=${rhs%% = *} rhs=${rhs%, *}
Subj[${lhs// }]="$rhs"
str=${str#"$lhs= $rhs"} str=${str#, }
done
printf "%(%d %b %Y %T)T - %(%d %b %Y %T)T: %s\n" \
${dates[@]} "${Subj[CN]}"
done
05 May 2011 09:37:37 - 31 Dec 2030 09:37:37: 3034 ACCVRAIZ1
26 Oct 2010 08:38:03 - 26 Oct 2040 08:38:03: 6620 Buypass Class 2 Root CA
19 Jan 2010 00:00:00 - 18 Jan 2038 23:59:59: 5609 COMODO RSA Certification Authority
13 Nov 2012 00:00:00 - 19 Jan 2038 03:14:07: 5609 GlobalSign
06 Apr 2001 07:29:40 - 06 Apr 2021 07:29:40: -522 Sonera Class2 CA
29 Jun 2004 17:39:16 - 29 Jun 2034 17:39:16: 4310 Starfield_Class_2_CA.pem
04 Feb 2016 12:32:16 - 31 Dec 2029 17:23:16: 2669 TrustCor RootCert CA-1
01 Nov 2004 17:14:04 - 01 Jan 2035 05:37:19: 4495 XRamp Global Certification Authority
...
注意:某些证书的主题中没有
CN
字段。为此,我通过将 $Subj
字段设置为文件名来初始化 CN
数组:
declare -A Subj='([CN]="${file##*/}")'
在这里分享一个完整的 bash 脚本,显示来自命令行参数的所有证书,可以通过
file
、domain name
或 IPv4 address
。将在一行(长)行中输出过去的日子、剩余的天数、备用域的数量以及所有替代项:
#!/bin/bash
showCert() {
local x509 dates lhs rhs str alts
mapfile -t x509 < <(
openssl x509 -noout -dates -subject -ext subjectAltName -in "$1")
x509=("${x509[@]#*=}")
mapfile -t dates < <(IFS=$'\n';date -f - <<<"${x509[*]::2}" +%s)
str="${x509[2]}"
local -A Subj;Subj[CN]="${file##*/}"
while [[ -n "$str" ]]; do
lhs=${str%%=*} rhs=${str#$lhs= } rhs=${rhs%% = *} rhs=${rhs%, *}
Subj[${lhs// }]="$rhs"
str=${str#"$lhs= $rhs"} str=${str#, }
done
read -ra alts <<<"${x509[4]//,}"
alts=("${alts[@]#*:}")
printf " %(%d %b %Y %H:%M)T %(%d %b %Y %H:%M)T %6d %6d %-30s %3d %s\n" \
"${dates[@]}" $(((dates[1]-EPOCHSECONDS)/86400)) $(((EPOCHSECONDS-
dates[0])/86400)) "${Subj[CN]}" "${#alts[@]}" "${alts[*]}"
}
checkIsIpv4() { # throw an error if not valid IPv4
local _iPointer _i _a _vareq=()
for _i ;do
case $_i in *[^0-9.]* ) return 1 ;; esac
read -ra _a <<<"${_i//./ }"
[ ${#_a[@]} -eq 4 ] || return 1
for _iPointer in "${_a[@]}" ;do
(( _iPointer == ( _iPointer & 255 ) )) || return 2
done
done
}
checkIsLabel() {
((${#1}<4 || ${#1}>253)) && return 1
[[ -z ${1//[a-zA-Z0-9.-]} ]] || return 2
[[ -z ${1//.} ]] && return 3
set -- ${1//./ }
(($#<2 )) && return 4
:
}
printf ' %-17s %-17s %6s %6s %-30s %2s\n' Not\ before Not\ after left \
past Common\ Name Alt
for arg ;do
if [ -f "$arg" ] ;then
showCert "$arg"
elif checkIsLabel "$arg" || checkIsIpv4 "$arg" ;then
showCert <(openssl s_client -ign_eof -connect "$arg:443" \
<<<$'HEAD / HTTP/1.0\r\n\r' 2> /dev/null)
else
echo "Unknown argument: '$arg'."
fi
done
showCert
创建一个数组变量$x590
,其中包含日期、主题和替代名称。mapfile
和(仅1个叉子)date
来转换两者开始和结束日期。$Subj
用于解析Subject字符串(第3行:${x509[2]}
)$alts
。printf
格式化并打印显示的每个证书
./certShow.sh /etc/ssl/certs/ssl-cert-snakeoil.pem www.example.com
Not before Not after left past Common Name Alt
08 Sep 2021 16:49 06 Sep 2031 16:49 3277 372 hostname.local 1 hostname.local
14 Mar 2022 00:00 14 Mar 2023 23:59 179 186 www.example.org 8 www.example.org example.net example.edu example.com example.org www.example.com www.example.edu www.example.net
对于 MAC OSX (El Capitan),尼古拉斯示例的这种修改对我有用。
for pem in /path/to/certs/*.pem; do
printf '%s: %s\n' \
"$(date -jf "%b %e %H:%M:%S %Y %Z" "$(openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" +"%Y-%m-%d")" \
"$pem";
done | sort
示例输出:
2014-12-19: /path/to/certs/MDM_Certificate.pem
2015-11-13: /path/to/certs/MDM_AirWatch_Certificate.pem
macOS 不喜欢我系统上的
--date=
或 --iso-8601
标志。
如果(出于某种原因)您想在 Linux 中使用 GUI 应用程序,请使用
gcr-viewer
(在大多数发行版中,它是由包 gcr
安装的(否则在包 gcr-viewer
中))
gcr-viewer file.pem
# or
gcr-viewer file.crt
我制作了一个与之相关的bash脚本来检查证书是否过期。如果需要,您可以使用相同的。
脚本
https://github.com/zeeshanjamal16/usefulScripts/blob/master/sslCertificateExpireCheck.sh
自述文件
https://github.com/zeeshanjamal16/usefulScripts/blob/master/README.md
使用
openssl
这是一个打印人类可读文本的脚本
cert-etime-check.sh
#!/bin/bash
set -euo pipefail
### get a cert file
declare -r cert_file=${1:? error ... cert_file ?}
### start and end dates
declare -r beg_date=$(openssl x509 -noout -startdate -in $cert_file)
declare -r end_date=$(openssl x509 -noout -enddate -in $cert_file)
### subject and issues
declare -r subject=$(openssl x509 -noout -subject -in $cert_file)
declare -r issuer=$(openssl x509 -noout -issuer -in $cert_file)
### calculate days gone and left
declare -ir beg_sec=$(date --date="${beg_date##*=}" +%s)
declare -ir end_sec=$(date --date="${end_date##*=}" +%s)
declare -ir now_sec=$(date +%s)
declare -ir day_gone=$(( $((now_sec - beg_sec)) / 86400))
declare -ir day_left=$(( $((end_sec - now_sec)) / 86400))
### print result
date --date="${beg_date##*=}" +"beg: %F-%A (gone: $day_gone)"
date --date="${end_date##*=}" +"end: %F-%A (left: $day_left)"
echo sum: $((day_gone + $day_left))
echo sub: ${subject##* = }
echo iss: ${issuer##* = }
echo
date +"now: %F-%A"
用法:
./cert-etime-check.sh cert.pem
输出:
beg: 2024-03-04-Monday (gone: 26)
end: 2024-06-02-Sunday (left: 63)
sum: 89
sub: ******.***
iss: R3
now: 2024-03-31-Sunday