JDK-8224891 : The CACERTS keystore type
  • Type: CSR
  • Component: security-libs
  • Sub-Component: java.security
  • Priority: P3
  • Status: Closed
  • Resolution: Withdrawn
  • Submitted: 2019-05-28
  • Updated: 2021-10-13
  • Resolved: 2021-10-13
Related Reports
CSR :  
Relates :  
Description
Summary
-------

Create a new CACERTS `KeyStore` type and file format.

Problem
-------

JDK stores out-of-box root CA certificates in `lib/security/cacerts` and it's using a proprietary keystore format JKS.

1. It's not standard.
1. It requires a password to check for integrity. This password must be publicized and therefore provides no security protection. On the other hand, including a hardcoded password is not a good security practice.
1. People might change the password and this could break existing applications that uses the hardcoded password.
1. We've already changed the default keystore type from JKS to PKCS12 in JDK 9 and we are warning people to migrate from JKS in keytool warnings now.
1. There are almost 100 certificates in this file and loading it spends quite some time

Solution
--------

Hardcode all builtin CA certificates into OpenJDK code, and migrate the cacerts file into an ASCII file that allows users to add their own CA certs or shadow builtin ones. The new keystore type will be named "CACERTS".

Specification
-------------

### Builtin CA certs

All (93) CA certificates that are currently inside the `lib/security/cacerts` file are moved into an *internal* class inside the `java.base` module. This class will be generated at build time using the same certificate files in `make/data/cacerts`.

### The CACERTS file format

    # CACERTS
    # DO NOT REMOVE THE LINE ABOVE
    #
    # Remove a CA cert by its alias
    @remove-alias: <a CA alias name>
    #
    # Remove all CA certs
    @remove-all
    #
    # Add a new CA cert with
    @alias: newaliasname
    -----BEGIN CERTIFICATE-----
    .....
    -----END CERTIFICATE-----

1. The first line must be `# CACERTS`
1. The `@remove-alias` directive can be used to remove a CA cert by its alias. Users can call `keytool -cacerts -list` to find out the aliases for all builtin CA certificates.
1. The `@remove-all` directive can be used to remove all CA certs.
1. The `@alias` directive following a BASE64 encoded X.509 certificate can be used to add a new CA cert with this alias.

Note:

1. This file format will be used by the new CACERTS keystore type below.
1. When parsing starts, all hardcoded builtin CA certs are loaded (the current list).
1. All directives are processed in their appearance order in the file. For example: Users can add `@remove-all` as the first directive in a CACERTS file to remove all builtin CA certs and then add their own ones. Or, they can add a `@remove-alias: verisignclass2g2ca [jdk]` directive to remove this single builtin CA cert and then (optionally) add their own ones. Please note that if `@remove-all` is on the last time, it will empty the whole list including newly added CA certs.
1. If a BASE64 encoded X.509 certificate appears without a leading `@alias` directive, its SHA-256 fingerprint will be used as its alias.
1. Unknown directives (other lines starting with `@`) will be treated as an error.
1. All other lines are ignored (as comments).
1. The out-of-box `lib/security/cacerts` will be all comment lines except for the first line. All existing (93) cacerts have been hardcoded inside OpenJDK using the same aliases as before.

### The CACERTS keystore type

1. When using `KeyStore.getInstance(File, password)` to load a file starting with the `# CACERTS` line, it will be probed as a CACERTS keystore.
1. The store password is ignored in `KeyStore.getInstance(File, password)` and `KeyStore.load(InputStream, password)`.
1. When calling `KeyStore.load(null, password)`, the system-default CACERTS keystore at `lib/security/cacerts` is loaded.
1. The creation time of a certificate inside a CACERTS keystore is not defined but it will not be null.
1. Calling `KeyStore.setEntry` with a `KeyEntry` or `KeyStore.setKeyEntry` on a CACERTS keystore is allowed and the new entry is visible in the keystore object (but see below).
1. Calling `KeyStore.store(OutputStream, password)` will write out a CACERTS keystore to the output stream. All key entries are ignored. The password is ignored. Loading a keystore from a CACERTS file and storing it back to the same file will not retain the comments or the order of directives. The output stream cannot be null (i.e. one cannot write into the system-default CACERTS keystore by using a null output stream).

This new keystore type name will be added into the "Java Security Standard Algorithm Names" and the "JDK Providers Documentation" in the Java SE Security Guide.

### Compatibility with existing keystore types

1. A CACERTS file can be loaded as a JKS or PKCS12 keystore, and the load password is ignored. When `KeyStore.store` is called on such a keystore, a CACERTS file will be written, and key entries will be ignored. This makes sure the current programs that is using "JKS" to load/store the `cacerts` file can work with the new file format.
1. One can load a JKS keystore with the CACERTS store type. This makes sure that if a user has replaced the system-default cacerts file with a JKS keystore, it can still be loaded as a CACERTS keystore. When calling `KeyStore.store` on such a keystore, a JKS keystore will be written, if any key entry was added into the `KeyStore` object it will be stored, the store password will be used.
1. The CACERTS `KeyStore` type and the CACERTS file format can be separated. We can forsee in the future that the system-default root CA certs list to be stored in a different way but they can still be loaded with `KeyStore.getInstance("CACERTS").load(null, null)`.