This directory contains:

- mkcert.py
  Python script; uses the cryptography package to deterministically generate X509 certificates,
  CRLs, and digests based on the contents of the specified certificate definition file.
- main_certs_def.bzl
  Main certificate definitions, embedded in a Bazel definition file.
- apple_certs_def.bzl
  Certificate definitions for certs to be installed on provision of OSX machines.

To run:

```
python x509/mkcert.py CONFIG [--mkcrl | --no-mkcrl] [-o OUTPUT] [--static-dir STATIC_DIR]
    [--dry-run] [--certificate-generation-year-file YEAR_FILE] [certs ...]
```

- CONFIG is the path to the JSON file specifying a list of certificates, required
- OUTPUT is the path to a directory where the generated items will be stored, default .
- STATIC_DIR is the path where signing keys needed by certificates are stored, default x509/static
- If --mkcrl is specified, CRLs will be generated after certificate generation ends. Default false.
  These are hardcoded and require certain certificates to be generated to work.
- If --dry-run is specified, no files will be written out. This can be used to test what files will
  be written and where.
- If --certificate-generation-year-file is specified, midnight on January 1 of the year contained
  in YEAR_FILE is used as the base time for certificate and CRL validity. The current UTC year is
  used when this option is omitted.
- certs is an optional list of certificate names to generate. If it is not specified, all
  certificates specified in the config are generated.

If a certificate specified in certs references other certificates, its dependencies and
subdependencies will be generated.

Deterministic generation is based on the selected certificate generation year. This means that if
mkcert is run on the same definitions file twice with the same year, it will produce the same
certificates. One exception to this is the pkcs12 format; the cryptography library does not provide
functionality for generating pkcs12 bundles deterministically.

Bazel first extracts the UTC year from stable workspace status into a dedicated year file, then
passes that file to mkcert. Only the extraction action depends on the complete stable-status file,
so unrelated status changes can reuse cached certificate generation while a UTC year change
invalidates it.

Future work:

- Define CRLs in the definition file instead of hardcoding them.
- Define keys in the definition file, and make a script to generate all necessary keys which would
  be run whenever a new key was defined.

Certificate definition format:

```
{
  "global": {
    # Optional, default value to use for Key1 for all certs, overridden by values in cert entries.
    "Key1": "Value1",
    ...
  },
  "certs": [
    {
      # Required, this will be used as the name of the file, and for referencing issuers.
      "name": "name-of-cert.pem",
      # Required, this will be included in the header of the generated certificate.
      "description": "Tell us about yourself.",
      # Required, The X509 subject name.
      "Subject": { "C": "US", "ST": "New York", ... },
      # Required, Who is the (intermediate) CA for this certificate.  May be 'self'.
      "Issuer": "ca.pem",
      # Required, relative (within static directory) path to the keyfile to sign this certificate with.
      "keyfile": "key.pem",
      # Optional, set to true to ignore global.Subject values.
      "explicit_subject": False,
      # Optional, serial number to assign this certificate (default: sequential numbers starting from 1000)
      "serial": 42,
      # Optional, validity start date, expressed in seconds relative to midnight on the first day of the current year.
      "not_before": -86400, # 1 day before
      # Optional, validity end date, currently expressed in seconds relative to midnight on the first day of the current year.
      # Note that not_after - not_before, the validity period, should be less than or equal to 825 days, see:
      # https://support.apple.com/en-us/HT210176
      "not_after": 71107200, # 823 days after
      # Optional, IDs of other public keys to append to the file
      "append_certs": ["ca.pem", "intermediate-ca.pem", ...],
      # Optional, passphrase to encript private key with
      "passphrase": "secret",
      # Optional, make a pkcs12 copy of the certificate
      "pkcs12": True | {
          # Optional, all PKCS#12 keys must be encrypted. Will use cert.passphase if not provided.
          "passphrase": "secret",
          # Optional, name of PKCS#12 version of certificate. If not provided, the original cert will be overwritten with the PKCS#12 version
          "name": "name-of-cert.pfx",
      },
      # Optional, in addition to the .pem file, write just the certificate to a .crt file and just the signing key to a .key file
      "split_cert_and_key": True,
      # Optional, X.509 extensions to include in the certificate
      "extensions": { # All extensions are optional.
        "basicConstraints": {},
        "keyUsage": {},
        "extendedKeyUsage": {},
        "subjectAltName": {"DNS": [...], "IP": [...]},
        "subjectKeyIdentifier": "hash",
        "authorityKeyIdentifier": "keyid" | "issuer",
        "authorityInfoAccess": {
          "method": "OCSP",
          "location": "uri-to-OCSP-server",
        },
        "mustStaple": True,
        "nsComment": "Comment",
        "mongoRoles": [
          {"role": "readWrite", "db": "test1"},
          {"role": "read", "db": "test2"}
        ],
        "mongoClusterMembership": "clusterName",
      }
    },
    ...
  ]
}
```