There are three certificates in my example, assume they form a chain but i don't know yet which of them signed which:
X509Certificate c1 = ....
X509Certificate c2 = ....
X509Certificate c2 = ....
I would like to know which certificate is responsible for signing the other certificate.
The plan was to get the "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"));
Im getting the following for the certificates (in order of the chain): subject/authority key ID pair
Values of the SubjectKeyIdentifier and AuthorityKeyIdentifier after Decoding:
Certificate 1: (end of the chain)
#0416041482b7384a93aa9b10ef80bbd954e2f10ffb809cde
#04183016801482b7384a93aa9b10ef80bbd954e2f10ffb809cde
Certificate 2: Signed by Certificate 1
#04160414ab8059c365836d1d7d13bd19c3ec1a8f0d476aa3
#04183016801482b7384a93aa9b10ef80bbd954e2f10ffb809cde
Certificate 3: Signed by Certificate 2
(no SubjectKeyIdentifier - null bytes)
#041830168014ab8059c365836d1d7d13bd19c3ec1a8f0d476aa3
Formatted and Aligned for Easy Reading (Same thing as the one on top)
------------------------------------------------------------------------------
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
I was expecting c3's AuthorityKeyIdentifier to be equivalent to c2's SubjectKeyIdentifier. that does not seem to be the case here.
EDIT: some parts of the result seem to match, I have some idea on the "SubjectKeyIdentifier" - it always starts with '#04' followed by the length of the contents (in hex). I now have a certain idea on how to decode the "SubjectKeyIdentifier", but the "AuthorityKeyIdentifier" is still a big mystery to me.
relevant SO post
Did i do anything wrong with the decoding? Why are the AuthorityKeyIdentifier not matching correctly against the SubjectKeyIdentifier of the certificate that signed it?
If you take a look at the ASN.1 definition of SKI and AKI in RFC5280 (following the links in your question) the difference becomes obvious:
SubjectKeyIdentifier ::= KeyIdentifier
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] KeyIdentifier OPTIONAL,
authorityCertIssuer [1] GeneralNames OPTIONAL,
authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
KeyIdentifier ::= OCTET STRING
So, the AKI is not an OCTET STRING, but a SEQUENCE of three optional elements. One of these elements is the octet string that can be compared to an SKI.
The 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
The first two bytes (04 18) are part of the Extension structure (as explained in the related question Why doesn't my key identifier match?), the actual AKI extension content starts at "30 16".
Your Java code for decoding the AKI should look like this (using 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));
And for decoding 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));
Also, both extensions are optional. If your code should work with arbitrary certificates, then a fallback mechanism is necessary (like verifying the signatures).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With