How to find out which X509 certificate signed another certificate (Java)

In my example, there are three certificates, suppose they form a chain, but I still do not know which of them are signed:

X509Certificate c1 = .... X509Certificate c2 = .... X509Certificate c2 = .... 

I would like to know which certificate is responsible for signing another certificate.

The plan was to get an AuthorityKeyIdentifier "and match it with a" 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")); 

I get the following for certificates (in the order of their chain): key identifier of subject / authority identifier

The values ​​of the SubjectKeyIdentifier and AuthorityKeyIdentifier after decoding:

Certificate 1: (end of chain)

 #0416041482b7384a93aa9b10ef80bbd954e2f10ffb809cde #04183016801482b7384a93aa9b10ef80bbd954e2f10ffb809cde 

Certificate 2: Signed by Certificate 1

 #04160414ab8059c365836d1d7d13bd19c3ec1a8f0d476aa3 #04183016801482b7384a93aa9b10ef80bbd954e2f10ffb809cde 

Certificate 3: Signature for Certificate 2

 (no SubjectKeyIdentifier - null bytes) #041830168014ab8059c365836d1d7d13bd19c3ec1a8f0d476aa3 

Formatted and aligned for easy reading (same as above)

 ------------------------------------------------------------------------------ 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 expected c3 AuthorityKeyIdentifier to be equivalent to c2 SubjectKeyIdentifier. this does not seem to be the case.

EDIT: some parts of the result seem to be the same, I have an idea in “SubjectKeyIdentifier” - it always starts with “# 04”, followed by the length of the content (in hexadecimal format). Now I have a definite idea on how to decode the “SubjectKeyIdentifier”, but the “AuthorityKeyIdentifier” is still a big mystery to me.

relevant SO post

Did I do something wrong with the decryption? Why does the AuthorityKeyIdentifier property not match the correct SubjectKeyIdentifier of the certificate that signed it?

+6
source share
2 answers

If you take a look at the definition of SKI and AKI ASN.1 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 AKI is not an OCTET STRING, but a SEQUENCE of three optional elements. One of these elements is an octet string that can be compared to SKI.

Distinctive Encoding Rules (DER) define the byte representation of these ASN.1 structures. The individual AKI extension bytes have the following meanings (see Layman Guide for 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 does my key identifier not match? ), The actual contents of the AKI extension starts with “30 16”.

Your Java code for AKI decoding 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 SKI decoding:

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

In addition, both extensions are optional. If your code should work with arbitrary certificates, then a backup mechanism is needed (for example, verification of signatures).

+4
source

If you are looking for a really quick check, just open the certificate in the windows and find the tab called "Certification Path". It also makes it easy to go through the certificate chain, if applicable. (I would post a photo, but apparently I have not yet received a sufficient reputation.)

0
source

All Articles