如何知道哪个 X509 证书签署了另一个证书 (Java)

How to know which X509 Certificate Signed another Certificate (Java)

我的示例中有三个证书,假设它们形成一个链,但我还不知道它们中的哪一个签署了哪个:

X509Certificate c1 = ....
X509Certificate c2 = ....
X509Certificate c2 = ....

我想知道哪个证书负责签署另一个证书。

计划是获得“AuthorityKeyIdentifier" and match it with the "SubjectKeyIdentifier”。

import org.bouncycastle.asn1. DEROctetString;

private static String decodeKey(byte[] e) {
    DEROctetString octet = new DEROctetString(e);
    return octet.toString();
}

String subjectKeyId = decodeKey(c.getExtensionValue("2.5.29.14"));
String authorityKeyId = decodeKey(c.getExtensionValue("2.5.29.35"));

我得到以下证书(按链的顺序):subject/authority 密钥 ID 对

SubjectKeyIdentifier 和 AuthorityKeyIdentifier 解码后的值:

证书 1:(链尾)

#0416041482b7384a93aa9b10ef80bbd954e2f10ffb809cde
#04183016801482b7384a93aa9b10ef80bbd954e2f10ffb809cde

证书 2:由证书 1 签名

#04160414ab8059c365836d1d7d13bd19c3ec1a8f0d476aa3
#04183016801482b7384a93aa9b10ef80bbd954e2f10ffb809cde

证书 3:由证书 2 签名

(no SubjectKeyIdentifier - null bytes)
#041830168014ab8059c365836d1d7d13bd19c3ec1a8f0d476aa3

格式化和对齐以便于阅读(与顶部相同)

------------------------------------------------------------------------------
       01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
------------------------------------------------------------------------------
Certificate 1
#04 16 04 14       82 b7 38 4a 93 aa 9b 10 ef 80 bb d9 54 e2 f1 0f fb 80 9c de
#04 18 30 16 80 14 82 b7 38 4a 93 aa 9b 10 ef 80 bb d9 54 e2 f1 0f fb 80 9c de

Certificate 2
#04 16 04 14       ab 80 59 c3 65 83 6d 1d 7d 13 bd 19 c3 ec 1a 8f 0d 47 6a a3
#04 18 30 16 80 14 82 b7 38 4a 93 aa 9b 10 ef 80 bb d9 54 e2 f1 0f fb 80 9c de

Certificate 3
=== == == == == == == == == == == NO DATA  == == == == == == == == == == == ==
#04 18 30 16 80 14 ab 80 59 c3 65 83 6d 1d 7d 13 bd 19 c3 ec 1a 8f 0d 47 6a a3

我原以为 c3 的 AuthorityKeyIdentifier 等同于 c2 的 SubjectKeyIdentifier。这似乎不是这里的情况。

编辑:结果的某些部分似乎匹配,我对“SubjectKeyIdentifier”有一些想法 - 它总是以“#04”开头,后跟内容的长度(十六进制)。我现在对如何解码“SubjectKeyIdentifier”有了一定的了解,但“AuthorityKeyIdentifier”对我来说仍然是个谜。

相关SO post

我是不是解码有问题? 为什么 AuthorityKeyIdentifier 与签署它的证书的 SubjectKeyIdentifier 不正确匹配?

如果您查看 RFC5280 中 SKI 和 AKI 的 ASN.1 定义(按照您问题中的链接),区别就会变得很明显:

SubjectKeyIdentifier ::= KeyIdentifier

AuthorityKeyIdentifier ::= SEQUENCE {
  keyIdentifier             [0] KeyIdentifier           OPTIONAL,
  authorityCertIssuer       [1] GeneralNames            OPTIONAL,
  authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }

KeyIdentifier ::= OCTET STRING

因此,AKI 不是一个 OCTET STRING,而是三个可选元素的 SEQUENCE。这些元素之一是可以与 SKI 进行比较的八位字节串。

Distinguished Encoding Rules (DER) determine the byte representation of these ASN.1 structures. The individual bytes of an AKI extension have the following meaning (see A Layman's Guide to a Subset of ASN.1, BER, and DER):

04 18 30 16 80 14 82 b7 38 4a 93 aa 9b 10 ef 80 bb d9 54 e2 f1 0f fb 80 9c de

04 OCTET STRING
18 LENGTH
30 SEQUENCE
16 LENGTH
80 CONTEXT-SPECIFIC PRIMITIVE TAG 0
14 LENGTH
.. DATA

前两个字节(04 18)是扩展结构的一部分(如相关问题Why doesn't my key identifier match?中所述),实际的AKI扩展内容从“30 16”开始。

用于解码 AKI 的 Java 代码应该如下所示(使用 Bouncy Castle):

byte[] extensionValue = cert.getExtensionValue("2.5.29.35");
byte[] octets = DEROctetString.getInstance(extensionValue).getOctets();
AuthorityKeyIdentifier authorityKeyIdentifier = AuthorityKeyIdentifier.getInstance(octets);
byte[] keyIdentifier = authorityKeyIdentifier.getKeyIdentifier();
String keyIdentifierHex = new String(Hex.encode(keyIdentifier));

并用于解码 SKI:

extensionValue = cert.getExtensionValue("2.5.29.14");
octets = DEROctetString.getInstance(extensionValue).getOctets();
SubjectKeyIdentifier subjectKeyIdentifier = SubjectKeyIdentifier.getInstance(octets);
keyIdentifier = subjectKeyIdentifier.getKeyIdentifier();
keyIdentifierHex = new String(Hex.encode(keyIdentifier));

此外,这两个扩展都是可选的。如果您的代码应使用任意证书,则需要一种回退机制(如验证签名)。

如果您想要快速检查,只需在 windows 中打开证书并查找名为 "Certification Path" 的选项卡。如果适用,它还可以让您轻松遍历证书链。 (我想 post 一张照片,但显然还没有足够的声誉。)