Search code examples
javasecuritysslkeystore

What does the Certificate[] chain in the Keystore.setKeyEntry() mean and how to obtain that info from a JKS or PKCS12?


I know what a certificate chain is. In java when working with KeyStore objects we can add certificates and private keys to a keystore object.

for that we do:

        KeyStore sourceKeystore = KeyStore.getInstance("jks");
        try (InputStream stream = new BufferedInputStream(Files.newInputStream(sourceKeystorePath))) {
            sourceKeystore.load(stream, sourceKeystorePassword);
        }

        KeyStore destKeystore = KeyStore.getInstance("jks");
        destKeystore.load(null, destKeystorePassword);

        Enumeration<String> aliasList = sourceKeystore.aliases();
        while (aliasList.hasMoreElements()) {
            String alias = aliasList.nextElement();
            destKeystore.setCertificateEntry(alias, sourceKeystore.getCertificate(alias));
            if(sourceKeystore.isKeyEntry(alias)) {
                System.out.println(alias + " : is private key");
                Key key = sourceKeystore.getKey(alias, "secret".toCharArray());
                Certificate[] chain = new Certificate[1];
                chain[0] = sourceKeystore.getCertificate(alias);
                destKeystore.setKeyEntry(alias, key, "secret".toCharArray(), chain);
            }
        }

        try (OutputStream stream = new BufferedOutputStream(Files.newOutputStream(destKeystorePath))) {
            destKeystore.store(stream, destKeystorePassword);
        }

What I want to undertstand is destKeystore.setKeyEntry(). When I give a cert chain as a parameter in this can I give an array of certs like this?

  1. [rootCert, interCert, mainCert]
  2. [mainCert, interCert, rootCert]
  3. [mainCert]

First Question: what does these various ways of setting the chain mean?

Second question: Also if I have a JKS file. How do I find this exact value of certificate chain and in which order the certificate chain was set for a private key in this KeyStore? basically what I mean is I want to find out what was the Certificate[] parameter passed to KeyStore.setKeyEntry() in that JKS file


Solution

  • First, the basics of how the certificate chain is formed.

    When you initially create a key pair by any means (keytool, openssl, etc,.), it basically consists of a private key associated with its self-signed certificate, where the self-signed certificate contains the public key. And then a PKCS#10 (certificate signing request) is created out of the key-pair, which is basically some identity information about the owner of the private key + public key, put together and signed by the private key. This CSR will be sent to a Certificate Authority to get signed certificate back. The CA signs it and responds with a certificate chain. This received certificate chain is then updated to the initially created private key, replacing the old self-signed certificate. Now, we call this key pair a signed key pair, it is not self-signed any more.

    Now understanding what the CA sent. Basically a certificate chain sent by the CA looks like this:

    CA Certificate (self-signed)
    |
    |__ 2. Sub CA Certificate (signed by the above CA)
            |
            |__ 1. Sub-sub CA Certificate (if any) (signed by the above Sub CA)
                    |
                    |__ 0. End Entity Certificate (your certificate, signed by the above cert)
    

    If you look at the indexes of the certificates, they tell the following:

    • The most important certificate is the first certificate (aka, the user/peer certificate)
    • The least important certificate is the last certificate (aka, the CA certificate)

    In coding terminology, the first (zeroth) element of the certificate array is the user certificate, and the last element of the certificate array is the CA certificate. Which means, the matching public key that belongs to your private key can be found in the first certificate.

    99% of the time you don't have to deal with the order of the certificate chain yourself. When the CA responds with a certificate chain, it is usually in the correct order. All you have to do, is to update the certificate chain to your private key.

    Now the answers to your questions:

    1. Since you are in the java world, the first order of certificates is considered backwards (incorrect). The second option is correct. The third option is correct as well, but it is advisable to always include the entire certificate chain if you have it.
    2. In you code where you are doing:
    Certificate[] chain = new Certificate[1];
    chain[0] = sourceKeystore.getCertificate(alias);
    destKeystore.setKeyEntry(alias, key, "secret".toCharArray(), chain);
    

    there is also a method available to return you the entire certificate chain that is associated with the private key getCertificateChain(). Where you can simple do:

    Certificate[] chain = sourceKeystore.getCertificateChain(alias);
    destKeystore.setKeyEntry(alias, key, "secret".toCharArray(), chain);
    

    The order in which the getCertificateChain() returns the array is the way it was set in the first place.