8

我正在尝试解密 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 个字节,对吗?)。

更令人困惑的是,当我使用 bouncycastle 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

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

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

4

3 回答 3

17

如果您了解所有规格,所有这些答案都是一致的,但这当然意味着如果您不了解,它们会令人困惑。首先要看的地方是RFC 5280,第 4.2.1.2 节。在这种情况下,使用方法(1)。接下来看A.2节中KeyIdentifier的定义。它被定义为一个八位字节串。现在看看应该如何编码ASN.1 OCTET STRING 。它应该以十六进制 04 开头,然后是字节长度(20 字节或 14 十六进制),然后是实际的八位字节字符串(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,它包括两个头字节04 14,所以长度是十六进制 16,编码是

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()方法返回的值。现在,密钥 id 的有趣部分只是以 开头的 SHA1 哈希88,所以这就是 Windows 实用程序显示的内容。显然,您使用的 bouncycastle 方法会显示扩展而不进行额外的解码。

于 2011-06-30T01:21:15.227 回答
1

S/MIME 邮件是由 Outlook 2010 创建的吗?

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

马丁·布林克斯

于 2011-06-30T10:14:42.143 回答
0

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 e.g. CMS SignerInfo
SignerInfoGenerator signerInfoGenerator = jcaSignerInfoGeneratorBuilder.build(sha1Signer, keyIdentifier);
于 2015-04-29T10:45:34.767 回答