如何从 PEM 编码证书确定 SSL 证书到期日期?

问题描述 投票:0回答:12

如果我在 Mac 或 Linux 中拥有实际文件和 Bash shell,我如何查询证书文件的过期时间?不是一个网站,但实际上是证书文件本身,假设我有 csr、密钥、pem 和链文件。

linux bash ssl openssl certificate
12个回答
996
投票

openssl

openssl x509 -enddate -noout -in file.pem

输出的形式为:

notAfter=Nov  3 22:23:50 2014 GMT

另请参阅 MikeW 的回答,了解如何轻松检查证书是否已过期,或者是否在特定时间段内过期,而无需解析上面的日期。


233
投票

如果您只是想知道证书是否已过期(或将在接下来的 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)。


47
投票

这是我的 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

29
投票

命令:

# cat {key_name} | openssl x509 -noout -enddate
Example: # cat tower.cert | openssl x509 -noout -enddate

结果:

notAfter=Dec  7 04:03:32 2023 GMT

16
投票

这里有一个 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

16
投票

与接受的答案相同,但请注意,它甚至适用于

.crt
文件而不仅仅是
.pem
文件,以防万一您无法找到
.pem
文件位置。

openssl x509 -enddate -noout -in e71c8ea7fa97ad6c.crt

结果:

notAfter=Mar 29 06:15:00 2020 GMT

8
投票

一行检查真/假,域名证书是否会在一段时间后(例如 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

3
投票

openssl
字段存储到 变量中

由于这个问题被标记为 ,我经常使用

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
...

更完整的 x509 阅读:

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

2
投票

对于 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
标志。


1
投票

如果(出于某种原因)您想在 Linux 中使用 GUI 应用程序,请使用

gcr-viewer
(在大多数发行版中,它是由包
gcr
安装的(否则在包
gcr-viewer
中))

gcr-viewer file.pem
# or
gcr-viewer file.crt

0
投票

我制作了一个与之相关的bash脚本来检查证书是否过期。如果需要,您可以使用相同的。

脚本

https://github.com/zeeshanjamal16/usefulScripts/blob/master/sslCertificateExpireCheck.sh

自述文件

https://github.com/zeeshanjamal16/usefulScripts/blob/master/README.md


0
投票

使用

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
© www.soinside.com 2019 - 2024. All rights reserved.