openssl ca:index.txt 中的无效到期日期及修复方法
index.txt 中有一行无法解析,CA 因此拒绝签发。请先复制 CA 状态,解码该条目,再与已签名的证书核对。
一次签发运行会停在 entry 22: invalid expiry date,而你面前的那份请求本身毫无问题。抱怨来自 CA 数据库的解析器,既不是 CSR,也不是最终会出示证书的 VPN 客户端。这个区别决定了后面的一切:损坏的是记录,所以修复应落在 CA 状态上,而不是请求或对端配置上。把这条带编号的条目就当成 index.txt 里的一行,问题便缩小为一个有边界、答案可验证的数据问题。
把这条错误读成对数据库的抱怨
每次 openssl ca 调用在签署任何东西之前,都会先遍历自己的文本数据库。每一行包含状态字母、到期时间戳、可选的吊销时间戳、十六进制序列号、文件名和主体。当某个字段无法解析时,命令会报告行号并停下,而不是去猜剩下的记录。
这种停下是保护性的,而不是吹毛求疵。工具读不懂的记录会让它无法判断请求的主体是否已存在、序列号是否已发出、下一个 CRL 该包含什么。越过坏行继续下去,恰恰会产生那种数月之后才浮现的隐性不一致,通常发生在一次谁也解释不清的吊销过程中。把这条消息当成对数据的抱怨,排查就始终留在 CA 目录里。
动手之前先复制 CA 状态
为整个 CA 目录做快照,而不只是数据库文件。索引、它的属性文件、序列号与 CRL 编号文件、私钥以及已签发的证书都会参与后续操作,而只恢复其中一个、漏掉其余的做法会制造第二起事故。有现成备份流程就用 CA 自己的流程,并在工作期间暂停定期签发。
记录失败命令实际加载的 OpenSSL 构建和配置文件。安装经常保留不止一个 CA 目录,这条错误只有对照所选配置指明的数据库才有意义。
找到该条目并解码它的日期字段
报告的行号从第一条记录数起,所以只要确认它上方没有空行或注释行,sed -n '22p' index.txt 就会落在出问题的那一条上。打印该行时让分隔符可见,因为制表符和行尾空格很常见,在普通编辑器窗口里却看不见。
到期字段是一个时间戳,宽度取决于年份。2049 年及以前的值按 UTCTime 写成 YYMMDDHHMMSSZ,更晚的值则改用四位年份的 GeneralizedTime。把两种形式混在一起、或在解析器现在要求四位的地方留着两位年份的记录,正好在这里失败。被中断的写入留下的截断字段也是如此。
要把记录与它所引用的证书对照,而不是与另一个 CA 的文件对照。已签名的证书才是有效期的权威来源,而且它本身就是一个可解析的对象。
依据已签名的证书修复该记录
用下面的命令读出真实日期:openssl x509 -in issued.pem -noout -dates -serial -subject。然后把 notAfter转换成你的构建为该年份写入的时间戳宽度,并在改动任何一个字符之前确认序列号与即将编辑的那条记录一致。
只改坏掉的那个字段,保留状态字母和序列号,其余部分一概不动。如果这条记录无法与任何已签名证书对上,就把它当作游离条目,走 CA 自己的吊销流程让它退役,而不是手工删行。
不要用裁剪日期去凑对年份的猜测来修复。两个字符的裁剪在不同构建之间不可移植,而且会悄悄改变数据库向将来的重复主体检查和 CRL 检查报告的有效期。当原始值无法恢复时,换一个新序列号重新签发,并写明旧的那条为何被放弃。审计痕迹是修复的一部分,不是围着它的文书工作。
离开之前验证签发与 CRL 生成
用同一份配置跑一次签发,确认它没有行号抱怨地跑完。然后用 openssl ca -gencrl -config openssl.cnf 生成一份 CRL 并检查它能被解析,因为受损记录往往在那里而不是在签名时暴露。
用下面的命令查看新证书:openssl x509 -noout -dates -serial,并与 CA 刚写入的那一行对比。确认状态字母和到期日期与证书一致,并确认 openssl verify -CAfile ca.pem issued.pem 无错返回。之后恢复定期签发并盯住第一次运行,因为第二条畸形记录会立刻冒出来。