ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SSL证书过期排查与自动续期:格式、部署、巡检一次讲清

SSL证书过期排查与自动续期:格式、部署、巡检一次讲清 我凌晨一点多在群里看到一条消息某公司一个小程序后台接口集体报错用户端页面全打不开。值班的人第一反应是服务器挂了结果端口通、进程在、日志干干净净最后用一条命令定位了问题——证书已经过期两天了。这种事故我这些年见过太多次而且绝大多数和“技术不会修”没关系就是单纯忘了SSL证书有有效期。今天借这个标题把证书更新的完整链路、原理、格式坑和自动续期方案一次讲清楚也当给自己留一份备忘。1. 一张过期的证书到底能坑多少人1.1 客户端报错里其实写着答案证书过期这件事最直观的表现是浏览器或者客户端直接拒连。拿Chrome举例地址栏不会变成红色而是直接给你一整页错误提示错误码是NET::ERR_CERT_DATE_INVALID。很多人一看“DATE”就懵了以为服务器时间不对其实这个报错的意思是“证书的有效期已经过了”。在命令行里测试更直接。用 curl 访问一个证书过期的站点结果大概长这样$ curl -vI https://expired.example.com ... * SSL certificate problem: certificate has expired * closing connection 0 curl: (60) SSL certificate problem: certificate has expired注意这个退出码 60它是 curl 对“证书验证失败”的统一返回码。再用 openssl 手工做一次 TLS 握手输出里会有一行Verify return code: 10 (certificate has expired)这个 return code 10 就是证书过期的标准标记。所以判断证书是不是过期不需要等浏览器渲染页面openssl 一次握手就能得到明确结论。1.2 影响范围远比你想的广网页打不开只是最小的影响面。真正麻烦的是那些“没有浏览器界面”的东西手机 App 内置的接口、小程序服务端、企业内网的老系统、CI/CD 部署脚本、IoT 设备上报链路、VSFTPD 这类 FTP over TLS 服务、还有各种自研客户端。它们内部用的 TLS 库验证方式更严格证书一过期直接抛异常而且很多客户端异常信息写得很含糊比如“handshake failed”“connection reset”“SSL error”根本不会告诉你“证书过期了”。我甚至见过因为一张证书过期整个公司的自动化报表链路停了两天的情况。报表脚本每天凌晨跑一次某天开始全部失败运维查了数据库、查了网络、查了权限最后才发现是调用外部接口的 HTTPS 证书过期了。这种场景最要命的地方在于服务端看起来一切正常日志也没有明显报错你根本不会第一时间往证书方向去想。1.3 别把排查方向带偏证书相关报错还有几个容易混淆的变体排查时需要区分清楚现象可能原因浏览器提示“证书过期”证书真的过期或本地系统时间超前浏览器提示“证书尚未生效”服务器时间比真实时间慢或本地时间落后curl 报证书验证失败但日期看着正常中间证书没配全证书链不完整个别老设备/老客户端打不开证书签名算法或 TLS 版本不兼容服务器时间飘了openssl 校验不过NTP 没配好系统时钟偏差超过有效期边界这里最有迷惑性的就是时间同步。如果你的服务器时间比真实时间慢了几天而证书恰好还剩两三天才到期客户端会认为证书“尚未生效”valid from 还没到同样报出 DATE 相关错误。我曾经排过一个故障最后发现是服务器 CMOS 电池没电每次重启时间都回退证书永远“还没到生效时间”。所以排查证书问题时第一件事除了看证书日期还要顺手看一眼服务器系统时间和 NTP 同步状态。2. 读懂有效期机制才知道为什么现在“更新频繁”2.1 证书里到底存了什么很多人以为 SSL 证书就是个“证明我网站安全的文件”这理解有点偏差。实际上一张 X.509 证书里存的是一组结构化信息绑定的域名、公钥、签发机构CA、有效期起止时间以及 CA 对整段内容做的数字签名。浏览器或客户端在验证证书时做的事可以简化成两步第一步用 CA 的公钥验证证书上的数字签名是否有效确认这张证书确实是那个 CA 签发的第二步把系统当前时间和证书里的 notBefore、notAfter 两个时间戳做比较确认当前时间落在有效期内。两步都通过才算建立信任。所以证书的有效期不是“一个建议”而是信任验证的一个硬性输入。你把一张 2023 年过期的证书放到 2025 年无论签名多完美、CA 多权威客户端都会直接拒掉。这有点像小区门禁卡卡是真的门禁系统也认这个发卡机构但卡本身的授权期限过了刷上去就是进不了门。2.2 有效期为什么越缩越短前些年 SSL 证书有效期还很长三年、五年都不稀奇。后来整个 CA/B 论坛CA/Browser Forum全球 CA 机构和浏览器厂商的联合组织一直在收缩有效期上限我记得比较清楚的时间线早期证书最长可达 8 年甚至更长后来缩减到 5 年2018 年前后缩到 825 天约 27 个月2020 年 9 月之后签发的证书有效期上限是 398 天约 13 个月。现在 Chrome、Apple 等还在推动把 TLS 证书有效期进一步压到 90 天。背后的逻辑不复杂证书有效期内绑定的私钥一直在生产环境里跑暴露时间越长被泄露、被暴力破解的风险窗口越大。万一某个 CA 被攻破或者私钥丢失、算法被证明不安全比如当年的 SHA-1、1024 位 RSA有效期越短能自动淘汰旧问题证书的速度就越快。2.3 免费证书为什么大多是 90 天现在大家常用的免费证书像 Lets Encrypt、阿里云免费版、腾讯云免费版有效期基本都是 90 天上下。Lets Encrypt 从第一天起就是 90 天目的很简单用短期证书倒逼自动化。因为只有证书足够短你才必须建立自动续期机制而自动化签发、自动化部署本身就是提升安全水平的路径。国内云厂商的免费证书策略也跟随这个方向阿里云的免费版 SSL 证书目前也是 3 个月有效期到期后在控制台重新申请并部署。90 天意味着什么意味着如果你纯靠记忆力去续期一年至少要记得四次而且每次都要留出操作余量。这不是“尽量别忘”的问题而是“必须建立机制”的问题。后面我详细讲怎么把这件事自动化先把各种证书格式和常规申请流程理清楚。3. 免费证书的申请、续期与自动化部署3.1 阿里云免费证书续期其实是重新签发一张阿里云 SSL 证书的免费续期实操过的朋友应该都知道控制台里点“续期”本质上不是把旧证书的有效期延长而是重新签发一张新证书然后你手动下载、替换到服务器上。具体流程大概是登录阿里云控制台搜索“数字证书管理服务”也叫 SSL 证书找到你之前申请的免费证书如果临近到期控制台会有提醒也有“续期”入口提交续期申请后需要做域名验证验证方式一般是 DNS 解析验证或者文件验证验证通过后等证书签发免费证书一般几分钟到几小时在证书列表里下载注意选择服务器类型Nginx 下下来的是 PEM 格式证书和私钥文件Tomcat 下下来的是 PFX 或 JKSIIS 下的是 PFX把新证书替换到服务器对应目录重启或 reload Web 服务验证生效。这里有几个我自己趟过的坑别在快过期时才去申请。免费证书的验证流程虽然不复杂但如果用的是文件验证你得能往网站根目录写文件DNS 验证则需要你有域名解析控制权。遇到节假日、域名不在自己手里、DNS 服务商反应慢都可能卡住。提前两周提交是最低要求。替换证书后一定要 reload 服务。Nginx 是nginx -t nginx -s reloadTomcat 是重启或 reload connector。有人替换了文件但忘记 reload页面仍然加载旧证书检查半天以为是没替换成功。下载时选对类型。同一张证书在不同服务器类型下打包格式完全不同最稳妥的做法是下载 Nginx 版PEM KEY然后再按自己需要转成 PFX 或 JKS。关于格式转换下一节专门展开讲。3.2 用 certbot 让自建服务器实现自动续期如果是自己管理的服务器和域名我强烈建议直接上 Lets Encrypt certbot把 90 天续期做成全自动。这套方案适合大多数自建 Nginx、Apache 之类的场景而且完全免费。安装 certbot 之后假设你的 Nginx 配置已经指向了域名example.com第一次签发可以这样# 用 nginx 插件自动签发并修改 nginx 配置 $ sudo certbot --nginx -d example.com -d www.example.com # 或者用 webroot 模式不改 nginx 配置只负责签发 $ sudo certbot certonly --webroot -w /var/www/html -d example.com签发成功后证书文件在/etc/letsencrypt/live/example.com/目录下。Nginx 配置里指向它server { listen 443 ssl; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; }自动续期的关键在于 cron 或 systemd timer。我习惯用 cron简单直接# 每天凌晨3点检查一次到期前30天内才续续完自动 reload nginx 0 3 * * * /usr/bin/certbot renew --quiet --deploy-hook /bin/systemctl reload nginxcertbot renew本身是幂等的没到续期时间它不会做任何操作天天跑也没压力。--deploy-hook里的systemctl reload nginx是续期成功后重新加载证书的关键没有这一步Nginx 还会继续用内存里的旧证书。配置完之后先手动验证一遍$ sudo certbot renew --dry-run看到Congratulations, all simulated renewals succeeded就代表自动化链路通了。3.3 续期时最容易漏掉的证书链拼接无论是阿里云免费证书还是 Lets Encrypt证书文件通常不是一个而是一组叶证书你的域名证书、中间证书Intermediate CA、可能还有根证书。Nginx 配置里的ssl_certificate必须指向一个包含完整证书链的文件一般叫fullchain.pem。只知道填第一个证书文件、没链中间证书的配置浏览器端会报ERR_CERT_COMMON_NAME_INVALID或者unable to get local issuer certificate在 curl 和 openssl 里则表现为 verify error。所以用 Lets Encrypt 就直接填fullchain.pem别去填cert.pem用阿里云下载的 Nginx 包解压后会有full_chain.pem和privkey.pem两个文件对应填到ssl_certificate和ssl_certificate_key用在线检测工具比如 SSL Labs后面会提到可以非常直观地看到证书链是否完整。4. cer、pem、pfx 这些格式别被绕晕一次讲清转换与部署4.1 这些格式到底谁是谁做证书部署绕不开格式转换。很多人第一次看到.cer、.crt、.pem、.pfx、.jks直接被绕晕其实它们之间的关系没那么复杂。扩展名本质通常包含内容常见场景.cer / .crt证书编码可能是 DER 或 PEM只有证书没有私钥Windows、各类平台下载.pemBase64 文本内容可以多样证书、私钥、证书链都可能Nginx、Apache、多数 Linux 服务.key私钥文件编码可能是 PEM 或 DER只有私钥各类服务端配置.pfx / .p12PKCS#12 打包容器证书 私钥可能带中间证书IIS、Tomcat、Windows 导入.jksJava KeyStore证书 私钥Java 专有格式老版本 Tomcat、Java 应用最关键的认知是PEM 其实是“文本编码方式”PFX 是“打包容器”JKS 是“Java 专用仓库”。同一份证书材料可以在这几种形态之间来回转换理论上不丢信息实际操作里大多数人卡在命令记不住和编码格式识别上。4.2 只有cer没有私钥转不出能用的pfx很多人在 Tomcat 部署时拿到一个.cer文件然后尝试转换成.pfx结果各种报错。这里有一个必须说清楚的规则只有证书cer没有配套私钥key永远转不出能用于服务器部署的 pfx。因为 pfx 里的核心是私钥公钥证书本身是公开的谁都可以下载。服务器端做 TLS 握手必须持有私钥只有证书没有私钥等于只有指纹没有本人。所以从平台下载证书时除非平台明确给了私钥文件否则那个 cer 只能用于“查看证书信息”或“导出给别人验证”不能用于服务器部署。正常转换的前提是你手里同时有证书文件和私钥文件。比如你在阿里云下载证书时选了 Nginx 类型拿到full_chain.pem和privkey.pem这时想转成 Tomcat 用的 pfx命令如下# 先确认证书和私钥是 PEM 编码文本文件第一行是 BEGIN CERTIFICATE / BEGIN PRIVATE KEY $ file cert.pem key.pem # 转换成 pfx过程中需要设置导出密码后面配置 Tomcat 要填 $ openssl pkcs12 -export \ -out server.pfx \ -inkey privkey.pem \ -in full_chain.pem \ -name tomcat如果你拿到的 cer 是 DER 二进制编码浏览器或 Windows 可以直接打开但 openssl 处理前需要先转成 PEM# DER - PEM $ openssl x509 -inform DER -in cert.cer -out cert.pem转换完成后可以用这条命令检查 pfx 里的内容确认证书和私钥是否齐全、匹配$ openssl pkcs12 -in server.pfx -info -noout这里会提示输入密码输入后能看到bag attributes、subject、issuer等信息。重点确认里面有friendlyName: tomcat对应的私钥条目同时证书的域名和你要部署的域名一致。4.3 Tomcat 装载 PFX 的配置与验证拿到 server.pfx 之后把它放到 Tomcat 的配置目录比如${CATALINA_HOME}/conf/下然后修改conf/server.xml。对于 Tomcat 8.5 以上的版本推荐直接使用 Java 标准 SSL 连接器配置如下Connector port8443 protocolHTTP/1.1 SSLEnabledtrue maxThreads150 schemehttps securetrue keystoreFileconf/server.pfx keystorePass你的密码 keystoreTypePKCS12 clientAuthfalse sslProtocolTLS /关键属性就三个keystoreFile指向 pfx 文件路径keystorePass填转换时设置的密码keystoreType必须写PKCS12。如果这两个地方错了最常见的报错是java.io.IOException: keystore password was incorrect java.security.KeyStoreException: Unrecognized Java KeyStore format前者是密码不对后者通常是文件类型和keystoreType不匹配。修改完成后重启 Tomcat然后用 curl 或 openssl 验证# 检查8443端口 HTTPS 握手是否能拿到证书 $ echo | openssl s_client -connect localhost:8443 -servername example.com 2/dev/null | openssl x509 -noout -subject -dates能看到subjectCN example.com并且notAfter日期还是未来就说明 pfx 部署成功了。5. vsftpd 配置 SSL 证书的隐藏要求与翻车现场5.1 vsftpd 需要什么样的证书文件VSFTPD 是老牌 FTP 服务器了很多人还用它做文件传输配合 TLS 加密后就是 FTPS。它加载证书用的是 OpenSSL 库所以只要 PEM 格式的文件就能用不需要 pfx也不需要 jks。但有几个隐藏要求网上很多教程没说清楚证书和私钥可以分开两个文件分别用rsa_cert_file和rsa_private_key_file指定也可以把证书和私钥拼在同一个 PEM 文件里两个配置项指向同一个文件私钥文件权限必须严格控制。VSFTPD 是 root 启动、后续降权运行但它读私钥是在启动阶段用 root 读的所以私钥权限设成600、属主 root 最稳妥。我见过有人把私钥权限改成644后 VSFTPD 直接拒绝加载OpenSSL 层面对私钥文件权限有安全检查过不了就会报错。证书过期后VSFTPD 还能正常启动但所有要求 TLS 的客户端会在握手阶段失败日志里出现一行类似SSL_accept: 14094415 ...或者certificate has expired的错误。这种故障隐蔽性很强因为 FTP 服务本身是“通”的普通连接也能建立但一启用隐式 TLS 或者显式 TLS 就废。5.2 一份可以照抄的 vsftpd.conf SSL 配置我整理一份当前环境下比较稳妥的 VSFTPD SSL 配置ssl_enableYES rsa_cert_file/etc/vsftpd/ssl/vsftpd-cert.pem rsa_private_key_file/etc/vsftpd/ssl/vsftpd-key.pem # 匿名用户不允许走加密也不允许传数据 allow_anon_sslNO force_local_data_sslYES force_local_logins_sslYES # 关闭空密码用户的SSL要求避免某些客户端被卡住 require_ssl_reuseNO # 严格限定 TLS 版本禁用古老的 SSLv2/SSLv3 ssl_tlsv1YES ssl_sslv2NO ssl_sslv3NO重点解释几个配置项force_local_logins_sslYES本地用户登录时必须走 TLS 加密防止密码在网络上裸奔force_local_data_sslYES数据传输也必须加密require_ssl_reuseNO这个必须解释一下。VSFTPD 默认要求客户端复用 SSL 会话但 FileZilla 等主流客户端对这个特性支持得不好不关掉的话会出现“可以登录但列目录或传输文件时断开”的诡异问题。这个配置一开始直接害我排查了大半天后来查到是 session reuse 的坑关掉后一切正常。5.3 三个高频 SSL 报错与定位报错/现象常见原因处理方式500 OOPS: SSL: cannot load RSA private key私钥路径错、权限不对、证书和私钥不匹配检查rsa_private_key_file路径用openssl rsa -in key.pem -check校验私钥用openssl x509 -in cert.pem -noout -modulus和私钥 modulus 对比客户端能连但登录/传输瞬间断开require_ssl_reuseYES导致客户端会话复用不兼容配置里加require_ssl_reuseNO握手失败日志有certificate has expired证书过期更新证书并重启 vsftpd同时检查客户端有没有缓存旧证书这里额外提醒一句更新 VSFTPD 证书之后务必重启服务不是 reload 一下就行。VSFTPD 很多版本的配置和证书都是在启动时加载的reload 不一定生效。我一般是这样做的$ sudo systemctl restart vsftpd $ sudo tail -f /var/log/vsftpd.log然后拿着 FileZilla 连接测试用openssl s_client -connect your-server:21 -starttls ftp也能确认证书有没有换成功。6. 一套最简单的检查与巡检方法把“忘记续期”变成小概率事件6.1 随手可查证书有效期检查远程站点的证书$ echo | openssl s_client \ -servername example.com \ -connect example.com:443 2/dev/null \ | openssl x509 -noout -dates输出两行notBeforeJan 1 00:00:00 2025 GMT notAfterApr 1 00:00:00 2025 GMT检查本地证书文件$ openssl x509 -in /path/to/cert.pem -noout -dates如果是 pfx 或 jks稍微多一步# pfx 需要先导出证书或者直接看 pkcs12 信息 $ openssl pkcs12 -in server.pfx -clcerts -nokeys -out /tmp/cert.pem $ openssl x509 -in /tmp/cert.pem -noout -dates这套命令跑完你就能算出还剩多少天到期。顺手看一眼服务器系统时间$ date $ timedatectl如果发现 NTP 没开启先把这个隐患解决掉否则证书时间校验会被系统时钟拖下水。6.2 写一个批量巡检脚本服务器多的话我建议写个几行的 bash 脚本放进 crontab 每天跑。下面这个脚本会检查一批域名输出证书的过期时间和服务状态#!/bin/bash check_domain() { local d$1 local port${2:-443} local enddate local exp_epoch local now_epoch local days_left enddate$(echo | openssl s_client \ -servername $d \ -connect $d:$port 2/dev/null \ | openssl x509 -noout -enddate 2/dev/null \ | cut -d -f2) if [ -z $enddate ]; then echo $d:$port 获取证书失败 return fi exp_epoch$(date -d $enddate %s) now_epoch$(date %s) days_left$(( (exp_epoch - now_epoch) / 86400 )) echo $d:$port 剩余 ${days_left} 天 ($enddate) } for domain in example.com api.example.com ftp.example.com; do check_domain $domain done把这个脚本放到/usr/local/bin/check_cert.sh然后加一个 crontab0 9 * * * /bin/bash /usr/local/bin/check_cert.sh /var/log/cert_check.log 21每天看一眼日志或者把输出重定向到监控告警里到期前 30 天开始提醒。如果你用的云平台有云监控、事件服务也可以直接把检查结果接进去做成更自动化的告警。6.3 我的个人习惯讲了这么多理论和命令最后说几个我自己的实际习惯供参考申请证书当天就在日历里设提醒提前 30 天和提前 7 天各一次。免费的 90 天证书一年至少四个周期日历提醒是最低成本的保底方案。别把所有站点部署在同一张证书的到期日上。如果都是 Lets Encrypt 自动续期还好如果是手动续期尽量错开时间避免某个月突然要换五六张证书。acme.sh 配合 DNS API 是更省心的替代方案。如果你不想装 certbot 那一套acme.sh 支持通过 DNS API 自动完成域名验证签发后还能定义 install-cert 钩子自动重载 Nginx、Tomcat。对阿里云、Cloudflare 等 DNS 服务商都有现成脚本设置一次之后也是全自动。每个季度抽十分钟用 SSL Labs 跑一次全站 HTTPS 评分能一次性看到证书链、TLS 协议版本、弱密码套件等细节。这个习惯能帮你把“证书有效期”之外的问题也提前暴露出来。我在实际维护中发现SSL 证书管理最危险的时间点不是“忘了续期”的那一天而是第一次手动续期成功之后的松懈。第一次坑爬出来之后大多数人会想“我都知道怎么换了下次提前换就行”结果第二次还是会在某个凌晨被用户提醒。所以我的原则是任何能用脚本、定时任务、日历提醒自动化的事绝不依赖自己的记性。花十分钟把检查机制搭好远比每年临到头来折腾几次从容得多。
返回列表