Search code examples
certificatex509keytoolpki

Why can't I import a public key certificate into Firefox that is generated using keytool in a certain way?


I am trying to generate a certificate for CA2 such that:

  1. There is a root CA called CA0.
  2. There is an intermediate CA called CA1.
  3. There is another intermediate CA called CA2.
  4. CA0 signs the certificate of CA1.
  5. CA1 signs the certificate of CA2.

I generate CA2 using various methods using keytool.

Method 1: CA0 signs CA1 and writes to file; CA1 signs CA2 and writes to file; CA0 is exported from keystore to file

# Start afresh
rm -f foo.jks
rm -f *.cer

# Generate self-signed CA0 (root), CA1 (intermediate) and CA2 (another intermediate).
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca0 -keypass kpass0 -dname CN=CA0 -ext bc=ca:true
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 -dname CN=CA1
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca2 -keypass kpass2 -dname CN=CA2

# CA0 signs CA1.
keytool -certreq -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 |
keytool -gencert -keystore foo.jks -storepass stpass -alias ca0 -keypass kpass0 -ext bc=ca:true -outfile ca1.cer

# CA1 signs CA2.
keytool -certreq -keystore foo.jks -storepass stpass -alias ca2 -keypass kpass2 |
keytool -gencert -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 -ext bc=ca:true -outfile ca2.cer

# Export CA0
keytool -export -keystore foo.jks -storepass stpass -alias ca0 -file ca0.cer

When I open Firefox and go to Preferences > Advanced > View Certificates > Authorities, click Import and import ca0.cer, ca1.cer and ca2.cer one by one, they get imported fine. Then if I select CA2 and click View > Details, I can see the complete certificate chain in the Certificate Hierarchy pane. All this is good.

Method 2: CA0 signs CA1 and imports it to keystore; CA1 signs CA2 and imports it to keystore; CA0, CA1 and CA2 are exported from keystore to files

# Start afresh
rm -f foo.jks
rm -f *.cer

# Generate self-signed CA0 (root), CA1 (intermediate) and CA2 (another intermediate).
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca0 -keypass kpass0 -dname CN=CA0 -ext bc=ca:true
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 -dname CN=CA1
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca2 -keypass kpass2 -dname CN=CA2

# CA0 signs CA1.
keytool -certreq -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 |
keytool -gencert -keystore foo.jks -storepass stpass -alias ca0 -keypass kpass0 -ext bc=ca:true |
keytool -importcert -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1

# CA1 signs CA2.
keytool -certreq -keystore foo.jks -storepass stpass -alias ca2 -keypass kpass2 |
keytool -gencert -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 -ext bc=ca:true |
keytool -importcert -keystore foo.jks -storepass stpass -alias ca2 -keypass kpass2

# Export CA0, CA1 and CA2
keytool -export -keystore foo.jks -storepass stpass -alias ca0 -file ca0.cer
keytool -export -keystore foo.jks -storepass stpass -alias ca1 -file ca1.cer
keytool -export -keystore foo.jks -storepass stpass -alias ca1 -file ca2.cer

Again, I can import ca0.cer, ca1.cer and ca2.cer to Authorities in Firefox.

Method 3: CA0 signs CA1 and imports it to keystore; CA1 signs and CA2 and exports to file; CA0 and CA1 are exported from keystore to files

# Start afresh
rm -f foo.jks
rm -f *.cer

# Generate self-signed CA0 (root), CA1 (intermediate) and CA2 (another intermediate).
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca0 -keypass kpass0 -dname CN=CA0 -ext bc=ca:true
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 -dname CN=CA1
keytool -genkeypair -keystore foo.jks -storepass stpass -alias ca2 -keypass kpass2 -dname CN=CA2

# CA0 signs CA1.
keytool -certreq -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 |
keytool -gencert -keystore foo.jks -storepass stpass -alias ca0 -keypass kpass0 -ext bc=ca:true |
keytool -importcert -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1

# CA1 signs CA2.
keytool -certreq -keystore foo.jks -storepass stpass -alias ca2 -keypass kpass2 |
keytool -gencert -keystore foo.jks -storepass stpass -alias ca1 -keypass kpass1 -ext bc=ca:true -outfile ca2.cer

# Export CA0 and CA1
keytool -export -keystore foo.jks -storepass stpass -alias ca0 -file ca0.cer
keytool -export -keystore foo.jks -storepass stpass -alias ca1 -file ca1.cer

This time I can import ca0.cer and ca1.cer into Authorities of Firefox but I cannot import ca2.cer. When I select ca2.cer in the 'Select File Containing CA certificate(s) to import' dialog box and click Open, nothing happens at all. The dialog box disappears and the certificate does not appear in the Authorities pane.


Solution

  • keytool -export writes only the first certificate in the chain to -outfile, see keytool manual:

    If alias refers to a trusted certificate, that certificate is output. Otherwise, alias refers to a key entry with an associated certificate chain. In that case, the first certificate in the chain is returned.

    Whereas keytool -gencert writes the whole chain to -outfile. You can see that when you add -rfc (output in PEM format) to the command:

    -----BEGIN CERTIFICATE-----
    MIICqDCCAmagAwIBAgIEHhRohzALBgcqhkjOOAQDBQAwDjEMMAoGA1UEAxMDQ0ExMB4XDTE2MDYw
    ...
    hkjOOAQDBQADLwAwLAIUfkhluVSKCpemYFYfKf2KfT7UQaACFFA8SLiKbfOo6xh5e01S1YXJhM/P
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    MIICqDCCAmagAwIBAgIEZgEJrjALBgcqhkjOOAQDBQAwDjEMMAoGA1UEAxMDQ0EwMB4XDTE2MDYw
    ...
    hkjOOAQDBQADLwAwLAIUd2DS+rPrJqlGwziqenDdVaYQWaoCFHleJS/5XfDk+GaEMSUw53gQ0vd7
    -----END CERTIFICATE-----
    

    So, ca2.cer contains two certificates (CA1 and CA2) in DER format, simply concatenated. No surprise that Firefox cannot process this.

    I don't think there is any standard that allows concatenated DER certificates. PKCS#7 would be the usual binary format for certificate chains. Concatenated PEM files are pretty common too, but not DER.

    The keytool documentation says nothing about writing out the chain to the file. In fact, it says "the X.509 certificate":

    The command reads the request from infile (if omitted, from the standard input), signs it using alias's private key, and outputs the X.509 certificate into outfile (if omitted, to the standard output).

    Taking a look at the sources of keytool, it writes the generated certificate to the file and the chain - excluding the root:

    dumpCert(cert, out);
    for (Certificate ca: keyStore.getCertificateChain(alias)) {
        if (ca instanceof X509Certificate) {
            X509Certificate xca = (X509Certificate)ca;
            if (!isSelfSigned(xca)) {
                dumpCert(xca, out);
            }
         }
    }
    

    The root certificate is not included because the processing side would verify the chain up to a trust anchor (the root CA) anyway (same concept as SSL chain verification).