11 个不稳定版本 (3 个重大更新)

0.6.1 2023年10月16日
0.6.0 2023年10月2日
0.5.2 2023年6月29日
0.5.0 2023年4月20日
0.3.3 2023年4月14日

#328 in 密码学

Download history 57/week @ 2024-07-29

每月下载量 57

MIT 许可证

445KB
9K SLoC

Rust 7K SLoC // 0.0% comments Python 2K SLoC // 0.0% comments Tera 122 SLoC SQL 84 SLoC // 0.2% comments

Björn - AS207960 ACME 服务器

Björn 本身不是一个完整的 CA,但它包含了完整 ACME CA 的许多构建模块。

在 Debian 上安装

apt install curl build-essential libssl-dev pkg-config protobuf-compiler libpq-dev
curl https://sh.rustup.rs -sSf | sh
source "$HOME/.cargo/env"
cargo install bjorn-acme

组件

Björn 由三个主要组件组成,并包括一个使用 Django 编写的示例 CA 后端。此 CA 后端**严禁在生产环境中使用**。它仅用于演示目的。

内部组件之间的通信通过 gRPC 进行。

组件包括

  • Björn - ACME 前端和最大的组件。它处理账户注册,并作为 CA 后端和 ACME 客户端之间的代理。
  • Benny - OCSP 响应者。它接收并响应来自 TLS 客户端的 OCSP 请求,并将请求路由到处理最终实体证书中使用的签名证书的后端。
  • Frida - ACME 验证器。它处理 CAA 检查,并验证 ACME http-01dns-01 挑战。

它们之间基本的工作关系图如下;

                          End user <-> CA boundary 
                                    |       *------------*
                                    |       | PostgreSQL | <--------------
                                    |       *------------*                \ 
                                    |                                      |
*-------------*                     |                                  *-------*
| ACME Client | ---ACME over HTTPS--|--                            --> | Björn | ------
*-------------*                     |  \    *-----------------*   /    *-------*       \   
                                    |   --> | TLS terminating | --                 gRPC |
                                    |   --> |  ingress proxy  | --                      |
*------------*                      |  /    *-----------------*   \    *-------*        |
| TLS Client | --OCSP over HTTP(S)--|--                            --> | Benny | ----   |
*------------*                      |                                  *-------*     \  |
                                    |                                            gRPC | |
                                    |                                                /  |
*-------------*    |  http-01  |    |      *-------*               *------------* <--   |
| User server | <--|   dns-01  |----|----- | Frida | <----gRPC---- | Backend CA |      /
*-------------*    |tls-alpn-01|    |      *-------*               *------------* <----

仓库布局

目录 内容
migrations SQL 数据库迁移
proto gRPC 互操作定义
python-ca 示例 CA 后端**严禁在生产环境中使用**
src 通用 Rust 源代码
src/bin/bjorn-acme.rs Björn 二进制文件
src/acme Björn 特定工具
src/bin/bjorn-ocsp.rs Benny 二进制文件
src/ocsp Benny 特定工具
src/bin/bjorn-validator.rs Frida 二进制文件
src/validator Frida 特定工具
templates Björn HTML 模板

实现 RFC

  • RFC 6960 - 在线证书状态协议 - OCSP
  • RFC 7638 - JSON Web Key (JWK) Thumbprint
  • RFC 8555 - 自动证书管理环境 (ACME)
  • RFC 8657 - 账户 URI 和自动证书管理环境 (ACME) 方法绑定证书授权 (CAA) 记录扩展
  • RFC 8659 - DNS 证书授权 (CAA) 资源记录
  • RFC 8737 - 自动证书管理环境 (ACME) TLS 应用层协议协商 (ALPN) 挑战扩展
  • RFC 8738 - 自动证书管理环境 (ACME) IP 标识符验证扩展
  • RFC 8954 - 在线证书状态协议 (OCSP) 非法扩展
  • draft-shoemaker-caa-ip-01 - IP 地址的证书授权 (CAA) 验证
  • draft-ietf-acme-onion-01 - 自动证书管理环境 (ACME) 对 ".onion" 专用域名名的扩展

注意:CAA iodef 尚未得到支持

设置

所有组件都使用 Rocket 配置系统 进行配置。请参阅链接的 Rocket 文档以了解配置监听端口和地址的详细信息。

Björn

Björn 应该设置在实现速率限制和 TLS 终止的 HTTP 代理后面。

示例 Rocket.toml

[development]
external_uri = "https://127.0.0.1:8000"
tos_uri = "https://as207960.net/assets/docs/AS207960_T_C_s.pdf"
tos_agreed_to_after = "2021-09-29T00:00:00Z"
website_uri = "https://as207960.net"
caa_identities = ["bjorn.as207960.net"]
ca_grpc_uri = "http://[::1]:50051"
template_dir = "templates/"
external_account_required = false
in_band_onion_caa_required = false

[[development.acme_issuers]]
cert_id = "a"
issuer_cert_file = "python-ca/ca-certs/intermediate-crt.pem"

[development.databases]
db = { url = "postgres://postgres@localhost/bjorn" }

配置说明。

external_uri

ACME 服务器可以通过外部最终用户访问的基本 URL,包括协议和端口。

tos_uri

ACME 服务器的服务条款和条件页面的 URL。最终用户应在他们的 ACME 客户端中看到此页面,并在继续之前同意它。

tos_agreed_to_after

一个 ISO8601 时间戳,对于在之前同意服务条款的账户,在继续请求之前将需要再次同意。如果 ToS 已更新且用户需要了解新/更新条款,则非常有用。

website_uri

运行 ACME 服务器的实体的主页。

caa_identities

哪些 CAA 发起者身份被 ACME 服务器识别为允许此 CA 发行证书。

ca_grpc_uri

CA 后端 gRPC URL。它应该实现以下部分中描述的协议。

template_dir

存储索引页(当 ACME 服务器未使用 ACME 客户端访问时)和更新 ToS 协议页面的 HTML 模板所在的目录路径。

external_account_required

此服务器是否要求所有账户注册使用 EAB。

in_band_onion_caa_required

此服务器是否仅支持 Tor 隐藏服务的带内 CAA。

acme_issuers

此服务器可以处理吊销请求的发行证书列表。

cert_id

后端 CA 用来识别发行证书的 ID。

issuer_cert_file

发行证书的 X.509 公钥文件(PEM 编码)的路径。

db.url

存储 ACME 账户的 PostgreSQL 数据库的连接 URL。

Benny

Benny 应该设置在尊重 Benny 设置的 Cache-ControlExpiresETag 等等 HTTP 头的缓存 HTTP 代理后面。

示例 Rocket.toml

[[development.ocsp_issuers]]
cert_id = "issuer-1"
issuer_cert_file = "python-ca/ca-certs/intermediate-crt.pem"
signer_pkcs12_file = "ocsp-signer.p12"
grpc_uri = "http://[::1]:50051"

配置说明。

ocsp_issuers

此响应者具有权威性的发行证书列表。

cert_id

后端 CA 用来识别发行证书的 ID。

issuer_cert_file

发行证书的 X.509 公钥文件(PEM 编码)的路径。

signer_pkcs12_file

包含用于签署OCSP响应的私钥、其相关公钥以及需要链接到根的任何证书的PKCS.12编码文件。签名证书应该是发行者证书直接(不推荐),或者是带有id-kp-OCSPSigning扩展关键用途属性的发行者证书。

grpc_uri

此发行者的CA后端gRPC URL。它应该实现以下部分描述的协议。

Frida

Frida应该在配置了本地DNSSEC验证解析器的机器上设置。DNSSEC验证对于CAA的完整性至关重要。

示例 Rocket.toml

[development]
caa_identities = ["bjorn.as207960.net"]
tor_storage = "./tor"

配置说明。

caa_identities

哪些CAA发行者身份被ACME服务器认可为允许由该CA签发(理想情况下与Björn上配置的相同)。

tor_storage

Frida将在此目录中存储Tor隐藏服务的缓存状态。此设置不是必需的。如果没有设置,Frida将不会连接到Tor网络,并且只能验证隐藏服务的带内CAA。

互操作协议描述

通用

错误

Björn理解基本的gRPC错误,例如未找到和内部服务器错误,并将它们转换为适合传输给客户端的错误。

为了获得更大的控制和具体性,后端CA还可以直接构建ACME错误以发送给客户端。《Error》类型基于ACME返回的JSON错误。

enum ErrorType {
  ...
}

message Error {
  ErrorType error_type = 1;
  string title = 2;
  uint32 status = 3;
  string detail = 4;
  google.protobuf.StringValue instance = 5;
  repeated Error sub_problems = 6;
  Identifier identifier = 7;
}

error_type字段包含在RFC 8555 § 6.7中定义的错误。应包含故障的概括摘要的title字段。应包含故障的合适HTTP错误代码的status字段。应包含故障的详细描述的detail字段。仅在返回userActionRequired字段时,应使用instance字段,并应包含一个URL以直接指导最终用户。可以使用sub_problems字段将故障分解成更小的故障。可以使用identifier字段来识别此故障与订单中的哪个标识符相关。

Björn

ValidateEAB

此函数用于检查客户端指定的外部账户绑定是否有效。后端应查找由kid字段指定的HMAC密钥,并使用指定的signature_method检查对signed_data字段的signature字段是否有效。

如果HMAC密钥不存在,或签名无效,后端必须返回一个结果,其中包含valid字段为false,否则必须返回true

CreateOrder

后端必须检查请求的标识符是否未受策略禁止,并且not_beforenot_after是否在服务器策略内。

account_id字段包含由前端生成的ACME账户ID,而eab_id字段包含在先前的ValidateEAB成功后的EAB kid

后端必须以挂起状态的Order响应,或者如果它不愿意以请求的方式创建订单,则返回错误。

CreateAuthorization

后端必须检查请求的标识符是否未受策略禁止,并且它是否愿意允许预先授权。

account_id字段包含由前端生成的ACME账户ID,而eab_id字段包含在先前的ValidateEAB成功后的EAB kid

后端必须以挂起状态的Authorization响应,或者如果它不愿意以请求的方式创建订单,则返回错误。

FinalizeOrder

后端必须根据之前订单对象中生成的ID查找订单。后端无需检查权限,因为前端已经完成了这项检查。如果指定的订单不存在,应使用标准的gRPC NOT_FOUND 状态码。

后端必须检查订单是否处于愿意颁发证书的状态(即有效),否则返回错误。

csr 字段包含DER编码的CSR。后端必须通过策略检查CSR是否有效。如果是,则必须开始颁发证书,这应该在后台完成。

account_uri 字段包含ACME账户的URI,用于CAA检查。

onion_caa 字段包含客户端提供的Tor隐藏服务的带内CAA记录。

后端必须以处理状态返回一个 Order,或者如果它不愿意按照请求处理订单,则返回错误。

DeactivateAuthorization

后端必须通过之前授权对象中生成的ID查找授权。后端无需检查权限,因为前端已经完成了这项检查。如果指定的授权不存在,应使用标准的gRPC NOT_FOUND 状态码。

如果授权处于可用状态(即未停用、未过期或未被撤销),后端必须将授权标记为停用,并使其对未来的订单不可用。

CompleteChallenge

后端必须通过之前授权对象中生成的ID和授权ID查找挑战。后端无需检查权限,因为前端已经完成了这项检查。后端必须检查挑战对象是否属于授权,否则返回 NOT_FOUND 错误。

account_thumbprint 可以传递给Frida以允许验证发生。验证应该在后台进行,因为客户端将轮询服务器,直到挑战成功或失败。

GetOrder

后端必须根据之前订单对象中生成的ID查找订单。后端无需检查权限,因为前端已经完成了这项检查。如果指定的订单不存在,应使用标准的gRPC NOT_FOUND 状态码。

GetAuthorization

后端必须通过之前授权对象中生成的ID查找授权。后端无需检查权限,因为前端已经完成了这项检查。如果指定的授权不存在,应使用标准的gRPC NOT_FOUND 状态码。

GetChallenge

后端必须通过之前授权对象中生成的ID和授权ID查找挑战。后端无需检查权限,因为前端已经完成了这项检查。后端必须检查挑战对象是否属于授权,否则返回 NOT_FOUND 错误。

GetCertificate

后端必须根据之前订单对象中生成的ID查找证书。后端无需检查权限,因为前端已经完成了这项检查。后端必须以DER编码的二进制列表元素返回每个链中的每个证书。

RevokeCertificate

后端应查找与 issuer_id(如在前端配置中设置)和证书的 serial_number 对应的证书。如果 revocation_reason 已设置,后端应检查它是否是策略可接受的理由。

如果 authz_checked 设置为 true,则后端无需检查请求上的权限,因为前端已经完成了这些检查。如果没有,则应检查ACME account_id 是否有权撤销证书。根据RFC 8555,后端“必须考虑以下账户有权撤销特定证书”

  • 颁发证书的账户。
  • 持有证书中所有标识符的授权的账户。

如果撤销成功,则必须返回一个空响应,否则必须返回一个解释请求被拒绝原因的错误。

Benny

Benny期望后端CA有一个用于证书状态检查的单个方法:CheckCert

请求消息包含配置中设置的发行者ID和要检查的证书的序列号字段。

响应表示证书状态以及一些关于撤销的可选元数据。

后端可能会表示它不知道证书的状态,该证书是已知的良好,证书已知已撤销,或者该证书从未签发。

撤销原因是与 CRL 中相同的值。有关可用撤销原因的更多信息,请参阅RFC 5280 § 5.3.1

revocation_timestamp 表示证书被撤销的时间(如果已知)。invalidity_date 表示被认为证书被破坏的时间(如果已知)。this_update 表示检查此证书的权威源的最后时间。next_update 表示权威源将具有更多最新信息的下一个最早时间。archive_cutoff 的工作方式如RFC 6960 § 4.4.4中定义。

Frida

Frida 有 4 种验证方法;ValidateHTTP01ValidateDNS01ValidateTLSALPN01ValidateOnionCSR01》。它们分别执行 http-01dns-01tls-alpn-01onion-csr-01 验证。

使用 CheckCAA 方法执行 CAA 检查。

Frida 支持验证 dnsip 标识符类型,不支持使用 email-reply-00 验证 email

KeyValidationRequest 输入消息包含;

  • token - 验证令牌,由后端 CA 生成。
  • account_thumbprint - 由 Björn 提供的 ACME 账户指纹。
  • identifier - 要验证的名称。
  • hs_private_key - 用于连接隐藏服务的 32 字节私钥(如果适用)。

响应消息包含一个布尔指示验证成功或失败,在失败的情况下,包含导致验证失败的错误列表的错误对象。错误格式在上文描述。

dns 类型标识符的情况下,标识符值必须为小写,非 punny 代码(IDNA),并且根区域后不带尾随点。

OnionCSRValidationRequest 输入消息包含;

  • csr - 由 ACME 客户端生成的 DER 编码的 CSR。
  • ca_nonce - 由后端 CA 生成的 nonce。
  • identifier - 要验证的名称。

CAACheckRequest 输入消息包含;

  • validation_method - 用于验证标识符的方法。
  • identifier - 要检查的名称。
  • account_uri - 为 RFC 8657 目的请求验证的账户的 URI,由 Björn 提供。
  • hs_private_key - 用于连接隐藏服务的 32 字节私钥(如果适用)。
  • onion_caa - 隐藏服务的带内 CAA,如果由 ACME 客户端提供。

依赖关系

~54–91MB
~1.5M SLoC