When working with internal production environments or development environments, it can be useful to have an Certificiate Authority (CA) to issue SSL Certificates to your infrastructure or services. This means that you don’t have to worry about obtaining externally valid SSL Certificates.
In my recent work I’ve been building up a number of development environments, and I hate having to remember the exact sequence and commands that I require to build up a new CA, or to issue a new certificate. Therefore I built a simple bootstrap script to build the CA, and another script to issue certificates with minimum manual editing required.
First, create a new working directory in which you can store all your Certs. For the example we will just use ‘ssl’. Do not have any spaces in the paths otherwise the bootstrap script will break.
mkdir ssl
cd into that directory and create a new file called ‘generate.sh’
cd ssl
Clone the following repository:
Scripts to generate a fully functional Self-Signed Certificate Authority
git clone https://github.com/tfindley/Self-Signed-CA.git
You should now see the following files inside your SSL Directry:
ssl
├── config.json.sample
├── default.json
├── genca.sh
├── gencnf.sh
├── gencrt.sh
├── README.md
At this point it is a good idea to review the three .sh files for their content. Never just run a script that you’ve downloaded from the internet. Whatever you do, do not run this with root privilages!
Ensure the three .sh files are all executable
chmod +x *.sh
Now you can go ahead and create your Certificate Authority
You can quickly boostrap this process by creating a config.json
file based on the config.json.sample
file I provided.
{
"domain": "my.awesome.domain.tld",
"country": "GB",
"state": "Farnborough",
"locality": "Hampshire",
"organization": "My Awesome Company",
"organizational_unit": "IT Wizards",
"common_name": "My Awesome CA"
}
Once you’ve edited the above fields, run the genca.sh script to generate your certificate authority
./genca.sh
Choose a passphrase and enter it to secure your key when prompted (twice). Then when prompted enter the passphrase again to use that key to generate your certificate.
./genca.sh
config.json detected - loading variables... done.
Building CA Key
+........+.......+...+..+.+......+.....+......+.......+...+........+.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
Building CA Certificate
Enter pass phrase for /path/to/ssl/my.awesome.domain.tld/ca/myCA.key:
Finished
------------------------------------------------------------------------
Domain Configuration: /path/to/ssl/my.awesome.domain.tld/config.json
Certificate Authority PEM: /path/to/ssl/my.awesome.domain.tld/ca/myCA.pem
Certificate Authority Key: /path/to/ssl/my.awesome.domain.tld/ca/myCA.key
Certificate Authority Configuration: /path/to/ssl/my.awesome.domain.tld/ca/myCA.cnf
------------------------------------------------------------------------
Congratulations - you now have a working Certificate Authority.
Inside your ssl directory you should now see a directory with the name of the domain you entered (in this example, my.awesome.domain.tld
). You should have the following directory structure:
ssl
├── config.json.sample
├── default.json
├── genca.sh
├── gencnf.sh
├── gencrt.sh
└── my.awesome.domain.tld
├── ca
│ ├── myCA.cnf
│ ├── myCA.key
│ └── myCA.pem
├── cnf
├── config.json
├── crt
│ ├── archive
│ └── live
└── csr
Generate CSR#
Now you can generate your first Certificate Signing Request.
Run the gencsr.sh
script and it will guide you through the process
./gencnf.sh
You can optionally pass the domain name, or the domain name and subdomain to it to speed up the process
./gencnf.sh my.awesome.domain.tld
or
./gencnf.sh my.awesome.domain.tld service
./gencnf.sh my.awesome.domain.tld service
Domain is: my.awesome.domain.tld
Subdomain is: service
CSR will be created for service.my.awesome.domain.tld
Would you like localhost/127.0.0.1 to be added to the certificate?
(yes/no) [yes]:
Optional:
Enter alternate DNS names (Subject Alternate Names)
These must be Fully Qualified Domain Names (FQDN)
e.g:
something.my.awesome.domain.tld
or:
something.somewhere.my.awesome.domain.tld
You do not need to enter service.my.awesome.domain.tld again
(press enter after each, leave empty to finish):
DNS.1 = service.my.awesome.domain.tld
DNS.2 = localhost
DNS.3 =
Recommended:
Enter IP address for each server or adapter
(press enter after each, leave empty to finish):
IP.1 = 127.0.0.1
IP.2 =
Finished
------------------------------------------------------------------------
Certificate generated for: service.my.awesome.domain.tld
Alternative Names:
DNS.1 = service.my.awesome.domain.tld
DNS.2 = localhost
IP.1 = 127.0.0.1
------------------------------------------------------------------------
Domain Configuration: /path/to/ssl/my.awesome.domain.tld/config.json
Certificate Configuration: /path/to/ssl/my.awesome.domain.tld/cnf/service.my.awesome.domain.tld.cnf
------------------------------------------------------------------------
Generate CRT#
Now that you have your Certificate Signing Request ready to go, you can generate your certificate.
Run the gencrt.sh
script and it will guide you through the process
./gencrt.sh
Again, you can optionally pass the domain name, or the domain name and subdomain to it to speed up the process
./gencrt.sh my.awesome.domain.tld
or
./gencrt.sh my.awesome.domain.tld service
When prompted, enter the passphrase you used to secure your CA Key. This is used to sign the certificate.
./gencrt.sh my.awesome.domain.tld service
Domain is: my.awesome.domain.tld
Subdomain is: service
Cert will be created for service.my.awesome.domain.tld
Creating new cert directory
Generating Key... done
Generating CSR... done
Generating CRT...
Certificate request self-signature ok
subject=C = GB, ST = Farnborough, L = Hampshire, O = My Awesome Company, OU = IT Wizards, CN = service.my.awesome.domain.tld
Enter pass phrase for /path/to/ssl/my.awesome.domain.tld/ca/myCA.key:
done
Create Chain... done
Assemble Full Chain... done
Finished
------------------------------------------------------------------------
Certificate PEM: /path/to/ssl/my.awesome.domain.tld/crt/live/service.my.awesome.domain.tld/cert.pem
Certificate Key: /path/to/ssl/my.awesome.domain.tld/crt/live/service.my.awesome.domain.tld/privkey.pem
Certificate Chain: /path/to/ssl/my.awesome.domain.tld/crt/live/service.my.awesome.domain.tld/chain.pem
Certiticate Full Chain: /path/to/ssl/my.awesome.domain.tld/crt/live/service.my.awesome.domain.tld/fullchain.pem
------------------------------------------------------------------------
Your new certificates will appear under ssl/my.awesome.domain.tld/crt/live/name.subdomain.domain.tld/
If a certificate existed by that name previously, it will be moved to ssl/my.awesome.domain.tld/crt/archive/name.subdomain.domain.tld.YYYYMMDD-HHMMSS/
You will have the following files (these filenames will match LetsEncrypt / Certbot generated certs):
- cert.pem - your certificate
- chain.pem - the chain for the certificate, also known as your CA or Certificate Authority
- fullchain.pem - Your certificate plus your CA
- privkey.pem - The private key for your certificate
By the end of this, your tree will look like the following (including two domain examples with one service each which has had its certificate re-issued once):
ssl
├── config.json.sample
├── default.json
├── genca.sh
├── gencnf.sh
├── gencrt.sh
└── my.awesome.domain.tld
├── ca
│ ├── myCA.cnf
│ ├── myCA.key
│ └── myCA.pem
├── cnf
| └── service.my.awesome.domain.tld.cnf
├── config.json
├── crt
│ ├── archive
│ │ └── service.my.awesome.domain.tld.20240826-152506
│ │ ├── cert.pem
│ │ ├── chain.pem
│ │ ├── fullchain.pem
│ │ └── privkey.pem
│ └── live
│ └── service.my.awesome.domain.tld
│ ├── cert.pem
│ ├── chain.pem
│ ├── fullchain.pem
│ └── privkey.pem
└── csr
└── service.my.awesome.domain.tld.csr