Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 673
CodeFactory.DEV Avatar asked Jul 02 '15 04:07

CodeFactory.DEV


1 Answers

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

like image 97
Omikron Avatar answered Sep 21 '22 18:09

Omikron