A GO tool to generate and sign all of your SSL certificates
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.
- Create and sign "normal" SSL server certs
- Verify those certs
- Revoke certs
- 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....
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.
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"
]
}
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 rootCAserials
is the latest generated certificate serialnewcerts/
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 filessrv/cert/
contains the certificates themselvessrv/csr/
contains the certificate signing request; I keep these in case you want to make your PKI structure publicsrv/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.
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
Either you already have your own json CA file (`cfg/rootCA.json`, in this example), or you will need to create your own:
`cm cert create rootCA` (assuming that your CA config file is actually `rootCA.json`) `cm cert create` <-- ensure that you select TRUE for a root CA when prompted
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.
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.
Simple: `cm cert revoke $CERTCONFIGFILE`
You just name the cert config file (as per `cm cert ls`), and that's it.
I provide both the source code and Alpine (APK), Debian-based (DEB) or RedHat-based (RPM) binary packages. - 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
- __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)