使用cURL与OpenSSL全面测试网站SSL/TLS证书有效性
针对你提到的批量网站SSL/TLS测试需求,下面直接给出具体实现方法和问题解答:
1. 检测服务器证书是否过期
用OpenSSL直接提取证书的有效期信息,批量处理可结合循环:
# 假设域名列表存在domains.txt文件中,每行一个域名 for domain in $(cat domains.txt); do echo "=== 检查 $domain 证书有效期 ===" openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | openssl x509 -noout -dates done
输出的notBefore是证书生效时间,notAfter是过期时间,直接对比当前时间即可判断是否过期。
2. 检测旧加密协议(SSLv2、SSLv3、TLS 1.0等)
通过指定OpenSSL的协议版本参数,测试服务器是否支持旧协议:
for domain in $(cat domains.txt); do echo "=== 检查 $domain 旧协议支持情况 ===" # 测试SSLv2 openssl s_client -connect "$domain:443" -servername "$domain" -ssl2 2>&1 | grep -E "(SSL handshake failed|New, )" # 测试SSLv3 openssl s_client -connect "$domain:443" -servername "$domain" -ssl3 2>&1 | grep -E "(SSL handshake failed|New, )" # 测试TLS 1.0 openssl s_client -connect "$domain:443" -servername "$domain" -tls1 2>&1 | grep -E "(SSL handshake failed|New, )" done
- 出现
New, TLSv1.0, Cipher这类内容,说明协议可用 - 出现
SSL handshake failed或连接错误,说明协议已被禁用
3. 证书链完整性检测及缺失中间证书定位
关于cURL错误的推断
你提到的SSL certificate problem: unable to get local issuer certificate或TLS alert, unknown CA (560)确实能推断证书链不完整,但这些错误只能告诉你链有问题,无法直接定位缺失的具体中间证书,必须用OpenSSL解析证书链内容。
具体检测方法
步骤1:导出服务器返回的完整证书链
openssl s_client -connect "$domain:443" -servername "$domain" -showcerts 2>/dev/null | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{print $0}' > "$domain-chain.pem"
步骤2:验证链完整性并定位缺失项
# 用系统根证书库验证(默认路径为/etc/ssl/certs/ca-certificates.crt) openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt -untrusted "$domain-chain.pem" "$domain-chain.pem"
如果输出error 20 at 0 depth lookup: unable to get local issuer certificate,说明链不完整。接下来解析每个证书的issuer和subject:
# 将链拆分为单个证书文件 awk '/BEGIN CERTIFICATE/{n++} {print > "'$domain'-cert"n".pem"}' "$domain-chain.pem" # 逐个查看证书的主体和颁发者 for cert in "$domain"-cert*.pem; do echo "=== 证书 $cert 信息 ===" openssl x509 -noout -subject -issuer -in "$cert" done
正常的证书链应该是:服务器证书的issuer等于中间证书的subject,中间证书的issuer等于根证书的subject。如果某证书的issuer没有匹配到链中的下一个证书,且该issuer不在系统根证书库中,那这个issuer对应的就是缺失的中间证书。
4. 检测根证书是否过期
方法1:检查系统根证书库中的对应根证书
先找到服务器证书链顶层的颁发者,再在系统根证书库中查找并检查有效期:
# 获取服务器证书的颁发者 top_issuer=$(openssl x509 -noout -issuer -in "$domain-cert1.pem" | sed 's/issuer=//') # 在根证书库中查找对应证书并输出有效期 openssl x509 -noout -dates -in <(grep -B1 -A "$top_issuer" /etc/ssl/certs/ca-certificates.crt)
方法2:如果服务器返回了根证书,直接检查
如果服务器返回的证书链包含根证书(通常不会,但少数情况存在),直接检查最后一个证书的有效期:
# 获取链中证书的数量 cert_count=$(ls "$domain"-cert*.pem | wc -l) # 检查最后一个证书(假设是根证书)的有效期 openssl x509 -noout -dates -in "$domain-cert$cert_count.pem"
批量测试脚本示例
把上述逻辑整合成可直接运行的Bash脚本ssl-test.sh:
#!/bin/bash DOMAIN_LIST=$1 CA_CERTS="/etc/ssl/certs/ca-certificates.crt" if [ -z "$DOMAIN_LIST" ]; then echo "用法: $0 <域名列表文件>" exit 1 fi for domain in $(cat "$DOMAIN_LIST"); do echo -e "\n=====================================" echo "正在测试: $domain" echo "=====================================" # 1. 证书有效期检查 echo -e "\n--- 证书有效期检查 ---" openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | openssl x509 -noout -dates || echo "无法获取证书信息" # 2. 旧协议检查 echo -e "\n--- 旧加密协议支持检查 ---" for proto in ssl2 ssl3 tls1 tls1_1; do result=$(openssl s_client -connect "$domain:443" -servername "$domain" -"$proto" 2>&1) if echo "$result" | grep -q "New, "; then echo "✅ $proto 协议可用" else echo "❌ $proto 协议不可用/被禁用" fi done # 3. 证书链完整性检查 echo -e "\n--- 证书链完整性检查 ---" openssl s_client -connect "$domain:443" -servername "$domain" -showcerts 2>/dev/null | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{print $0}' > "$domain-chain.tmp" verify_result=$(openssl verify -CAfile "$CA_CERTS" -untrusted "$domain-chain.tmp" "$domain-chain.tmp" 2>&1) if echo "$verify_result" | grep -q "OK"; then echo "✅ 证书链完整" else echo "❌ 证书链存在问题: $verify_result" # 解析证书链详情 echo -e "\n--- 证书链详情 ---" awk '/BEGIN CERTIFICATE/{n++} {print > "'$domain'-cert"n".tmp"}' "$domain-chain.tmp" for cert in "$domain"-cert*.tmp; do echo -e "\n证书 $cert:" openssl x509 -noout -subject -issuer -in "$cert" done rm "$domain"-cert*.tmp fi rm "$domain-chain.tmp" # 4. 根证书有效期检查 echo -e "\n--- 根证书有效期检查 ---" cert_issuer=$(openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | openssl x509 -noout -issuer | sed 's/issuer=//') root_cert=$(grep -B1 -A "$cert_issuer" "$CA_CERTS") if [ -n "$root_cert" ]; then echo "$root_cert" | openssl x509 -noout -dates || echo "无法解析根证书信息" else echo "警告: 未在系统根证书库中找到对应根证书" fi done
使用方法:
- 给脚本加执行权限:
chmod +x ssl-test.sh - 准备域名列表文件
domains.txt(每行一个域名) - 运行脚本:
./ssl-test.sh domains.txt
内容的提问来源于stack exchange,提问作者Eric Smith

