使用 OpenSSL 签署 PKCS#7 并验证 PKCS#7 签名

信息安全 公钥基础设施 openssl x.509
2021-09-04 18:34:05

如果有人必须在单个包中传输 X.509 证书,通常建议将它们打包到 PKCS#7 中。并且可以对 PKCS#7 的内容进行签名。

OpenSSL 允许通过以下方式将证书打包到 PKCS#7 中:

openssl crl2pkcs7 -nocrl -certfile domain.crt -certfile ca-chain.crt -out domain.p7b

据我从“openssl crl2pkcs7”的手册页中了解到,此 PKCS#7 已签名:

输出文件是 PKCS#7 签名数据结构,不包含签名者,仅包含 证书和可选的 CRL。

这里有几个问题:

  1. “不包含签名者”是什么意思?
  2. 如果 PKCS#7 的内容(证书)没有真正签名,如何使用 OpenSSL 来完成?
  3. 考虑到已签名,如何使用 OpenSSL 验证 PKCS#7 的签名?

如果我错误地理解了整体概念,请澄清这一点。

1个回答

TLDR:实际上并没有签名。你确实理解错了。为了解释,我将从头开始。

PKCS#7 及其略微修改和增强的 IETFized[1] 形式的 CMS 加密消息语法https://www.rfc-editor.org/rfc/rfc3852 et pred et amici,是一个标准,定义了一系列不同但各种加密功能的类似消息。一种类型的 PKCS#7/CMS消息称为SignedData,正如您可能合理猜测的那样,它的最初目的是传送带有数字签名的数据,或者一般来说是一个或多个签名,例如双方签署的合同。PKCS#7(主要)使用公钥密码术,需要/期望一种方法将公钥值与人员、组织和系统等实体正确匹配,称为 PKI 公钥基础结构;在实践中,我们使用的 PKI 是CA 证书颁发机构颁发的X.509 证书,可以使用CRL 证书吊销列表(现在也使用 OCSP,但在 PKCS#7 之后)吊销坏证书。

SignedData 实际上由三个主要部分组成:名为ContentInfo的结构中的实际数据;可变数量的SignerInfo结构,每个结构包含一个签名和一些元数据,包括包含验证签名所需的公钥的证书标识;以及接收者可能需要确定和验证(签名)公钥或用于任何目的的可变数量(可能没有)证书和/或 CRL一般而言,SignerInfo 的数量以及签名的数量可能为零,在这种情况下,数据是无用的并被省略。

因此,没有数据且没有“签名者”(零 SignerInfo 结构)、只有证书和/或 CRL的 SignedData在没有其他好的标准化方法的早期提供了包含多个相关证书和/或 CRL 的标准化格式为了这。结果,它被广泛采用并变得无处不在。这种格式通常由扩展名“p7b”和“p7c”标识。请参阅X.509 和 PKCS#7 证书有什么区别? .

至于你的问题:这种格式是名为 SignedData 类型的 PKCS#7 或 CMS 结构,但它实际上并不包含数据或该数据上的签名,它仅包含证书和/或 CRL。因为它没有签名,所以它本身不能被验证。

每个证书或 CRL 本身都是使用特定于 cert/CRL 格式的签名而不是通用数据签名进行签名的。这与它是在 p7b 中还是单独的无关。提取后的每个(非根)证书或 CRL 都可以进行验证,如果您在其“上方”有有效的证书链,并且如果您将其用于任何用途,则应该是。然而,所有 PKI 签名仅将信任从一组(通常很小)受信任的“根”或“锚”(例如 Verisign、GoDaddy 等,但希望不是 DigiNotar)传播到无限大的其他实体集(HTTPS 或其他 SSL/网络上的 TLS 服务器、世界上的电子邮件发件人、世界上的程序开发人员等)。最终,您决定信任哪些锚点,或者将该决定委托给某人(例如 Microsoft 用于 Windows,Oracle 用于 Java,

在使用证书和(可选)CRL 的 OpenSSL 所有 (AFAICS) 操作中,根据您可以选择或默认的 CA(根)证书的信任库来验证它们。您还可以使用命令行verify实用程序手动(重新)验证证书;https://www.openssl.org/docs/apps/verify.html其他工具和程序会做类似的事情,但细节会有所不同。

[1] 作为比较,Netscape 协议 SSL 安全套接字层被移交给 IETF,在那里它被稍微修改和增强并重命名为 TLS 传输层安全性。尽管有时存在一些重要的技术差异(例如最近影响 SSL 但在 TLS 中修复的 POODLE 漏洞,至少在正确的实现中),但在大多数情况下,SSL 和 TLS 在功能上是相同的,人们通常只是说 SSL 意味着两个都。同样,PKCS#7 和 CMS 非常相似,很多人都使用这两个名称。

每条评论更新 2/06:如果您将有效链发送给使用该链正确验证最低(叶)证书的接收者,这将证明该链是否在收到时有效。

  • 如果攻击者或故障修改或删除任何(需要的)证书或用无效或错误的证书替换一个,则接收到的链将无法验证。
  • 如果添加了不需要的证书(无论是否有效),它将不会被检测到,但也不会损害接收者的安全性。
  • 如果您发送了一个(或多个)不需要的证书并且该不需要的证书被修改、删除或替换,它将不会被检测到,也不会造成伤害。
  • 如果您省略了所需的证书并添加了它(特别是中间 CA 证书可能广为人知且可用),接收者将不会检测到更改,但会有助于安全性。
  • 如果有多个有效链——公共 CA 经常出现这种情况,尤其是现在许多最近升级到一个或多个 RSA-2k ECC SHA2 时——如果你发送一个链并且它被修改为另一个链同样有效,收件人不会检测到更改。这是我能看到的唯一可能伤害接收者的情况:如果有多个有效链但在某些指标上不是同样“好”,比如长期可持续性,接收者可能会被欺骗使用更差的链。

PKCS#7/CMS 可以嵌套。这通常用于签名和加密(又名信封),但其他组合也是可能的。如果您真的关心收件人得到您发送的确切链(或其他集合),而不仅仅是一个有效的链,您可以将 p7b 作为第二个外部 SignedData 消息的内容。在这种情况下,您还有另一种选择:主要是由于历史原因,PKCS#7/CMS 可以生成“分离”签名,其中 SignedData 对象实际上不包含数据,只有 SignerInfo(s) 和一些元数据,如哈希算法( s); 在这种情况下,您必须以收件人可以可靠地连接它们的方式发送这两个数据块,例如 Message12345.dat 和 Message12345.sig 。或者,它可以将数据“嵌入”到 SignedData 消息的内容槽中;对于您的情况,这可能更简单。

RFC 4510(和 2510)有很多我没有研究过的功能和选项,我很容易相信其中有一些东西可以满足你的需求,但我从未见过它的任何实现。您可能必须自己编写和支持这两个(或全部?)的软件。