如何证明一个证书是另一个证书的颁发者

时间:2013-07-26 09:14:46

标签: certificate x509certificate bouncycastle x509 digital-certificate

我有两个证书。一个证书是另一个证书的颁发者。

我如何看到java代码,我的发行人证书真的是发行人?

我知道我的证书的AuthorityKeyIdentifier和颁发者证书的SubjectKeyIdentifie必须是相同的。我检查,他们相同

但是使用java代码我得到了结果:

    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");

    InputStream usrCertificateIn = new FileInputStream("/usr.cer");
    X509Certificate cert = (X509Certificate) certFactory.generateCertificate(usrCertificateIn);

    InputStream SiningCACertificateIn = new FileInputStream("/siningCA.cer");
    X509Certificate issuer = (X509Certificate) certFactory.generateCertificate(SiningCACertificateIn);

    byte[] octets = (ASN1OctetString.getInstance(cert.getExtensionValue("2.5.29.35")).getOctets());     
    System.out.println(Arrays.toString(octets) + " bouncycastle, AuthorityKeyIdentifier");
    System.out.println(Arrays.toString(cert.getExtensionValue("2.5.29.35")) + "java.security, AuthorityKeyIdentifier");

    octets = ASN1OctetString.getInstance(issuer.getExtensionValue("2.5.29.14")).getOctets();
    System.out.println((Arrays.toString(octets) + "bouncycastle, SubjectKeyIdentifie "));
    System.out.println(Arrays.toString(issuer.getExtensionValue("2.5.29.14")) + "java.security, SubjectKeyIdentifie ");

结果是:

[48,22,-128, 20,52,-105,49,-70,-24,78,127,-113,-25,55,39,99,46,6,31 ,66,-55,-86,-79,113 ] bouncycastle, AuthorityKeyIdentifier

[ 4 ,24,48,22,-128, 20,52,-105,49,-70,-24,78,127,-113,-25, 55,39,99,46,6,31,66,-55,-86,-79,113 ] java.security, AuthorityKeyIdentifier

必须相同的另一个字节数组,但不是在数组的开头添加了另一个字节。

[ 4,20,52,-105,49,-70,-24,78,127,-113,-25,55,39,99,46,6,31,66,-55 ,-86,-79,113 ] bouncycastle, SubjectKeyIdentifie

[4,22, 4,20,52,-105,49,-70,-24,78,127,-113,-25,55,39,99,46,6,31, 66,-55,-86,-79,113 ] java.security, SubjectKeyIdentifie

问题1) 我可以计算关键标识符以获得相同的数组吗?

问题2) 还有另一种方法可以证明一个证书是另一个证书的发行者

2 个答案:

答案 0 :(得分:7)

AuthorityKeyIdentifierSubjectKeyIdentifier的定义不同:

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

SubjectKeyIdentifier ::= KeyIdentifier

KeyIdentifier ::= OCTET STRING
  

RFC 5280的第4.2.1.1和4.2.1.2节)

因此,仅仅比较扩展值不起作用,而是必须提取KeyIdentifier内容并进行比较,例如使用BouncyCastle ASN.1辅助类。

BTW,实际的密钥标识符字节仅为

52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113

之前的4,20表示OCTET STRING,长度为20个字节。在AuthorityKeyIdentifier中,由于隐式标记,4被标记[0](字节-128)替换。

在你的AuthorityKeyIdentifier之前的48,22意味着(构造)SEQUENCE,22字节长。

等。等

因此,

  

我可以计算关键标识符以获得相同的数组吗?

是,深入查看实际的KeyIdentifier OCTET STRING值。

  

还有另一种方法可以证明一个证书是另一个证书的发行者

那么,您可以通过验证该证书的公钥来检查证书中的签名是否由与假定的颁发者证书关联的私钥签名。

PS:关于评论中的问题

  

密钥标识符的长度总是20?它固定了吗?可能不是,不是吗?

不,不是。前面提到的RFC 5280说:

For CA certificates, subject key identifiers SHOULD be derived from
the public key or a method that generates unique values.  Two common
methods for generating key identifiers from the public key are:

  (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
       value of the BIT STRING subjectPublicKey (excluding the tag,
       length, and number of unused bits).
  (2) The keyIdentifier is composed of a four-bit type field with
       the value 0100 followed by the least significant 60 bits of
       the SHA-1 hash of the value of the BIT STRING
       subjectPublicKey (excluding the tag, length, and number of
       unused bits).

Other methods of generating unique numbers are also acceptable.

我假设您的CA使用方法1(160位= 20字节),但这只是一种常用方法,甚至没有明确推荐,更不用说必需。因此,不,你不能指望标识符是20个字节长。

PPS:关于评论中的问题

  

签名是否真正证明一个证书是由另一个颁发的唯一途径?

发行人发行的关系也不是证明,它只是证明(至少在某种程度上)与假定的发行人证书相关联的私钥签署了检查证书但是有在多个证书中使用相同的私钥 - 公钥对的情况。

从本质上讲,你需要做多次互补测试,甚至必须相信CA不要做奇怪的事情。不久前,例如Swisscom改变了他们的一个CA证书,包括一个额外的扩展或关键属性(我必须查找详细信息;我认为有人证明他们要求更改),并且通过证书签名验证测试现在老签名者证书似乎即使签名者证书的所有者可能不知道新的扩展/关键属性,也要由新的CA证书颁发。

所以最终现实生活并不像人们希望的那么简单......

答案 1 :(得分:4)

为了证明一个证书是由另一个颁发的,您应该证明它是由与发行证书中的公钥对应的私钥签名。

让我们拨打2个证书caCertissuedCert。这些类型为X509Certificate

证明issuedCert的Java代码由caCert代表的实体签名非常简单。

PublicKey caPubKey = caCert.getPublicKey();

issuedCert.verify(caPubKey);

如果verify方法返回而不抛出异常,则issuedCert由与caCert中的公钥对应的私钥签名。