io7m | single-page | multi-page | epub | Canonmill User Manual

Canonmill User Manual

CREATOR Mark Raynsford
DATE 2022-07-01T12:36:21+00:00
DESCRIPTION Documentation for the Canonmill keystore.
IDENTIFIER dcc7df2a-0093-4b52-8204-88d6f61395d6
LANGUAGE en
RIGHTS Public Domain
TITLE Canonmill User Manual

Table Of Contents

The canonmill package provides a Keystore implementation designed to be less painful from an operational perspective than any of the Keystore implementations included in the standard JDK.

1.2. Features

  • Exposes a simple directory-based keystore with a single JSON file that maps certificate aliases to files. Keys and certificates are expected to be PEM-encoded regular files.
  • Implicit compatibility with ACME systems; ACME clients can simply copy certificate files into the directory and, as long as the certificates have an entry in the JSON index file, the new certificates will become available as soon as the Keystore is reloaded.
  • A small, easily auditable codebase with use of modularity for correctness.
  • An extensive automated test suite with high coverage.
  • Platform independence. No platform-dependent code is included in any form.
  • OSGi-ready
  • JPMS-ready
  • ISC license
The package does not support encryption of the keystore with a password, or encryption of the keystore entries with passwords. In the author's opinion, this offers no real security. To elaborate, keystore encryption is intended to ensure that private keys are encrypted at rest so that, in the event of a compromise, the private keys cannot be used by an attacker. Unfortunately, this is ineffectual for a couple of reasons. Firstly, because nobody wants to have to manually type in a password each time their server-based Java application starts, the password is typically stored along with the application configuration. This means if an attacker compromises the application, they have both the keys and the passwords anyway. Secondly, if an application is compromised and the keystore is stolen, the keys within the keystore are going to have to be blacklisted and reissued anyway, so "protecting" them with a passphrase is of very little utility.
Use the following Maven dependency:

2.1.2. Maven Dependency

<dependency>
  <groupId>com.io7m.canonmill</groupId>
  <artifactId>com.io7m.canonmill.core</artifactId>
  <version>0.0.5</version>
</dependency>
To use the canonmill keystore, it's first necessary to create a directory that will contain PEM-encoded private keys and certificates. It's then necessary to create an index file that maps aliases to keys/certificates.
An an example, assume the following files exist:

2.2.3. Example Keys/Certificates

Name Description
www.key The private key for www.example.com
www.crt The public certificate for www.example.com
mail.key The private key for mail.example.com
mail.crt The public certificate for mail.example.com
Now, all of these files could be placed in a directory /etc/certs. We can now create the index file and place it anywhere the application wants it to be. The index file has a strictly-defined JSON format with a schema. An example index file might look like this:

2.2.5. Example Index

{
  "%Schema": "https://www.io7m.com/software/canonmill/keystore-1.schema.json",

  "BaseDirectory": "/etc/certs",

  "Keys": {
    "www.example.com": "www.key",
    "mail.example.com": "mail.key"
  },

  "Certificates": {
    "www.example.com": "www.crt",
    "mail.example.com": "mail.crt"
  }
}
Each entry in the Keys and Certificates map assigns an alias to a key or certificate, respectively. The filenames given are resolved relative to the directory given by the BaseDirectory property.
To load a keystore, the standard Keystore API should be used. Assuming that the keystore's JSON index file exists at file:

2.3.2. Example Load

final var ks =
  KeyStore.getInstance("CANONMILL", new CMKeyStoreProvider());

try (var stream = Files.newInputStream(file)) {
  ks.load(stream, null);
}
The CMKeyStoreProvider class is registered using the name CANONMILL as service of type java.security.Provider and so can be used in the same manner as any other provider in the JDK security API.
The JSON schema for the keystore's index file is as follows:

2.4.2. Keystore Schema

{
  "$id": "https://www.io7m.com/software/canonmill/keystore-1.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",

  "$defs": {
    "SchemaIdentifier": {
      "type": "string",
      "const": "https://www.io7m.com/software/canonmill/keystore-1.schema.json"
    },

    "Configuration": {
      "type": "object",
      "properties": {
        "%Schema": {
          "$ref": "#/$defs/SchemaIdentifier"
        },
        "BaseDirectory": {
          "type": "string"
        },
        "Keys": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "Certificates": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      },
      "additionalProperties": false,
      "required": [
        "%Schema",
        "BaseDirectory",
        "Keys",
        "Certificates"
      ]
    }
  },

  "$ref": "#/$defs/Configuration"
}
io7m | single-page | multi-page | epub | Canonmill User Manual