A revised perl script to manage a small private Certificate Authority (CA) based on CSP-0.3.4 by Leif Johansson. Revised by James B. Byrne [email protected]
This is a command line driven application used to create and administer a private Public Key Infrastructure (PKI).
This script is simply a small wrapper around OpenSSL. It likely will not scale well but for small organisations requiring a publiclly accessable Certificate Policy (CP) and Certificate Practices Statement (CPS) together with providing web accessible and downloadable PEM and DER formats for fewer than several hundred generated certificates this should suffice.
More complete documentation can be obtained from cspguide.pdf found
in the docs
directory.
Administrating Public Key Infrastructure (PKI) Certificate Authorities (CA) using CSP.
2016-Nov-01 [email protected]
This procedure creates an X509
PKI with a root CA with subordinate CAs
issing end-use certificate. For reasons of security the private keys of
all CA's that form part of a deployed PKI should be kept offline and
physically secured when not in use. Likewise, all private keys of any
end-use certificates must be controlled in the same fashion.
Whenever a CA private key is in use then the host employed should be first disconnected from all networks (LAN and Internet). Consequently It will likely prove most convenient to keep the CA directory trees on removable media such as USB flash memory sticks or SD cards. When so employed removable media must be stored in a secure location and only inserted into the host used to manage the PKI when required and under direct supervision of an authorised user.
It is advised that a separate user id be created solely for the purpose of administering the PKI. Failing that then a system priviledged user should be used. In either case care must be taken that any situation where the private keys might be compromised is never permitted to arise. That implies that the medium holding the CA keys is never left unsecured; or unobserved when in use; or inserted into a host that has multiple user access enabled.
OpenSSL permits the use of key encypherment pass phrases that include
embedded spaces. However, the csp
script presently passes all its
arguments to openssl
as a single string. The method used to call
openssl
from within the perl script treats a space as a delimiter
for arguments passed in this fashion. As single or double quotes
are stripped by the Perl module used to invoke openssl
there is no
method of escaping pass-phrases that contain spaces.
Thus it is not possible to use a pass-phrase containing spaces with csp.
If such a phrase is desired then openssl rsa
must be called directly to
reset the key encryption phrase after it is created:
openssl rsa -v -aes256 -in short_password.key -out passwd_with_spaces.key
Some applications no longer allow self-signed certificates to be added their trusted certificate store. Thus, to meet this requirement, it is necessary that a private PKI use a CA other than their root CA to issue end-use certificates.
Generally speaking, when subordinate issuing CAs are employed in a PKI, the root CA is only used to create additional issuing CA's; or replacements for compromised issuing CA's.
PATH=$PATH:/path/to/csp/
mkdir -p /path/to/my-ca
cd /path/to/my-ca
cp -pr /path/to/csp/ca/etc ./etc
export CSPHOME=$(pwd)
export OPENSSL=$(which openssl)
csp CA_ROOT_2016 create
Copy a localised extensions.conf template file into ./csp or modify the default extensions.conf in place. At a minimum you need to change all example.com references to your local domain.
cp ./extensions.conf.ca_hll_root ./csp/CA_ROOT_2016/extensions.conf
This can be done in one step if one does not care what time of day the certificate expires. However, if that route is taken then there is no entry made in the root's certificate database for its own certificate and the time of expiry is the same as the time of day when its own certificate is created.
To specify an exact time of day to expire (i.e. midnight UTC) one
cannot use the default openssl ca
command as that only takes as an
argument the number of days to expiry. Instead issue a certificate
signing request by specifying a csr file. Then self sign that request
providing the exact expiry date, time and timezone. This requires
that we first capture the csp generated openssl.conf
file since
openssl
requires a vaild config file to operate.
The temporary config file generated by csp may be retained by defining
the environment variable CSPDEBUG
. The resulting csp-9999.conf
file
is found in ./csp/CA_ROOT_2016/tmp/
. The exact serial number
will vary and a new csp-xxxx.conf
file created each time a csp command
is issued.
export CSPDEBUG=true # any value or none, it just needs to be defined.
Create a signing request file ca.csr
instead of automatically self-signing
the CA certificate by specifying --csrfile=
.
csp CA_ROOT_2016 init \
--type=root \
--keysize=4096 \
--digest=sha512 \
--url=ca.harte-lyne.ca \
[email protected] \
--csrfile=./csp/CA_ROOT_2016/csrs/ca.csr \
"CN=CA_ROOT_2016,OU=Networked Data Services,\
O=Harte & Lyne Limited,L=Hamilton,ST=Ontario,C=CA,DC=harte-lyne,DC=ca"
Copy the generated conf file from ./csp/CA_ROOT_2016/tmp
to ./CA_ROOT_2016.conf
ls -l ./csp/CA_ROOT_2016/tmp/*conf
-rw-rw-r--. 1 cspuser cspuser 2966 Nov 1 13:52 ./csp/CA_ROOT_2016/tmp/csp-9999.conf
cp ./csp/CA_ROOT_2016/tmp/csp-9999.conf ./CA_ROOT_2016.conf
Note that the start and end date arguments have the expected effect on the certificate validity date and time.
openssl ca \
-selfsign \
-config ./CA_ROOT_2016.conf \
-startdate 20161101000000Z \
-enddate 20361031235959Z \
-keyfile ./csp/CA_ROOT_2016/private/ca.key \
-infiles ./csp/CA_ROOT_2016/csrs/ca.csr
This creates the first certificate, 01.pem
, in the
./csp/CA_ROOT_2016/certs/
directory. Note that csp
serialises
certificate numbers in hexadecimal. When the tenth certicicate is
produced the serial number will be 0A
and the certificate will be
saved as 0A.pem
.
The first serial number defaults to 01
. This can be changed by
modifying the serial
file for the CA. If this modification is
made then take care not to introduce the possibility of reusing
previously issued serial numbers. The value is best left alone
once the first certificate is issued by the CA.
echo 20160001 > ./csp/CA_ROOT_2016/serial
The example above causes certificate serial numbers to start at 20160001 (hexadecimal).
csp
expects that the CA certificate is called ca.crt
and resides
in the root of the CA's working directory. csp
also expects that its
private key will likewise be named ca.key
and reside in the
private/
directory under the CA's working directory root.
If the default csp CANAME init
path is taken and a separate ca.csr
request is not generated then both these files are placed in those
locations automatically. As the example above does not follow that path
some further housekeeping is required.
Copy ./csp/CA_ROOT_2016/certs/01.pem to /csp/CA_ROOT_2016/ca.crt
.
cp ./csp/CA_ROOT_2016/certs/01.pem to /csp/CA_ROOT_2016/ca.crt
Do not delete, remove or rename ./csp/CA_ROOT_2016/certs/01.pem
or
any other file comprising the CA database. The serial number must be the
name of a certificate's associated .pem
file and this must reside in
the certs/
directory. Likewise the index.txt
file must not be edited,
removed or renamed.
Manually manipulating files in the CA database has a very real risk of
damaging the PKI to the point of uselessness. The exception being
extensions.conf
which often requires customisation for a specific
signing request.
Warning: Do not even try to reuse a serial number or attempt to clean up the CA database following a botched issue or signing attempt. Serial numbers are cheap and limitless. Just revoke the resulting certificate and move on to the next number.
csp CA_ROOT_2016 request \
--type=ca \
--keyfile=./csp/CA_ROOT_2016/tmp/CA_ISSUER_2016.key \
--keysize=4096 \
--digest=sha512 \
--url=ca.harte-lyne.ca \
[email protected] \
--csrfile=./csp/CA_ROOT_2016/csrs/CA_ISSUER_2016.csr \
"CN=CA_ISSUER_2016,OU=Networked Data Services,O=Harte & Lyne Limited,L=Hamilton,ST=Ontario,C=CA,DC=harte-lyne,DC=ca"
Because csp
does not use openssl ca
to sign requests one can
specify the exact end-date and time in the signing command.
csp CA_ROOT_2016 sign \
--type=ca \
--digest=sha512 \
--startdate=20161101000000Z \
--enddate=20351101235959Z \
--url=ca.harte-lyne.ca \
[email protected] \
--csrfile=./csp/CA_ROOT_2016/csrs/CA_ISSUER_2016.csr
ls -l ./csp/CA_ROOT_2016/certs
total 24
-rw-rw-r--. 1 cspuser cspuser 9556 Nov 1 14:00 01.pem
-rw-rw-r--. 1 cspuser cspuser 9569 Nov 1 15:38 02.pem
mv ./csp/CA_ROOT_2016/tmp/CA_ISSUER_2016.key ./csp/CA_ROOT_2016/private/keys/
This is the key which we will use for the issuing CA setup that follows.
These steps are very similar to those used to create the root CA.
csp create
tree csp
csp
├── CA_ISSUER_2016
│ ├── certs
│ ├── crl_extensions.conf
│ ├── csrs
│ ├── extensions.conf
│ ├── index.txt
│ ├── private
│ │ └── keys
│ ├── public_html
│ │ ├── certs
│ │ │ ├── cert.html.mpp
│ │ │ ├── expired.html.mpp
│ │ │ ├── index.html.mpp
│ │ │ ├── revoked.html.mpp
│ │ │ └── valid.html.mpp
│ │ └── index.html.mpp
│ ├── serial
│ └── tmp
└── CA_ROOT_2016
├── ca.crt
├── certs
│ ├── 01.pem
│ └── 02.pem
├── crl_extensions.conf
├── crl-v1.crl
├── crl-v1.pem
├── crl-v2.crl
├── crl-v2.pem
├── csrs
│ ├── ca.csr
│ └── CA_ISSUER_2016.csr
├── extensions.conf
├── index.txt
├── index.txt.attr
├── index.txt.attr.old
├── private
│ ├── ca.key
│ └── keys
│ ├── CA_ISSUER_2016.key
│ └── CA_ROOT_2016.key
├── public_html
│ ├── certs
│ │ ├── cert.html.mpp
│ │ ├── expired.html.mpp
│ │ ├── index.html.mpp
│ │ ├── revoked.html.mpp
│ │ └── valid.html.mpp
│ └── index.html.mpp
├── serial
└── tmp
csp CA_ISSUER_2016 init --crtfile=./csp/CA_ROOT_2016/certs/02.pem
The PKI hierarchy desends from the root CA. So the issing CA's private key and public certificate must be copied from the root CA store into the new issuing CA's directory tree.
cp from ./csp/CA_ROOT_2016/private/keys/CA_ISSUER_2016.key \
./csp/CA_ISSUER_2016/private/ca.key
chmod 600 ./csp/CA_ISSUER_2016/private/ca.key
In practice this file will require modification for nearly every certificate issued. This is the main reason that CSP is not suited for a large scale deployment.
cp ./extensions.conf.ca_hll_issuer ./csp/CA_ISSUER_2016/extensions.conf
mkdir -p ./public_html/CA_ROOT_2016/ ./public_html/CA_ISSUER_2016/
tree public_html
public_html
├── CA_ISSUER_2016
└── CA_ROOT_2016
2 directories, 0 files
for CSPCA in CA_ROOT_2016 CA_ISSUER_2016; \
do csp $CSPCA gencrl; \
csp $CSPCA genpublic --export=./public_html/$CSPCA; \
done;
tree ./public_html
./public_html
├── CA_ISSUER_2016
│ ├── ca.crt
│ ├── certdb.xml
│ ├── certs
│ │ ├── expired.html
│ │ ├── index.html
│ │ ├── revoked.html
│ │ └── valid.html
│ ├── crl-v1.crl
│ ├── crl-v2.crl
│ └── index.html
└── CA_ROOT_2016
├── ca.crt
├── certdb.xml
├── certs
│ ├── 01.crt
│ ├── 01.html
│ ├── 01.pem
│ ├── 02.crt
│ ├── 02.html
│ ├── 02.pem
│ ├── expired.html
│ ├── index.html
│ ├── revoked.html
│ └── valid.html
├── crl-v1.crl
├── crl-v2.crl
└── index.html
At this point the issuing CA may commence creating end use certificates. For many private CAs this generally implies that the issuing CA will create and archive the end use private key as well.
An employee private key and certificate is created thus:
csp CA_ISSUER_2016 issue \
--type=user \
--days=528 \
--digest=SHA512 \
--keysize=4096 \
[email protected] \
--url=mailto:[email protected] \
"CN=User Name,OU=Employee,O=Harte & Lyne Limited,L=Hamilton,ST=Ontario,C=CA,DC=hamilton,DC=harte-lyne,DC=ca"
In these cases distributing the resulting private key:
csp/CA_ISSUER_2016/private/keys/HH.pem
and certificate:
csp/CA_ISSUER_2016/certs/HH.pem
is the responsibility of the CA operator. Generally private keys should be encrypted when created and the two elements should be packaged together for transmission to the end user. Server keys typically are not encrypted and so these must be moved to their hosts via some secure method. Implicit with unencrypted server keys is that the hosts they are installed on must be physically secure from unauthorised access.
After each CA management session the operator must regenerate the Certificate Revocation List (CRL) and rebuild the public website. Both of these should be updated every thirty (30) days in any case.
The directory for outputting the html files can be any directory or
device accessible from the host. When no output location is specified
on the command line csp
defaults to /mnt/floppy
. This usually fails.
for CSPCA in CA_ROOT_2016 CA_ISSUER_2016; \
do csp $CSPCA gencrl; \
csp $CSPCA genpublic --export=./public_html/$CSPCA; \
done;
When signing a new certificate one may encounter the following openssl
error:
140128600782664:error:0E06D06C:configuration file routines:
NCONF_get_string:no value:conf_lib.c:335:group=TESTCA name=email_in_dn
This information provided in this error is completely irrelevant to the
actual cause. The most frequent cause of this error is attempting to add
a certificate to the certificate database (index.txt
) where the DN of
the new certificate is identical to the DN of a certificate already in
the database AND the setting unique_subject=yes
is found in index.txt.attr
.