3. 核心模块拆分:证书申请、签发、吊销、存储与查询

说实话,很多团队做 PKI 系统,上来就撸代码。结果呢?三个月后发现代码拧成一团麻花。证书申请和签发混在一起,吊销逻辑塞在存储层,查询接口慢得像蜗牛。

我个人习惯,动手前先把模块边界画清楚。今天我们就聊聊这五个核心模块——申请、签发、吊销、存储、查询。每个模块该干什么、不该干什么,边界在哪,我会结合踩过的坑来讲。

3.1 证书申请模块:入口的守门人

证书申请模块,说白了就是用户和系统之间的第一道门。用户提交 CSR(证书签名请求),系统做校验,然后决定要不要往下走。

这个模块的核心职责有三块:

  • 身份验证:确认申请者确实是他声称的那个人。我见过一个项目,这里没做严格校验,结果有人冒用他人邮箱申请了证书,差点出安全事故。
  • CSR 校验:检查 CSR 格式、公钥算法、域名合法性。嗯,这里要注意,有些 CSR 里会带奇怪的扩展字段,必须过滤掉。
  • 策略匹配:判断申请是否符合证书策略(比如有效期不能超过 90 天、必须包含 SAN 等)。
我的经验:申请模块最好做成无状态的。每次请求都独立校验,不要依赖会话状态。这样方便水平扩展,也容易做限流。

我曾经遇到一个案例:申请模块里混了签发逻辑,结果每次申请都要等签发完成才返回。高峰期时,申请队列堵死,用户超时重试,系统雪崩。后来我们把申请和签发拆成异步流程,问题才解决。

3.2 证书签发模块:核心中的核心

签发模块是 PKI 的心脏。它接收经过校验的申请,生成证书,然后用 CA 私钥签名。

这里有几个关键点:

  • 私钥保护:CA 私钥绝对不能出 HSM(硬件安全模块)或 KMS(密钥管理服务)。我见过有人把私钥文件放在磁盘上,还忘了加密——这等于把大门钥匙挂在门上。
  • 序列号管理:每个证书必须有唯一序列号。分布式环境下,序列号生成不能依赖数据库自增,否则会冲突。我建议用雪花算法或 Redis 原子递增。
  • 签名性能:RSA 2048 签名大约每秒几百次,ECDSA 可以到几千次。如果量很大,考虑用多台签名服务器 + 负载均衡。
算法 签名速度(次/秒) 推荐场景
RSA 2048 ~300 兼容性要求高
ECDSA P-256 ~2000 性能优先
Ed25519 ~5000 现代系统
避坑指南:签发模块一定要做幂等。同一个申请 ID 重复提交,不能签发两张不同的证书。我曾经因为没做幂等,导致同一个 CSR 被签了两次,吊销时还得吊销两张,麻烦得很。

3.3 证书吊销模块:紧急刹车

证书吊销,说白了就是给证书判死刑。私钥泄露、域名变更、员工离职——这些场景都需要吊销证书。

吊销模块要处理两件事:

  • CRL(证书吊销列表):定期发布的吊销清单。CRL 不能太大,否则下载慢。我建议 CRL 超过 10MB 就分片。
  • OCSP(在线证书状态协议):实时查询证书状态。OCSP 响应必须快,一般要求在 100ms 以内。

你想想看,如果吊销模块挂了,已经泄露的证书还在有效期内,攻击者就能继续用。所以吊销模块必须高可用,我一般会部署至少两个实例,一个主一个备。

关键设计:吊销操作要记录审计日志。谁吊销的、什么时候、什么原因——这些信息在合规审计时必不可少。

3.4 证书存储模块:数据的保险箱

存储模块负责把证书、私钥、CSR、吊销记录等数据持久化。这里的选择很多,但核心原则只有一个:安全第一,性能第二。

我常用的分层存储策略:

  • 热数据:最近 30 天签发的证书,放在 Redis 或内存缓存里,查询快。
  • 温数据:30 天到 1 年的证书,放在 MySQL 或 PostgreSQL 里,做好索引。
  • 冷数据:超过 1 年的证书,归档到对象存储(如 S3、MinIO),只保留元数据在数据库里。

嗯,这里要注意:私钥绝对不能明文存。必须用 KMS 加密后再存,而且加密密钥和存储位置要分开。我曾经见过一个团队,私钥和加密密钥放在同一个数据库里——那加密还有什么意义?

3.5 证书查询模块:用户的望远镜

查询模块看起来简单,其实最容易出问题。用户要查证书、查吊销状态、查历史记录——如果查询慢,整个系统的体验就毁了。

查询模块的设计要点:

  • 索引优化:按序列号、域名、申请人、有效期等字段建索引。我建议用覆盖索引,避免回表查询。
  • 缓存策略:热点证书(比如最近签发的)缓存到 Redis,设置合理的 TTL。注意缓存和数据库的一致性,我一般用 Cache-Aside 模式。
  • 分页与限流:查询接口必须做分页,防止一次查几十万条数据把数据库打崩。同时要做限流,防止恶意爬虫。
我的习惯:查询模块单独部署,不要和签发模块混在一起。这样查询压力再大,也不会影响证书签发。

3.6 模块关系图

下面这张图展示了五个模块之间的协作关系。你仔细看,申请和签发是异步的,存储是中心化的,查询和吊销都依赖存储层。

证书系统核心模块关系图 证书系统核心模块关系图 证书申请模块 证书签发模块 证书吊销模块 证书存储模块 证书查询模块 异步消息 写入 写入 读取 说明: 申请模块与签发模块通过异步消息解耦,避免阻塞。 存储模块作为数据中枢,所有模块都依赖它读写数据。

3.7 模块拆分的原则

最后,我总结几条模块拆分的经验:

  • 单一职责:每个模块只做一件事。申请模块不签发,签发模块不存储,存储模块不查询。
  • 异步解耦:模块之间通过消息队列通信,不要直接调用。这样即使某个模块挂了,其他模块还能继续工作。
  • 独立部署:每个模块可以独立部署、独立扩缩容。申请模块压力大,就多部署几个实例;查询模块压力小,就少部署几个。
  • 统一监控:每个模块都要暴露健康检查和指标(如 QPS、延迟、错误率)。我习惯用 Prometheus + Grafana 做统一监控。
核心思想:模块拆分不是越细越好,而是找到合适的粒度。太粗了耦合严重,太细了管理成本高。我个人觉得,五个模块对于证书系统来说刚刚好。