惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 司徒正美
T
The Blog of Author Tim Ferriss
雷峰网
雷峰网
S
Secure Thoughts
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
G
GRAHAM CLULEY
MongoDB | Blog
MongoDB | Blog
WordPress大学
WordPress大学
M
MIT News - Artificial intelligence
Martin Fowler
Martin Fowler
C
Cyber Attacks, Cyber Crime and Cyber Security
I
Intezer
A
About on SuperTechFans
Hugging Face - Blog
Hugging Face - Blog
T
Threatpost
S
Securelist
T
Tenable Blog
博客园_首页
P
Privacy International News Feed
Cisco Talos Blog
Cisco Talos Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
C
CXSECURITY Database RSS Feed - CXSecurity.com
小众软件
小众软件
美团技术团队
Project Zero
Project Zero
The Cloudflare Blog
L
Lohrmann on Cybersecurity
The Register - Security
The Register - Security
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 热门话题
C
CERT Recently Published Vulnerability Notes
C
Cybersecurity and Infrastructure Security Agency CISA
有赞技术团队
有赞技术团队
IT之家
IT之家
A
Arctic Wolf
Scott Helme
Scott Helme
Latest news
Latest news
T
Tailwind CSS Blog
Jina AI
Jina AI
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements
Cyberwarzone
Cyberwarzone
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
S
Schneier on Security
Y
Y Combinator Blog

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
如何用cfssl初始化根CA和中间CA - 少数派
2025-01-27 · via 少数派

前言

本文翻译并改编自Initializing a root and intermediate CA with cfssl

介绍

在本文中,我将使用 cfssl 在本地初始化根 CA 和中间 CA,以及保存当前 PKI 状态和运行 API 所需的数据库

根 CA 密钥稍后必须保持脱机状态,即备份到安全地方然后删除pki服务器上的证书,而我们将使用中间 CA 进行日常证书签名。这样,如果中间 CA 遭到入侵,我们可以撤销它并重新开始。这也将允许我们创建多个中间 CA,如果一个中间 CA 遭到入侵,我们可以将其提供给组织的不同实体,而不会撤销唯一的颁发者。


cfssl 是由 Cloudflare 创建并用 Go 编写的 PKI 工具包。具体按照如下:

安装 Go 1.16+

使用命令行go install github.com/cloudflare/cfssl/cmd/...@latest

找到生成的存储到 $home/go/bin 的可执行程序或库文件,并且复制到/bin


Caddy是一款基于Go语言编写的强大且可扩展的平台,可以给你的站点、服务和应用程序提供服务。

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

配置

数据库

cfssl 可以将其状态保存在数据库中。支持三种数据库提供程序:mysql、pgsql 和 sqlite。为方便起见,我将在此处使用 sqlite,但您可能应该在生产环境中使用其他两个之一。使用数据库让我们可以使用 OCSP 相关的命令,与证书签名相关的所有内容也将存储证书。

用于初始化数据库的配置可以在 cloudflare/cfssl/certdb/sqlite

我在同一个文件 db/definition.sql 中分解了两个初始化脚本:

CREATE TABLE certificates (
  serial_number            blob NOT NULL,
  authority_key_identifier blob NOT NULL,
  ca_label                 blob,
  status                   blob NOT NULL,
  reason                   int,
  expiry                   timestamp,
  revoked_at               timestamp,
  pem                      blob NOT NULL,
  issued_at                timestamp,
  not_before               timestamp,
  metadata                 text,
  sans                     text,
  common_name              text,
  PRIMARY KEY(serial_number, authority_key_identifier)
);

CREATE TABLE ocsp_responses (
  serial_number            blob NOT NULL,
  authority_key_identifier blob NOT NULL,
  body                     blob NOT NULL,
  expiry                   timestamp,
  PRIMARY KEY(serial_number, authority_key_identifier),
  FOREIGN KEY(serial_number, authority_key_identifier) REFERENCES certificates(serial_number, authority_key_identifier)
);

然后,我为这两个 CA 创建了一个数据库:

sqlite3 db/root-certstore.db < db/definition.sql
sqlite3 db/intermediate-certstore.db < db/definition.sql

cfssl 需要一些配置才能工作。

首先,我必须创建配置文件,以便他能够访问我刚刚创建的数据库:

mkdir -p {root,intermediate}/config
cat <<EOF > root/config/db.json
{
    "driver": "sqlite3",
    "data_source": "db/root-certstore.db"
}
EOF
cat <<EOF > intermediate/config/db.json
{
    "driver": "sqlite3",
    "data_source": "db/intermediate-certstore.db"
}
EOF

然后,我必须定义我的 CA 将使用的配置文件。现在有几件事需要考虑,即使它在我可以托管 PKI 之前不会使用:

我的 CA 将托管在哪里;

CRL 将托管在哪里;

什么 URI 将为 OCSP 响应程序提供服务。

我选择在域名 pki.xxxxx.top 下为 PKI 提供服务。以下是我将使用的 uri 列表:

  • http://pki.xxxxx.local/root/ca
  • http://pki.xxxxx.local/root/crl
  • http://pki.xxxxx.local/root/ocsp
  • http://pki.xxxxx.local/intermediate/ca
  • http://pki.xxxxx.local/intermediate/crl
  • http://pki.xxxxx.local/intermediate/ocsp

这些信息稍后将作为签名证书中的扩展提供。我们来看看 google.com 背后的证书:

echo | \
  openssl s_client \
    -showcerts \
    -servername google.com \
    -connect google.com:443 2>/dev/null | \
  openssl x509 \
    -inform pem \
    -noout -text

# Certificate:
#     Data:
#         ...
#         X509v3 extensions:
#             ...
#             Authority Information Access:
#                 OCSP - URI:http://ocsp.pki.goog/gts1c3
#                 CA Issuers - URI:http://pki.goog/repo/certs/gts1c3.der
#             X509v3 CRL Distribution Points:
#                 Full Name:
#                   URI:http://crls.pki.goog/gts1c3/moVDfISia2k.crl

这些嵌入在证书中的扩展是验证过程的一部分。

cfssl 使用定义签名配置文件的 JSON 文件等。签名配置文件是用于对某种证书进行签名的预定义参数集。我之前写过关于仅对中间 CA 进行签名的根 CA。它的配置文件为:

{
  "signing": {
    "default": {
      "crl_url": "http://pki.xxxxx.top/root/crl",
      "ocsp_url": "http://ocsp.xxxxx.top/root/ocsp",
      "issuer_urls": [
        "http://pki.xxxxx.top/root/ca"
      ],
      "expiry": "8760h"
    },
    "profiles": {
      "intermediate": {
        "usages": [
          "signing",
          "digital signature",
          "key encipherment",
          "cert sign",
          "crl sign"
        ],
        "ca_constraint": {
          "is_ca": true,
          "max_path_len": 0,
          "max_path_len_zero": true
        },
        "expiry": "87600h"
      },
      "ocsp": {
        "usages": [
          "digital signature",
          "ocsp signing"
        ],
        "expiry": "26280h"
      }
    }
  }
}

在上面的 json 配置中,我定义了两个配置文件,一个是中间配置文件,用于签署其他 CA 证书,另一个是 ocsp,用于签署 OCSP 响应程序使用的证书。.signing.default 标识符 用于设置配置文件之间共享的参数。

中间 CA 将主要用于签署服务器的证书和客户端身份验证。cfssl只能签出不被浏览器信任的证书,因此我选择使使用服务器配置文件签名的证书具有长期性:

{
  "signing": {
    "default": {
      "crl_url": "http://pki.xxxxx.top/intermediate/crl",
      "ocsp_url": "http://ocsp.xxxxx.top/intermediate/ocsp",
      "issuer_urls": [
        "http://pki.xxxxx.top/intermediate/ca"
      ],
      "expiry": "8760h"
    },
    "profiles": {
      "client": {
        "usages": [
          "signing",
          "digital signing",
          "key encipherment",
          "client auth"
        ],
        "expiry": "8760h"
      },
      "server": {
        "usages": [
          "signing",
          "digital signing",
          "key encipherment",
          "server auth"
        ],
        "expiry": "2190h"
      },
      "ocsp": {
        "usages": [
          "digital signature",
          "ocsp signing"
        ],
        "expiry": "26280h"
      }
    }
  }
}

这些配置文件将保存在 root/config/profiles.jsonintermediate/config/profiles.json

Here are the two definitions I’ll use, respectfully in root/config/init.json and intermediate/config/init.json:
以下是我将使用的两个定义,分别在 root/config/init.jsonintermediate/config/init.json 中:

{
  "CN": "xxxxx Root CA Certificate",
  "CA": {
    "expiry": "87600h"
  },
  "key": {
    "algo": "rsa",
    "size": 4096
  },
  "names": [{
    "C":  "CN",
    "ST": "beijing",
    "L":  "sihuan",
    "O":  "xxxxx"
  }]
}
{
  "CN": "xxxxx Intermediate CA Certificate",
  "CA": {
    "expiry": "87600h"
  },
  "key": {
    "algo": "rsa",
    "size": 4096
  },
  "names": [{
    "C":  "CN",
    "ST": "beijing",
    "L":  "sihuan",
    "O":  "xxxxx"
  }]
}

创建私钥和证书非常简单。

cfssl toolkit 的 genkey 命令将创建一个私钥、一个签名请求,并对其进行自签名。

cfssl genkey -initca root/config/init.json | cfssljson -bare root/ca

cfssl genkey 返回一个包含三个键的 JSON:certcsrkeycfssljson 将创建这三个文件。

创建中间 CA 的过程相同。我只需丢弃证书,并使用根 CA 签署 CSR:

cfssl genkey -initca intermediate/config/init.json | cfssljson -bare intermediate/ca
rm intermediate/ca.pem
cfssl sign \
    -ca root/ca.pem \
    -ca-key root/ca-key.pem \
    -config root/config/profiles.json \
    -profile intermediate \
    -db-config root/config/db.json \
    intermediate/ca.csr | cfssljson -bare intermediate/ca

现在,您的当前目录中应该有以下文件:

tree

# .
# ├── db
# │   ├── definition.sql
# │   ├── intermediate-certstore.db
# │   └── root-certstore.db
# ├── intermediate
# │   ├── ca.csr
# │   ├── ca-key.pem
# │   ├── ca.pem
# │   └── config
# │       ├── db.json
# │       ├── init.json
# │       └── profiles.json
# └── root
#     ├── ca.csr
#     ├── ca-key.pem
#     ├── ca.pem
#     └── config
#         ├── db.json
#         ├── init.json
#         └── profiles.json
#
# 6 directories, 15 files

根数据库中的 SQL 请求显示还存储了中间 CA 证书。

$ sqlite3 db/root-certstore.db "SELECT common_name FROM certificates;"
#xxxxx Intermediate CA Certificate

几乎完成,剩下的唯一任务是为 CA 的 ocsp 响应程序生成证书和密钥,并生成 CRL。

cat <<EOF > root/config/ocsp.json
{
  "CN": "xxxxx Root OCSP Certificate",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [{
    "C":  "CN",
    "ST": "beijing",
    "L":  "sihuan",
    "O":  "xxxxx"
  }]
}
EOF
cfssl gencert \
    -ca root/ca.pem \
    -ca-key root/ca-key.pem \
    -config root/config/profiles.json \
    -profile ocsp \
    -db-config root/config/db.json \
    root/config/ocsp.json | cfssljson -bare root/ocsp
cat <<EOF > intermediate/config/ocsp.json
{
  "CN": "xxxxx Intermediate OCSP Certificate",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [{
    "C":  "CN",
    "ST": "beijing",
    "L":  "sihuan",
    "O":  "xxxxx"
  }]
}
EOF
cfssl gencert \
    -ca intermediate/ca.pem \
    -ca-key intermediate/ca-key.pem \
    -config intermediate/config/profiles.json \
    -profile ocsp \
    -db-config intermediate/config/db.json \
    intermediate/config/ocsp.json | cfssljson -bare intermediate/ocsp

证书吊销列表是应该(可能)定期更新的文件。我不确定客户端如何实现此功能的缓存机制,因为 CRL 公布两个日期:上次更新和下次更新。这意味着,如果他们将其缓存到下一个更新日期,则他们可能会错过被吊销的证书,直到达到缓存 ttl 为止。CRL 也使用其 CA 的密钥进行签名,这意味着如果您想使根 CA 的私有密钥保持离线状态,这可能会非常棘手。我见过不同的方法:有些创建 CRL 会在 CA 过期时发布下一次更新日期,并且仅在需要时手动刷新它,而另一些则创建每周 CRL,这是 cfssl 的默认设置:

cfssl crl -h

# ...
#   -expiry=168h0m0s: time from now after which the CRL will expire (default: one week)

我选择执行后者,因为我无法想象创建每周 CRL 而不自动执行任务,因此我稍后会将私钥存储在我管理的 Hashicorp Vault 实例中,这对于我的家庭实验室来说是一个可以接受的风险。

cfssl crl 输出一个没有页眉/页脚和换行符的 PEM CRL,所以我必须处理它:

echo "-----BEGIN X509 CRL-----" > root/crl.pem
cfssl crl \
    -ca root/ca.pem \
    -ca-key root/ca-key.pem \
    -db-config root/config/db.json | fold -w 64 >> root/crl.pem
echo "-----END X509 CRL-----" >> root/crl.pem
echo "-----BEGIN X509 CRL-----" > intermediate/crl.pem
cfssl crl \
    -ca intermediate/ca.pem \
    -ca-key intermediate/ca-key.pem \
    -db-config intermediate/config/db.json | fold -w 64 >> intermediate/crl.pem
echo "-----END X509 CRL-----" >> intermediate/crl.pem

您可以使用 openssl 命令检查 CRL:

openssl crl -inform PEM -text -noout -in root/crl.pem
# Certificate Revocation List (CRL):
#         Version 2 (0x1)
#         Signature Algorithm: sha256WithRSAEncryption
#         Issuer: C = CN, ST = beijing, L = sihuan, O = xxxxx, CN = xxxxx Root CA Certificate
#         Last Update: Jun 17 08:39:31 2024 GMT
#         Next Update: Jun 24 08:39:31 2024 GMT
#         CRL extensions:
#             X509v3 Authority Key Identifier:
#                 D0:33:EF:44:95:BD:B2:0B:61:6D:B8:E0:19:95:6D:80:90:AA:3F:A6
# No Revoked Certificates.
#     Signature Algorithm: sha256WithRSAEncryption
#     Signature Value:
#         7b:91:89:00:41:d4:80:72:0b:af:db:7d:e5:19:cd:d0:29:3b:

现在我们使用caddy作为文件服务器发布CRl和CAIssuers的发布点,并却作为ocsp和API的服务的反向代理服务器。

cp root/Ca.crl /usr/share/caddy/root/
cp root/Ca.pem /usr/share/caddy/root/
cp intermediate/Ca.crl /usr/share/caddy/intermediate/
cp intermediate/Ca.pem /usr/share/caddy/intermediate/

 编辑Caddyfile。

pki.xxxxx.top {
        root * /usr/share/caddy
        encode gzip
        file_server
        tls your_email
}
http://ca.xxxxx.top:8888 {
        reverse_proxy 127.0.0.1:7777
}

http://ocsp.xxxxx.top {
        reverse_proxy /intermediate/ocsp  127.0.0.1:8889
        reverse_proxy /root/ocsp  127.0.0.1:8890
}

现在,我应该已经拥有尝试签署证书、撤销证书等所需的一切。让我们启动 API:

cfssl serve \
      -ca=intermediate/ca.pem \
      -ca-key=intermediate/ca-key.pem \
      -responder=intermediate/ocsp.pem \
      -responder-key=intermediate/ocsp-key.pem \
      -db-config=intermediate/config/db.json \
      -config=intermediate/config/profiles.json \
      -port 7777
# 2024/06/17 10:55:12 [INFO] Initializing signer
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/newcert' is enabled
# 2024/06/17 10:55:12 [INFO] setting up key / CSR generator
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/newkey' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/ocspsign' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/info' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/gencrl' is enabled
# 2024/06/17 10:55:12 [INFO] bundler API ready
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/bundle' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/scan' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/revoke' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/certadd' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/sign' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/init_ca' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/scaninfo' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/certinfo' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/' is enabled
# 2024/06/17 10:55:12 [WARNING] endpoint 'authsign' is disabled: {"code":5200,"message":"Invalid or unknown policy"}
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/crl' is enabled
# 2024/06/17 10:55:12 [INFO] endpoint '/api/v1/cfssl/health' is enabled
# 2024/06/17 10:55:12 [INFO] Handler set up complete.
# 2024/06/17 10:55:12 [INFO] Now listening on 127.0.0.1:8888

您会注意到有关禁用 authsign API 端点的警告:我将在有关在 kubernetes 中提供 API 的文章中介绍这一点。它只是因为我没有在配置文件中设置任何身份验证方法而被禁用。如果您想按原样提供 API,则应仔细考虑谁将能够访问它。其他端点不会全部使用,并且还有一种方法可以禁用您不想使用或公开的端点。

在另一个终端中,我将使用 cfssl 创建密钥 CSR,并要求 API 背后的 CA 对 CSR 进行签名。

cfssl gencert \
      -remote="ca.xxxxx.top:8888" \
      -config=intermediate/config/profiles.json \
      -profile server \
      <(echo '
{
  "CN": "Test",
  "hosts": [
    "test.xxxxx.top"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [{
    "C":  "CN",
    "ST": "beijing",
    "L":  "sihuan",
    "O":  "xxxxx"
  }]
}') | \
    cfssljson -bare test
# 2024/06/17 11:17:28 [INFO] generate received request
# 2024/06/17 11:17:28 [INFO] received CSR
# 2024/06/17 11:17:28 [INFO] generating key: rsa-2048
# 2024/06/17 11:17:28 [INFO] encoded CSR

您可以在服务器端看到新日志:

# 2024/06/17 11:17:28 [INFO] signature request received
# 2024/06/17 11:17:28 [INFO] signed certificate with serial number 485014236354530765875959101436276396320072239922
# 2024/06/17 11:17:28 [INFO] wrote response
# 2024/06/17 11:17:28 [INFO] 127.0.0.1:46402 - "POST /api/v1/cfssl/sign" 200

您还可以使用 cfssl sign 命令对直接使用 openssl 创建的现有 CSR 进行签名。

cfssl gencert \
    -remote="ca.xxxxx.top:8888" \
    -config=intermediate/config/profiles.json \
    -profile server \
    test.crt | \
    cfssljson -bare test

让我们看看我之前所做的所有配置是否都正确:

openssl x509 -text -in test.pem
# Certificate:
#     Data:
#         Version: 3 (0x2)
#         Serial Number:
#             54:f4:ca:61:42:01:e9:0d:8a:ae:93:65:42:b1:66:37:5d:91:8b:32
#         Signature Algorithm: sha512WithRSAEncryption
#         Issuer: C = CN, ST = beijing, L = sihuan, O = xxxxxx, CN = xxxxxx Intermediate CA Certificate
#         Validity
#             Not Before: Jun 17 09:12:00 2024 GMT
#             Not After : Sep 16 15:12:00 2024 GMT
#         Subject: C = CN, ST = Pays de la Loire, L = sihuan, O = xxxxxx, CN = Test
#         Subject Public Key Info:
#             Public Key Algorithm: rsaEncryption
#                 Public-Key: (2048 bit)
#                 Modulus:
#                     00:c3:6e:f3:0a:21:ff:fa:be:10:11:48:63:60:1a:
#                     ...
#                     cf:f1
#                 Exponent: 65537 (0x10001)
#         X509v3 extensions:
#             X509v3 Key Usage: critical
#                 Digital Signature, Key Encipherment
#             X509v3 Extended Key Usage:
#                 TLS Web Server Authentication
#             X509v3 Basic Constraints: critical
#                 CA:FALSE
#             X509v3 Subject Key Identifier:
#                 33:29:48:E7:3A:B6:4E:90:92:1E:F7:F2:33:E6:1F:99:F2:42:5E:EF
#             X509v3 Authority Key Identifier:
#                 C8:80:42:BF:8B:0D:C9:9F:55:78:DD:56:E2:8B:1A:AE:57:49:37:1B
#             Authority Information Access:
#                 OCSP - URI:http://pki.xxxxxx.top/intermediate/ocsp
#                 CA Issuers - URI:http://pki.xxxxxx.top/intermediate/ca
#             X509v3 Subject Alternative Name:
#                 DNS:test.xxxxxx.top
#             X509v3 CRL Distribution Points:
#                 Full Name:
#                   URI:http://pki.xxxxxx.top/intermediate/crl
#     Signature Algorithm: sha512WithRSAEncryption
#     Signature Value:
#         a2:e2:9a:dd:83:57:ff:4e:3c:92:b3:cc:78:1b:4c:0e:f0:da:
#         ...
#         0e:3c:54:81:ef:04:9b:af
# -----BEGIN CERTIFICATE-----
# MIIFsDCCA5igAwIBAgIUVPTKYUIB6Q2KrpNlQrFmN12RizIwDQYJKoZIhvcNAQEN
# ...
# qVBNU7XEsx7X+n4rDjxUge8Em68=
# -----END CERTIFICATE-----

服务器配置文件已正确使用:有效期为三个月,并且 OCSP、CA 和 CRL 终端节点正确。

让我们启动 OCSP 响应程序:

cfssl ocspserve -db-config intermediate/config/db.json -port 8889
# 2024/06/18 10:23:06 [INFO] Registering OCSP responder handler
# 2024/06/18 10:23:06 [INFO] Now listening on 127.0.0.1:8889

cfssl ocspserve -db-config root/config/db.json -port 8890
# 2024/06/18 10:24:06 [INFO] Registering OCSP responder handler
# 2024/06/18 10:24:06 [INFO] Now listening on 127.0.0.1:8889
openssl ocsp \
    -issuer <(cat intermediate/ca.pem) \
    -CAfile <(cat root/ca.pem) \
    -cert test.pem \
    -url http://ocsp.xxxxx.top/intermediate/ocsp

如果您立即运行此命令,您应该已经收到了错误 unauthorized。这是因为 cfssl 使用预签名的 ocsp 响应,这意味着每次我签署或吊销证书时,它都必须签署新的响应。

# Responder Error: unauthorized (6)

让我们刷新 OCSP 响应 - 它将存储在数据库中 - 然后重试 openssl ocsp 命令:

cfssl ocsprefresh \
    -ca intermediate/ca.pem \
    -responder intermediate/ocsp.pem \
    -responder-key intermediate/ocsp-key.pem \
    -db-config intermediate/config/db.json \
    -interval 336h
    #14day fresh next time of ocsp respander
# WARNING: no nonce in response
# Response verify OK
# test.pem: good
#   This Update: Jun 18 08:00:00 2024 GMT
#   Next Update: Jun 22 08:00:00 2024 GMT

您可以禁用有关参数 -no_nonce 不存在 nonce 的警告。如您所见,cfssl 使用预签名的 OCSP 响应,因此它们不能包含 nonces(这与 RFC 5019,第 4 节)。cfssl 项目不打算 support nonces 写在 cloudflare/cfssl/ocsp/responder.go#L336

现在让我们吊销证书。撤销 API 采用三个参数:

  • serial:以十进制表示的证书序列号(???);
  • authority_key_id:颁发密钥标识符,小写十六进制,不带分隔符;
  • reason:撤销的原因。

吊销的可能原因列在 RFC 5280,第 6.3.2 节。他们的 cfssl api 语法是用 cloudflare/cfssl/ocsp/ocsp.go#L26 编写的:

// to integers as defined in RFC 5280
var revocationReasonCodes = map[string]int{
    "unspecified":          ocsp.Unspecified,
    "keycompromise":        ocsp.KeyCompromise,
    "cacompromise":         ocsp.CACompromise,
    "affiliationchanged":   ocsp.AffiliationChanged,
    "superseded":           ocsp.Superseded,
    "cessationofoperation": ocsp.CessationOfOperation,
    "certificatehold":      ocsp.CertificateHold,
    "removefromcrl":        ocsp.RemoveFromCRL,
    "privilegewithdrawn":   ocsp.PrivilegeWithdrawn,
    "aacompromise":         ocsp.AACompromise,
}

在前面使用 openssl x509 的证书描述中,我可以看到其他两个参数的值:

序列号 54:f4:ca:61:42:01:e9:0d:8a:ae:93:65:42:b1:66:37:5d:91:8b:32 变为 485014236354530765875959101436276396320072239922 ;

颁发机构密钥标识符 C8:80:42:BF:8B:0D:C9:9F:55:78:DD:56:E2:8B:1A:AE:57:49:37:1B 将变为 c88042bf8b0dc99f5578dd56e28b1aae5749371b

curl -d '{
  "serial": "485014236354530765875959101436276396320072239922",
  "authority_key_id": "c88042bf8b0dc99f5578dd56e28b1aae5749371b",
  "reason": "cessationofoperation"
}' http://ca.xxxxx.top:8888/api/v1/cfssl/revoke
# {"success":true,"result":{},"errors":[],"messages":[]}

如您所见,没有为此终端节点提供任何身份验证,这与签名终端节点也至少提供 HMAC 身份验证作为 authsign 相反,因此按原样公开它不是一个好主意。

刷新缓存的 OCSP 响应后,openssl ocsp 命令会回答:

# Response verify OK
# test.pem: revoked
#   This Update: Jun 19 08:00:00 2024 GMT
#   Next Update: Jun 23 08:00:00 2024 GMT
#   Reason: cessationOfOperation
#   Revocation Time: Jun 19 08:25:56 2024 GMT

重新生成 CRL 文件后,openssl crl 输出:

# Certificate Revocation List (CRL):
#         Version 2 (0x1)
#         Signature Algorithm: sha256WithRSAEncryption
#         Issuer: C = CN, ST = beijing, L = sihuan, O = xxxxx, CN = xxxxx Intermediate CA Certificate
#         Last Update: Jun 19 08:46:10 2024 GMT
#         Next Update: Jun 26 08:46:10 2024 GMT
#         CRL extensions:
#             X509v3 Authority Key Identifier:
#                 C8:80:42:BF:8B:0D:C9:9F:55:78:DD:56:E2:8B:1A:AE:57:49:37:1B
# Revoked Certificates:
#     Serial Number: 54F4CA614201E90D8AAE936542B166375D918B32
#         Revocation Date: Jun 19 08:25:56 2024 GMT
#     Signature Algorithm: sha256WithRSAEncryption
#     Signature Value:
#         d6:3d:18:16:6c:6a:db:07:99:41:02:76:aa:4b:16:b5:da:bd:
#         ...
#         42:df:ef:c2:6f:17:6b:3c

从这两种方法中,我们都知道证书确实被吊销了。

已知问题

OCSP Stapling 问题:使用这种方式生成的证书可能无法在 Nginx 中使用 OCSP Stapling。

总结

在这篇文章中,我创建了一个根 CA 和一个中间 CA,能够签署然后吊销证书,并根据 OCSP 响应程序和 CRL 文件对其进行测试。一切都准备好了。