Search code examples
javacertificatex509decodingsubject

How to know which X509 Certificate Signed another Certificate (Java)


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?


Solution

  • 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).