Skip to content

jeanfrancoisgratton/certificateManager

Repository files navigation

certificateManager

A GO tool to generate and sign all of your SSL certificates

Overview

This tool uses GO's x509 package to:

  • Generate
  • Sign
  • Verify all of your SSL certificates, your custom Root Certificate Authority (Root CA) certs, and even your Java certs in the .JKS (Java Keystore) certificates.

What that tool does do

- Create your own custom PKI (Private Key Infrastructure), and handle all certificates from under it - Create, if needed, a root CA cert
- Create and sign "normal" SSL server certs
- Verify those certs
- Revoke certs

What that tool does not do

- Sign certificates against a remote CA:
- No CRL (Certificate Revokation List) is implemented
- No CDP (Certificate Distribution Point) is implemented
- Any operation against a remote CA, actually.

Bear in mind : this software is intended to run on an internal network.
For instance, if you wish to publish your own rootCA certificate, it's yours do deploy it at VeriSign, etc....

Concepts

Environments

In most use cases, you would see a single rootCA or rootCA + intermediateCA present in a PKI.

You might wish to have this tool in a docker container and be able to manage multiple PKIs, so to facilitate this, we introduce the idea of environments.

An environment is a set of rules that define the directory structure that will be used by a PKI. This allows handling of multiple PKIs as sandboxed environments.

The contents of an environment file is such:

{
  "CertificateRootDir": "/Users/jfgratton/.config/JFG/certificatemanager/certificates",
  "RootCAdir": "rootCA",
  "ServerCertsDir": "servers",
  "CertificatesConfigDir": "conf",
  "RemoveDuplicates": true
}

The file is in JSON format; every key in the file (except the last one, RemoveDuplicates) are string values representing a path. The first path must be absolute, while the others are relative to it.
(btw... RemoveDuplicates is meaningless for now, as that key is not treated -yet- anywhere in my code)

You switch between environments with the -e flag. Not using this flag will assume that you use the default environment file, $HOME/.config/certficatemanager/default.Env , assuming of course that the file is there.

Certificates, root certificates and certificate config files

A certificate (extension .crt) is the actual x509 SSL file that you might wish to deploy on a server, for example. The root certificate (or rootCA) is the certificate used to sign (validate) all other certificates within the PKI. A certificate config file is the JSON file that this tool uses to generate the certificate file.

A typical certificate config file looks like this:

{
  "Country": "CA",
  "Province": "Quebec",
  "Locality": "Blainville",
  "Organization": "myorg.net",
  "OrganizationalUnit": "myorg",
  "CommonName": "myorg.net root CA",
  "IsCA": true,
  "EmailAddresses": [
    "cert@myorg.net",
    "cert@org,net"
  ],
  "Duration": 10,
  "KeyUsage": [
    "cert sign",
    "crl sign",
    "digital signature"
  ],
  "DNSNames": [
    "myorg.net",
    "myorg.com",
    "lan.myorg.net"
  ],
  "IPAddresses": [
    "10.0.0.1",
    "127.0.0.1"
  ],
  "CertificateName": "sampleCert",
  "SerialNumber": 1,
  "Comments": [
    "To see which values to put in the KeyUsage field, see https://pkg.go.dev/crypto/x509#KeyUsage",
    "Strip off 'KeyUsage' from the const name and there you go.",
    "",
    "Please note that this field offers no functionality and is strictly here for documentation purposes"
  ]
}

PKI / environment directory structure

As mentioned above, an environment is a sandbox. Different environments represent different PKIs. We'll use the variables from sampleEnv.json here to describe the structure.

Here, I have an environment called test (test.json) :

[16:56:29|jfgratton@bergen:certificatemanager]: cm env ls
Number of environment files: 1
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Environment file ┃ File size ┃ Modification time   ┃
┣━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━┫
┃ test.json        ┃ 145       ┃ 2023/10/02 16:56:29 ┃
┗━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━┛

[16:56:38|jfgratton@bergen:certificatemanager]: cm env explain test
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Environment file ┃ Certificate root dir ┃ CA dir ┃ Server certificates dir ┃ Certificates config dir ┃
┣━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ test.json        ┃ /test                ┃ CA     ┃ srv                     ┃ cfg                     ┃
┗━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━┛

So you see, my environment / sandbox / PKI, sits under /test/

Now, I've cheated a bit here, I've already created some certs, to show you the directory structure:

[17:16:19|jfgratton@bergen:/test]: cm -e test cert ls
Number of certificates: 4
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Cert name    ┃ Common Name       ┃ File size ┃ Modification time   ┃
┣━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━┫
┃ gitea.json   ┃ git.myorg.net     ┃ 488       ┃ 2023/10/02 17:16:11 ┃
┃ haproxy.json ┃ haproxy.myorg.net ┃ 515       ┃ 2023/10/02 17:16:17 ┃
┃ nexus.json   ┃ nexus.myorg.net   ┃ 518       ┃ 2023/10/02 17:16:19 ┃
┃ testCA.json  ┃ myorg.net root CA ┃ 726       ┃ 2023/10/02 17:15:28 ┃
┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━┛


A NOTE ABOUT cm cert ls:
This command lists certificate config files, not certificate files. This means a config file might be present, but no valid certificate being present.
If you wish to see that a certificate exists (and is valid) : cm cert verify $PATH_TO_CERTIFICATE_FILE

cm env explain test, above, reflects the following directory structure:

With test as being the root PKI directory, we get this:

test
├── CA
│   ├── index.txt
│   ├── index.txt.attr
│   ├── newcerts
│   │   ├── 0002.pem
│   │   ├── 0003.pem
│   │   └── 0004.pem
│   ├── serial
│   ├── testCA.crt
│   └── testCA.key
├── cfg
│   ├── gitea.json
│   ├── haproxy.json
│   ├── nexus.json
│   └── testCA.json
└── srv
    ├── cert
    │   ├── gitea.crt
    │   ├── haproxy.crt
    │   └── nexus.crt
    ├── csr
    │   ├── gitea.csr
    │   ├── haproxy.csr
    │   └── nexus.csr
    ├── java
    └── private
        ├── gitea.key
        ├── haproxy.key
        └── nexus.key

In this structure:
CA is the root PKI directory:
cfg is the config dir, where all certificates config files are stored
srv is where you store all certificates files, private keys, java keys (jks, p12)

  • index.txt is the main database that stores every certificate in the PKI, including the rootCA
  • serials is the latest generated certificate serial
  • newcerts/ is the directory holding a copy of the generated certificates; the filename is an hexidecimal-translated number (from the cert serial number)
  • cfg/ is the certificate configuration files
  • srv/cert/ contains the certificates themselves
  • srv/csr/ contains the certificate signing request; I keep these in case you want to make your PKI structure public
  • srv/private/ contains the certificate private key (needed by CSR)
  • srv/java/ are the certificates (.crt), converted in PKCS#12 and JKS formats, for Java usage

The notable exception is the CA itself, and its key: both reside in the CA directory itself.

How do we use the software

Create an environment file

As mentioned earlier, at the initial run of the software, it will create a few files in $HOME/.config/JFG/certificatemanager:

  • sampleCert.json
  • sampleCert-README.txt
  • sampleEnv.json
  • sampleEnv-README.txt

Read both .txt files for further explanations. For now, the software is not yet usable; you need an environment file to run.
By default, if you do not pass an -e argument to the app, it will assume -e defaultEnv.json (you do not need to provide the extension, btw)

The easiest way, then, to create that default Environment file is: cm env create.
You could add a filename such as test. If you provide another name, this means that any execution of the app will need the -e ENVFILE args (ENVFILE being the filename you've selected)

By default, the software runs with the -e defaultEnv.json flag as a default environment file (which is why you need to adapt the above file with sane values). This will create the correct directory structure this software needs to operate

Create a CA cert

The very first step in building your own PKI is to have a root CA (root certificate authority)
Either you already have your own json CA file (`cfg/rootCA.json`, in this example), or you will need to create your own:

You have your own config

`cm cert create rootCA` (assuming that your CA config file is actually `rootCA.json`)

Create your own config

`cm cert create` <-- ensure that you select TRUE for a root CA when prompted

Create "standard" SSL certs

The process is exactly as the one above, except that this time you specify that you are not creating a CA certificate

This means that you follow the steps, above, and if you create a new file, you will need to answer FALSE to the prompt where it asks you if this is a CA cert.

Java certificates

A Java certificate can be created with the `-j` flag with `cm cert create`.
This flag will convert the newly-created `.crt` certificate in a PKCS#12 format (`.p12` file), and then, convert that PKCS#12 in a Java Keystore (`.jks`) file.

**HUGE CAVEAT:** In order to convert the `.p12` to `.jks`, we need an external tool, `keytool`, which is provided by any Java SDK or JRE. For many reasons, *I do not factor that dependency in my binary package build toolchain*, the main reason being that Java package names are inconsistent on any given distro.

In a future release, I will provide a flag to ignore the conversion from PKCS#12 to Java Keystore.

Revoke certs

Simple: `cm cert revoke $CERTCONFIGFILE`
You just name the cert config file (as per `cm cert ls`), and that's it.

Building, installing CertificateManager

I provide both the source code and Alpine (APK), Debian-based (DEB) or RedHat-based (RPM) binary packages.

Install from source

- Clone the repo
- from the `src/` directory, run: `./build [-o OUTPUTDIR]`, where OUTPUTDIR is where you want the final binary to be copied. By default it uses `/opt/bin/`

NOTE: The script assumes that the building user has sudo rights to write in /opt/bin and strip the binary from debugging code

Install from binary packages

Go in the Releases link from this site, and pick your package, once downloaded, in say, `/tmp/` :

Alpine (APK)

```bash $ apk add /tmp/$PACKAGENAME ```

Debian-based (DEB)

```bash $ apt install /tmp/$PACKAGENAME ```

RedHat-based (RPM)

```bash $ dnf localinstall /tmp/$PACKAGENAME ```

A note about some extra directories and files

The following directories:
- __alpine/
- __debian/
- ./certificateManager.spec
- ./rpmbuild-deps.sh

Those dirs and files are needed for my own home setup. That setup relies on my own custom Docker containers to build the binary packages. Those containers will be made available once I manage to strip them from my personal information, but it is an involved process, so for now don't count on them. (teaser: it's a pity, it works so well :D)

About

Go tool to manage server certificates and custom root CA certs

Resources

License

Stars

Watchers

Forks

Packages

No packages published