We have more than 700 certs, generated for OpenVPN usage by Easy-RSA 2. I don't know how this happened (suspecting deleting one time by somebody index.txt
, serial
or both), but more than half of the generated certificates have identical serial numbers. Also, the original index.txt
contains only half of all certs (last half), not including previous.
So, new certs can be done, it's OK. But when I try to revoke a cert which is not in index.txt
, I have an error.
I tried to re-create index.txt
by script:
for cert in *.crt
do
echo "-> $cert"
enddate=`openssl x509 -enddate -noout -in $cert | sed 's/notAfter=//' | awk '\
{ year=$4-2000;
months="JanFebMarAprMayJunJulAugSepOctNovDec" ;
month=1+index(months, $1)/3 ;
day=$2;
hour=substr($3,1,2) ;
minutes=substr($3,4,2);
seconds=substr($3,7,2);
printf "%02d%02d%02d%02d%02d%02dZ", year, month, day, hour, minutes, seconds}'`
serial=`openssl x509 -serial -noout -in $cert |sed 's/serial=//'`
subject=`openssl x509 -subject -noout -in $cert |sed 's/subject= //'`
echo -e "V\t$enddate\t\t$serial\tunknown\t$subject" >>index.txt
done
It reads certs one-by-one, get their data and fills new index.txt
. Everything seems OK.
But, according to upper text, script fills it by certs, which has equal serial numbers. So, with this newly created index.txt
I can't do anything with certs (create, revoke, etc…)
The question is: are there any possibilities to repair index.txt
base if I have all certs? Or, maybe, somehow change the serial number of a cert (simple change the head of *.crt
file does nothing – serial stays old when queried by openssl
)
If not – I only need to revoke certs, which're not in index.txt
, can I do this without index.txt
base?
Best Answer
I'm going to try to get you pointed in the right direction, but I probably cannot take it to the end because I did not setup a test rig and duplicate the problem. My apologies in advance.
If you want the higher level overview of issuing a certificate in a private PKI, then see How do you sign Certificate Signing Request with your Certification Authority? It explains how you would do things manually if Easy-RSA was not doing it for you.
Another important document is RFC 4158, Internet X.509 Public Key Infrastructure: Certification Path Building. This is __the__ document which explains what matches are, and how you can use tuples like
{Issuer Distinguished Name,Serial Number}
or{Subject Distinguished Name,Public Key Identifier}
to compare two certificates for equivalency. OpenSSL uses this document for matching. Also see Section 3.5.15, "Endpoint Distinguished Name (DN) Matching" and Section 3.5.12, "Matching Key Identifiers (KIDs)".The serial numbers are supposed to be unique. This is the problem to overcome. Subject Distinguished Names (DN) are a different story. If your
openssl.cnf
hasunique_subject=yes
, then they cannot be duplicated. Ifunique_subject=no
, then the DNs can be duplicated.I think you need to do a few things. First, use a modern or updated version of the OpenSSL utilities. Here, "modern" means one of the later 1.0.2's or 1.1.0's. Previous versions of the utility had subtle problems in matching names and serial numbers.
Second, examine your config file (normally
openssl.cnf
but you can use a different, perhaps copied, file with-config filename
) and write down the relevant settings, likeserial.txt
andunique_subject=no
. I believe these are the relevant ones from[CA_Default]
fromopenssl.cnf
:Third, backup everything, especially the important stuff like
index.txt
andserial.txt
.Fourth, create a list of the certificates you want to revoke. The list might have entries like filenames -
john-doe-vpn.pem
. Move them into their own folder if you like. Preferably each should have a unique serial and they MUST all have the same Issuer name; theopenssl ca
andocsp
functions can't handle more than one Issuer at a time, although for OCSP the protocol could.Fifth, create a new
index.txt
containing a line for each serial. One approach is to extract the subject, serial and expiration from each cert file as in the script you posted, although you can fold most of the shell work into one openssl and one awk per cert:If it's difficult to (reliably) remove duplicate serials in advance, you can put everything in and then discard duplicates with
awk -F'\t' '!already[$4]++'
orsort -t$'\t' -k4,4 -u
or similar.Another approach available in 1.0.2 up, but only documented in 1.1.0, is to use
openssl ca [-config conffile] -valid certfile
to do this extraction automatically. But-valid
unnecessarily loads the CA privatekey each time so if your privatekey is password-encrypted, as is good practice, this will mean typing your password over and over; to save time temporarily replace the real CA key and cert with a scratch unencrypted key and matching but otherwise bogus (probably selfsigned) cert.-valid
won't write a duplicate serial entry, so you needn't worry about excluding or removing them.Put in the
serial
file a value which is at least the highest value of any previously issued cert; if you want to jump up to the next10000
or1000000
or whatever to be safe and perhaps also more clear, that's fine. You may need to setunique_subject=no
at this point.Sixth, mark each certificate (serial) in the
index
file as revoked. You can loop through the cert files usingopenssl ca -revoke
on each, but it's easier to just use awk like:Seventh, generate a CRL from this
index
withopenssl ca -gencrl [-crldays n] [-out file]
and/or set up an OCSP responder using it if (any of) the old certs specified the OCSP extension.Eighth, once you distribute the CRL and/or start running the (new) OCSP responder, all certificates with the affected serials are revoked and will cause communication to fail if used (and properly checked). If any of the duplicated serials are in certificates that your systems are still using, they must be replaced first. If you still have the request files (CSRs) from the systems using the affected certs, you can just re-issue with
openssl ca [-config conffile] [-in reqfile | -infiles reqfile...]
and send the new certs to the subject systems and have the operators of those systems install them. Otherwise you need to first have the operator of each system send you a CSR, which can be one they previously used (and saved) or a new one they generate.Finally, restore any 'good' entries (serials you didn't revoke) from the old
index
file, combining with any new entries for replacement certs issued in #8 just above. If you're running an OCSP responder (see above) you must also keep the revoked entries; it not it doesn't matter but is probably easier. Do not restore the old value toserial
if that is lower than the highest old cert or the highest new replacement cert, instead let it continue to increment from the new value.Regarding the
for-loop
and printing dates:Don't even worry about the dates. If they are expired they cannot be used if your PKI is functioning properly. If it makes you feel better, then delete the private key associated with certificate and public key.
All you care about is the list of certs to revoke, and their serial number and distinguished name. Here, your list would be composed of certs like (1) exiting employee who holds a non-expired certificate and private key (i.e., employee is retiring or terminated); (2) an employee who lost a device (i.e., the private key is in the wild); etc.
Another option you have... Burn the existing PKI to the ground and start over. In this case, step (1) is revoke the Root CA and all Intermediate/Subordinate CAs. Then, throw away the private key. Step (2) is create a new Root CA, issue new Intermediate/Subordinate CAs, and finally issue new end-entity certificates. For step (2), you can even do the signing party dance.
Believe it or not, OpenStack uses this strategy (or was looking into using it). Its sort of an "ephemeral PKI" that's meant to stick around long enough to meet your needs; and then be discarded when things go wrong.
For a laugh, you might want to check out Peter Gutmann's Engineering Security. He is ruthless when it comes to PKIs and Public CAs.