通用证书 API¶
https://blueprints.launchpad.net/barbican/+spec/certificate-order-api
问题描述¶
我们目前有一种通过创建新的订单并将参数传递到订单的元数据中来注册证书请求的机制。目前,元数据中的参数会未经修改地传递给 CA 插件。这意味着 - 只要传递的参数与特定后端期望的参数匹配,就可以为该后端创建证书请求。
但这降低了抽象的威力。客户端不应该知道 Barbican 服务器提供的 CA 是哪个。相反,客户端应该能够通过一个通用 API 请求证书,该 API 应该被所有已知的证书插件支持。
关键在于定义一个大多数 CA 都将支持的接口/元数据参数。在 [1] 中已经尝试过这样做。
最近,有人建议使用 RFC 7030 [4],作为一种基于标准的通用 API 方法。RFC7030 基本上提出了一种用于提交 CMC (RFC 5272) 请求和接收相关响应的 HTTP 接口。
RFC 7030 与我们当前的订单接口和功能差异太大,我们不考虑在 Kilo 中添加它。
另一方面,如 RFC 5272 [2] 中定义的 CMC 请求 - 简单和完整 - 已经成为标准一段时间了,并且大多数(如果不是全部)证书颁发机构都支持它们。这使它们成为多个后端 CA 支持的通用参数集的理想候选者。
理想情况下,CMC 请求可以从 barbican 核心完整地传递到插件,再到后端 CA。以下 CA 已知直接支持 CMC 请求:Dogtag、Microsoft CA。
即使 CA 不直接支持 CMC 请求,CMC 请求也提供了一种方便的基于标准的工具,用于描述所有请求的 x509 扩展、请求数据和身份验证机制,如 POP。这些是我们不想重新发明的。
当然,简单的 CMC == PKCS10 - 所有 CA 都应该支持,编写一个插件方法来支持简单的 CMC 请求应该很容易。
提议的变更¶
我们将继续使用订单资源来注册证书请求,并使用订单元数据来包含请求参数。订单请求可能如下所示
选项 1:使用简单的 PKI 请求进行订单(参见 RFC 5272 的第 3.1 节)
{
"type": "certificate",
"meta": {
"ca": "CA identifier (optional, see note in reference [3] below)",
"request_type": "simple-cmc",
"request_data": "base64 encoded simple CMC request (PKCS10)"
"requestor_name": "(optional) string"
"requestor_email": "(optional) email address - RFC 5322, 5321"
"requestor_phone" : "(optional) string"
}
}
选项 2:使用完整的 PKI 请求进行订单(PKCS7,参见 RFC 5272 的第 3.2 节)
{
"type": "certificate",
"meta": {
"ca": "CA identifier (optional, see note in reference [3] below)",
"request_type": "full-cmc",
"request_data": "base64 encoded full CMC request (PKCS7)"
"requestor_name": "(optional) string"
"requestor_email": "(optional) email address - RFC 5322, 5321"
"requestor_phone" : "(optional) string"
}
}
选项 3:使用已经存储在 Barbican 中的密钥进行订单
{
"type": "certificate",
"meta": {
"ca": "CA identifier (optionali, see note in reference [3] below)",
"request_type": "stored-key",
"public_key_ref": "barbican UUID for public key",
"subject_dn": "subject DN (RFC1485 [5])"
"extensions": "base 64 DER encoded ASN.1 values (RFC 5280)"
"requestor_name": "(optional) string"
"requestor_email": "(optional) email address - RFC 5322, 5321"
"requestor_phone" : "(optional) string"
}
}
Extensions data defined by the following in RFC 5272 (section 3.1), where
the specific ASN.1 structures for each extension are defined in RFC 5280
(section 4.2) [6]:
Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
-- contains the DER encoding of an ASN.1 value
-- corresponding to the extension type identified
-- by extnID
}
订单 4:使用特定 CA 的参数进行订单。
现有代码已经支持此机制。有一个用于从服务器检索可能参数的 API 的蓝图 [7]
{
"type": "certificate",
"meta": {
"ca": "CA identifier (optional, see note in reference [3] below)",
"request_type": "custom",
"ca_specific_parameter1": "value",
"ca_specific_parameter2": "value2",
...
}
}
在 Barbican 服务器中,需要添加逻辑来区分这些不同的请求类型。为了向后兼容,如果未提供 request_type,我们将假定它为“custom”。
在服务器上,我们可以添加以下逻辑
对于订单 1 和 2,我们可能能够验证 request_data 是否是有效的 PKCS10/ PKCS7。为此,我们可以使用 PyASN.1 尝试将请求分解为相关的 ASN.1 结构,并在分解不成功时失败。
对于订单 3,我们应该检索相关的公钥,使用公钥、主题 DN 和扩展数据创建 PKCS10 请求。作为简单的 CMC 请求提交到后端。
对于订单 1、2、3,通过新的 CertPlugin API 调用提交到插件:issue_simple_cmc_request()、issue_full_cmc_request()。
插件将与后端 CA 交互,后者预计会返回 PKCS7 完整的 CMC 响应。
插件将负责解释响应并向 barbican-core 返回 ResultDTO 对象。然后处理将按照 Barbican-core 中已经编码的方式进行。
为了简化插件代码并避免不必要的代码重复,barbican-core 将提供一些实用函数来解析 CMCResponse 并将其转换为 ResultDTO 对象。
这项工作的细节不必在此规范中指定,但基本上涉及将 CMCStatus 字段映射到 Barbican CertificateStatus 值,并提取证书、中间证书、Query Pending 控制/ pendInfo 数据和/或错误消息(如果适用)。
新的和现有的证书请求颁发方法(issue_*_cmc_request、issue_certificate_request)将在插件尚未实现相关方法的情况下提供默认实现(这将引发 NotImplemented 异常)。
此外,每个插件上的 supports() 需要修改,以指定插件是否支持每种证书颁发方法。
此外,识别可用 CA 的 API 还将包括有关每个插件支持的方法的信息,以便客户端可以预先确定为特定 CA 使用哪种方法。可以从 supports() 方法中提取此信息。有关更多信息,请参见 [3]。
关于证书请求更新的说明
通常,CA 不允许在提交证书请求后对其进行修改,但可选的请求者数据除外。
因此,对于证书更新 API,我建议使用以下方法
PUT /orders/{order_id}
{ "type": "certificate", "meta": { "requestor_name": "(optional) string" "requestor_email": "(optional) email address - RFC 5322, 5321" "requestor_phone" : "(optional) string" } }
这明确指定了我们期望能够更改所有上述请求类型中的字段。
备选方案¶
一种可能性是实现一个实现 RFC 7030 的 API,它也是围绕 CMC 请求的 HTTP 接口。RFC 7030 期望客户端将 CMC 请求发送到特定的 URL,并处理 CMC 响应。
采用 RFC 7030 接口的主要问题是它与我们已经在 Barbican 中实现的内容非常不同,我们必须放弃/重写我们已经拥有的很多代码。这在 kilo 中是不可行的。
其中一些工作流程也大不相同。
例如,如果证书待处理,Barbican 内部存在轮询证书直到获得批准和签发的逻辑。然后,证书及其中间证书存储在 Barbican 内部以供以后检索。
对于 RFC7030,客户端需要通过解码 pkcs7-mime 响应 - 202 - 并重新提交相同的请求直到获得批准来处理轮询。
此外,不支持在服务器上存储证书,并返回 order_id、secret_id 等。也不支持修改/取消订单。
与其支持 CMC,我们可以尝试定义一个具有不同证书配置的基本参数的通用 API。这是在 [1] 中开始的方法。如果采用此蓝图,我们将放弃 [1]。
这更简单,但似乎有点武断。使用 CMC 可能会被大多数 CA 支持,因为它基于标准。此外,可能已经存在已经使用 CMC 的现有客户端。
更简单的机制也有限制,不支持加密或 POP。最终,我认为我们需要演变为支持类似 CMC 的东西。
数据模型影响¶
我们可能需要在 order_metadata 中添加其他数据,但这不应导致数据库更改。
REST API 影响¶
参见上述建议的更改。
安全影响¶
无
通知与审计影响¶
没有其他内容,除了需要的额外审计更改。
其他最终用户影响¶
barbican-client 或 certmonger 将需要进行更改以支持新的 API。
性能影响¶
将需要进行一些额外的处理来解析 CMCRequest。这些更改不在规范范围内。
其他部署者影响¶
无。
开发人员影响¶
插件开发人员需要修改 supports() 方法以指定他们将支持哪些颁发方法。此信息将用于适当地路由证书请求,并在与接口 [3] 中定义的接口交互时向客户端通知 CA 的功能。
如果插件开发人员希望支持这些请求,则需要实现两种新方法:issue_simple_cmc_request() 和 issue_full_cmc_request()。我们预计简单的 CMC 很容易实现,因为简单的 CMS == PKCS10。
实现¶
负责人
- 主要负责人
alee chellygel dave-mccowan woodster
工作项¶
修改 API 代码以解析新的订单参数并将数据传递到较低级别。
添加代码以验证 CMC 请求数据。
在 certificate_manger.py 中添加代码以将请求发送到插件。修改插件接口合同以提供默认实现。
插件应修改 supports() 方法并根据需要实现新方法。
在 certificate_manager.py 中添加代码以解析 CMC 响应并将其转换为 ResultDTO 对象以返回到 barbican-core。
添加客户端代码。这可能需要一个单独的蓝图。
依赖项¶
这项工作应该与接口工作一起完成,以获取 [3] 中的 CA 标识符。
测试¶
需要更多的单元和功能测试。
文档影响¶
文档(包括 sphinx 和插件文档)需要更改以反映此 API。
参考资料¶
[1] https://review.openstack.org/#/c/129695 现有蓝图,用于定义一组通用的证书 API 参数。
[2] http://tools.ietf.org/html/rfc5272 通过 CMS 进行证书管理
[3] API 用于获取 CA 标识符在此蓝图中定义。此外,还有一个用于获取 CA 签名证书和中间证书的 API。https://review.openstack.org/129048
[4] https://tools.ietf.org/html/rfc7030 通过安全传输进行注册
[5] http://tools.ietf.org/html/rfc1485 区分名的字符串表示形式
[6] http://tools.ietf.org/html/rfc5280 互联网 X.509 公钥基础设施证书和证书吊销列表 (CRL) 配置文件
[7] https://review.openstack.org/129377 获取 CA 特定证书注册参数的 API。
[8] LibEST 实现 https://github.com/cisco/libest