为什么我的密钥标识符不匹配?

我正在尝试解密S / MIME电子邮件(最初通过Outlook发送),为此,我正在使用bouncycastle API。 不过,我遇到了困难。

我在Windows证书存储区中有收件人的证书。 我之前用它将签名和加密的电子邮件发送给另一方,然后他们用它向我发送加密回复。 然后我将证书(带私钥)导出为.pfx文件,然后将此pfx文件加载到Java KeyStore中。 但是,它不起作用,我怀疑这是因为主题密钥标识符不匹配。

这是我用来从KeyStore获取主题密钥id的代码:

KeyStore ks = KeyStore.getInstance("PKCS12"); char[] pw = "password".toCharArray(); ks.load(new FileInputStream("d:\\cert_priv_key.pfx"), pw); Enumeration en = ks.aliases(); while( en.hasMoreElements() ) { String alias = (String)en.nextElement(); System.out.println(alias); if( ks.isKeyEntry(alias) ) { Certificate[] chain = ks.getCertificateChain(alias); X509Certificate cert = (X509Certificate)chain[0]; byte[] id = cert.getExtensionValue("2.5.29.14"); System.out.println(" " + toHex(id)); } } 

这将打印出以下密钥标识符:

 04 16 04 14 88 ed bb 7c 64 7b 41 63 48 0a 24 40 2b 3c d0 78 72 3c 30 b3 

但是,当我检查Windows证书存储区时,密钥标识符是不同的:

 88 ed bb 7c 64 7b 41 63 48 0a 24 40 2b 3c d0 78 72 3c 30 b3 

KeyStore在前面返回额外的4个字节(主题密钥标识符应该是密钥的160位SHA1散列,因此20个字节长,正确吗?)。

更令人困惑的是,当我使用SMIMEEnveloped.getRecipientInfos().getRecipients() API解析S / MIME电子邮件并通过收件人( SMIMEEnveloped.getRecipientInfos().getRecipients() )时,唯一收到的收件人(应该只有一个)有这个主题密钥标识符:

 04 14 88 ed bb 7c 64 7b 41 63 48 0a 24 40 2b 3c d0 78 72 3c 30 b3 

…它只有两个额外的字节,而不是四个,我认为这就是为什么我无法使用证书解密电子邮件。

为什么这些主题密钥标识符都不匹配? 我究竟做错了什么?

如果您了解所有规格,所有这些答案都是一致的,但当然这意味着如果您不理解它们会让您感到困惑。 首先要看的是RFC 5280,第4.2.1.2节 。 在这种情况下,使用方法(1)。 接下来,查看KeyIdentifier定义中的A.2节 。 它被定义为OCTET STRING。 现在看看ASN.1 OCTET STRING 应该如何编码 。 它应该以hex04开始,后跟长度(以字节为单位)(20字节或14hex),然后是实际的八位字节字符串(SHA1哈希)。 所以扩展的内容应该是

 04 14 88 ed bb 7c 64 7b 41 63 48 0a 24 40 2b 3c d0 78 72 3c 30 b3 

最后,查看Extension的ASN.1定义。 它表示扩展值必须编码为OCTET STRING。 在这个特定扩展的情况下,净效果是连续两次编码为OCTET STRING。 在这个级别,OCTET STRING是前一个OCTET STRING,包括两个头字节04 14 ,所以长度是hex16,编码是

 04 16 04 14 88 ed bb 7c 64 7b 41 63 48 0a 24 40 2b 3c d0 78 72 3c 30 b3 

这是X509Extension.getExtensionValue()方法返回的值。 现在key id的有趣部分只是以88开头的SHA1哈希,所以这就是Windows实用程序显示的内容。 显然,您正在使用的bouncycastle方法显示扩展而不进行额外的解码。

Outlook 2010是否创建了S / MIME邮件?

如果是这样,请参阅http://bouncy-castle.1462172.n4.nabble.com/Re-ReadEncryptedMail-sample-and-SubjectKeyIdentifier-instead-of-IssuerSerial-Outlook-2010-Hack-td3042968.html和https:// bugzilla .mozilla.org / show_bug.cgi?id = 559243了解更多信息

Martijn Brinkers

GregS接受的答案帮助了我很多。

最终为我工作的代码是:

 X509Certificate certificate = ... byte[] encExtensionSubjectKeyIdentifier = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId()); // Unwrap first 'layer' ASN1Primitive skiPrimitive = JcaX509ExtensionUtils.parseExtensionValue(encExtensionSubjectKeyIdentifier); // Unwrap second 'layer' byte[] keyIdentifier = ASN1OctetString.getInstance(skiPrimitive.getEncoded()).getOctets(); // Use keyIdentifier in eg CMS SignerInfo SignerInfoGenerator signerInfoGenerator = jcaSignerInfoGeneratorBuilder.build(sha1Signer, keyIdentifier);