Why doesn't my key identifier match?

2019-04-27 04:40发布

问题:

I'm attempting to decrypt an S/MIME email (sent originally via Outlook), and to do that, I'm using the bouncycastle API. I'm running into a snag, though.

I have, in the Windows certificate store, the certificate for the recipient. I had previously used it to send a signed and encrypted email to the other party, and they in turn used it to send me an encrypted reply. I then exported the certificate (with private key) as a .pfx file, and I loaded this pfx file into a Java KeyStore. It doesn't work, however, and I suspect that's because the subject key identifiers don't match.

Here's the code I'm using to get the subject key id from the KeyStore:

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));
    }
}

This prints out the following key identifier:

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

When I check the Windows certificate store, however, the key identifier is different:

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

The KeyStore returns an extra 4 bytes in the front (the subject key identifier should be the 160-bit SHA1 hash of the key, and therefore 20 bytes long, correct?).

Even more confusing is the fact that when I parse the S/MIME email using the bouncycastle API, and go through the recipients (SMIMEEnveloped.getRecipientInfos().getRecipients()), the only recipient returned (there should be only one) has this subject key identifier:

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

... it has only two extra bytes, not four, and I assume this is why I'm unable to decrypt the email with the certificate.

Why do none of these subject key identifiers match up? What am I doing wrong?

回答1:

All these answers are consistent if you understand all the specs, but of course that means they are confusing if you don't. The first place to look is in RFC 5280, section 4.2.1.2. In this case method (1) is used. Next, look at section A.2 at the definition of KeyIdentifier. It is defined as an OCTET STRING. Now look at how an ASN.1 OCTET STRING should be encoded. It should start out with hex 04 followed by the length in bytes (20 bytes, or 14 hex) followed by the actual octet string (the SHA1 hash). So the contents of the extension should be

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

Finally, look at the ASN.1 definition of Extension. It says that the extension value must be encoded as an OCTET STRING. In the case of this particular extension, the net effect is that is get encoded twice in a row as an OCTET STRING. At this level the OCTET STRING is the previous OCTET STRING which includes the two header bytes 04 14, so the length is hex 16, and the encoding is

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

This is the value returned by the X509Extension.getExtensionValue() method. Now the interesting part of the key id is just the SHA1 hash which start with 88, so this is what the Windows utility displays. Obviously the bouncycastle method you are using displays the extension without doing additional decoding.



回答2:

Is the S/MIME message created by Outlook 2010?

if so see http://bouncy-castle.1462172.n4.nabble.com/Re-ReadEncryptedMail-sample-and-SubjectKeyIdentifier-instead-of-IssuerSerial-Outlook-2010-Hack-td3042968.html and https://bugzilla.mozilla.org/show_bug.cgi?id=559243 for more info

Martijn Brinkers



回答3:

The accepted answer from GregS helped me alot.

The code that ended up working for me is:

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);