137

In 2004, I set up a small certification authority using OpenSSL on Linux and the simple management scripts provided with OpenVPN. In accordance with the guides I found at the time, I set the validity period for the root CA certificate to 10 years. Since then, I have signed many certificates for OpenVPN tunnels, web sites and e-mail servers, all of which also have a validity period of 10 years (this may have been wrong, but I didn't know better at the time).

I have found many guides about setting up a CA, but only very little information about its management, and in particular, about what has to be done when the root CA certificate expires, which will happen some time in 2014. So I have the following questions:

  • Will the certificates that have a validity period extending after the expiry of the root CA certificate become invalid as soon as the latter expires, or will they continue to be valid (because they were signed during the validity period of the CA certificate)?
  • What operations are needed to renew the root CA certificate and ensure a smooth transition over its expiry?
    • Can I somehow re-sign the current root CA certificate with a different validity period, and upload the newly-signed cert to clients so that client certificates remain valid?
    • Or do I need to replace all client certificates with new ones signed by a new root CA certificate?
  • When should the root CA certificate be renewed? Close to expiry, or a reasonable time before expiry?
  • If the renewal of the root CA certificate becomes a major piece of work, what can I do better now to ensure a smoother transition at the next renewal (short of setting the validity period to 100 years, of course)?

The situation is made slightly more complicated by the fact that my only access to some of the clients is through an OpenVPN tunnel that uses a certificate signed by the current CA certificate, so if I have to replace all client certs, I will need to copy the new files to the client, restart the tunnel, cross my fingers and hope that it comes up afterwards.

HopelessN00b
  • 54,273
Remy Blank
  • 2,055

7 Answers7

192

Keeping the same private key on your root CA allows for all certificates to continue to validate successfully against the new root; all that's required of you is to trust the new root.

The certificate signing relationship is based on a signature from the private key; keeping the same private key (and, implicitly, the same public key) while generating a new public certificate, with a new validity period and any other new attributes changed as needed, keeps the trust relationship in place. CRLs, too, can continue over from the old cert to the new, as they are, like certificates, signed by the private key.


So, let's verify!

Make a root CA:

openssl req -new -x509 -keyout root.key -out origroot.pem -days 3650 -nodes

Generate a child certificate from it:

openssl genrsa -out cert.key 1024
openssl req -new -key cert.key -out cert.csr

Sign the child cert:

openssl x509 -req -in cert.csr -CA origroot.pem -CAkey root.key -create_serial -out cert.pem
rm cert.csr

All set there, normal certificate relationship. Let's verify the trust:

# openssl verify -CAfile origroot.pem -verbose cert.pem
cert.pem: OK

Ok, so, now let's say 10 years passed. Let's generate a new public certificate from the same root private key.

openssl req -new -key root.key -out newcsr.csr
openssl x509 -req -days 3650 -in newcsr.csr -signkey root.key -out newroot.pem
rm newcsr.csr

And.. did it work?

# openssl verify -CAfile newroot.pem -verbose cert.pem
cert.pem: OK

But.. why? They're different files, right?

# sha1sum newroot.pem
62577e00309e5eacf210d0538cd79c3cdc834020  newroot.pem
# sha1sum origroot.pem
c1d65a6cdfa6fc0e0a800be5edd3ab3b603e1899  origroot.pem

Yes, but, that doesn't mean that the new public key doesn't cryptographically match the signature on the certificate. Different serial numbers, same modulus:

# openssl x509 -noout -text -in origroot.pem
        Serial Number:
            c0:67:16:c0:8a:6b:59:1d
...
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
                    3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
                    8f:7c:92:4d:8f:b3:cc:e9:56:8d:db:f7:fd:d3:57:
                    1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
                    4b:8d:23:fe:52:98:15:53:3a:91:e1:14:05:a7:7a:
                    9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
                    6b:73:5b:f1:dd:9e:70:2b:f7:6e:bd:dc:d1:39:98:
                    1f:84:2a:ca:6c:ad:99:8a:fa:05:41:68:f8:e4:10:
                    d7:a3:66:0a:45:bd:0e:cd:9d
# openssl x509 -noout -text -in newroot.pem
        Serial Number:
            9a:a4:7b:e9:2b:0e:2c:32
...
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
                    3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
                    8f:7c:92:4d:8f:b3:cc:e9:56:8d:db:f7:fd:d3:57:
                    1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
                    4b:8d:23:fe:52:98:15:53:3a:91:e1:14:05:a7:7a:
                    9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
                    6b:73:5b:f1:dd:9e:70:2b:f7:6e:bd:dc:d1:39:98:
                    1f:84:2a:ca:6c:ad:99:8a:fa:05:41:68:f8:e4:10:
                    d7:a3:66:0a:45:bd:0e:cd:9d

Let's go a little further to verify that it's working in real world certificate validation.

Fire up an Apache instance, and let's give it a go (debian file structure, adjust as needed):

# cp cert.pem /etc/ssl/certs/
# cp origroot.pem /etc/ssl/certs/
# cp newroot.pem /etc/ssl/certs/
# cp cert.key /etc/ssl/private/

We'll set these directives on a VirtualHost listening on 443 - remember, the newroot.pem root certificate didn't even exist when cert.pem was generated and signed.

SSLEngine on
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/cert.key
SSLCertificateChainFile /etc/ssl/certs/newroot.pem

Let's check out how openssl sees it:

# openssl s_client -showcerts -CAfile newroot.pem -connect localhost:443

Certificate chain
 0 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=server.lan
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
 1 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
MIICHzCCAYgCCQCapHvpKw4sMjANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJB
...
-----END CERTIFICATE-----
(this should match the actual contents of newroot.pem)
...
Verify return code: 0 (ok)

Ok, and how about a browser using MS's crypto API? Gotta trust the root, first, then it's all good, with the new root's serial number:

newroot

And, we should still be working with the old root, too. Switch Apache's config around:

SSLEngine on
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/cert.key
SSLCertificateChainFile /etc/ssl/certs/origroot.pem

Do a full restart on Apache, a reload won't switch the certs properly.

# openssl s_client -showcerts -CAfile origroot.pem -connect localhost:443

Certificate chain
 0 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=server.lan
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
 1 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
MIIC3jCCAkegAwIBAgIJAMBnFsCKa1kdMA0GCSqGSIb3DQEBBQUAMFQxCzAJBgNV
...
-----END CERTIFICATE-----
(this should match the actual contents of origroot.pem)
...
Verify return code: 0 (ok)

And, with the MS crypto API browser, Apache's presenting the old root, but the new root's still in the computer's trusted root store. It'll automatically find it and validate the cert against the trusted (new) root, despite Apache presenting a different chain (the old root). After stripping the new root from trusted roots and adding the original root cert, all is well:

oldroot


So, that's it! Keep the same private key when you renew, swap in the new trusted root, and it pretty much all just works. Good luck!

Shane Madden
  • 116,404
  • 13
  • 187
  • 256
22

I've noticed that CA extensions could be missing in the renewed certificate of the original CA key. This worked more appropriately for me (it creates a ./renewedselfsignedca.conf where v3 CA extensions are defined, and ca.key and ca.crt are assumed to be the original CA key and certificate):

openssl x509 -x509toreq -in ca.crt -signkey ca.key -out \
    renewedselfsignedca.csr

echo -e "[ v3_ca ]\nbasicConstraints= CA:TRUE\nsubjectKeyIdentifier= hash\nauthorityKeyIdentifier= keyid:always,issuer:always\n" >
renewedselfsignedca.conf

openssl x509 -req -days 1095 -in renewedselfsignedca.csr -signkey
ca.key -out renewedselfsignedca.crt
-extfile ./renewedselfsignedca.conf
-extensions v3_ca

pevik
  • 302
5

@Bianconiglio plus -set_serial worked for me. My server is intranet only so I am not worrying to much what the side effects are and I now have time to work on a "proper" solution.

I used the following configurable script. Just set the variables CACRT, CAKEY and NEWCA.

# WF 2017-06-30
# https://serverfault.com/a/501513/162693
CACRT=SnakeOilCA.crt
CAKEY=SnakeOilCA.key
NEWCA=SnakeOilCA2017
serial=`openssl x509 -in $CACRT -serial -noout | cut -f2 -d=`
echo $serial
openssl x509 -x509toreq -in $CACRT -signkey $CAKEY -out $NEWCA.csr
echo -e "[ v3_ca ]\nbasicConstraints= CA:TRUE\nsubjectKeyIdentifier= hash\nauthorityKeyIdentifier= keyid:always,issuer:always\n" > $NEWCA.conf
openssl x509 -req -days 3650 -in $NEWCA.csr -set_serial 0x$serial -signkey $CAKEY -out $NEWCA.crt -extfile ./$NEWCA.conf -extensions v3_ca
openssl x509 -in $NEWCA.crt -enddate -serial -noout
pevik
  • 302
4

Basic mode to extend the valid period of root (you need the public X.509 and asociated private key):

Generate the CSR from public X.509 and private key:

openssl x509 -x509toreq -in XXX.crt -signkey XXX.key -out XXX.csr

Re-sign the CSR with private key:

openssl x509 -in XXX.csr -out XXX.crt -signkey XXX.key -req -days 365
ggrandes
  • 255
3

When your root certificate expires, so do the certs you've signed with it. You will have to generate a new root cert and sign new certificates with it. If you don't want to repeat the process every few years the only real option is to extend the valid date on the root cert something like ten or twenty years: The root I generated for my own use I set out twenty years.

You can't "renew" a root cert. All you can do is generate a new one.

Generate a new root at least a year or two before your old one expires so you have time to change over without being against a time wall if something goes wrong. That way you can always temporarily switch back to the old certs until you get your teething problems with the new one resolved.

As far as the VPN tunnels go, I would set up a couple of testbed servers to experiment with so you understand precisely what you have to do before you do it with a client's machine.

Snowhare
  • 177
0

The answer https://serverfault.com/a/308100/971795 seems to suggest it's not necessary to renew the private key - only renew the public key certificate is enough. However, it is best practice to rotate the private key of root CA once in a while.

The procedure is to "replace" the old CA with a new one (not just the public key certificate, but the entire CA), by

  1. Create a new CA and start issuing new certificates from it
  2. Disable issuance on old CA, BUT KEEP certificate revocation/validation
  3. Wait for all the certificates issued by the old CA to expire (you can generate an audit report on the old CA)
  4. shut down the old CA

Note that step 2, 3 ensures the smooth transition from old to new CA.

For more detail, check out https://docs.aws.amazon.com/acm-pca/latest/userguide/ca-lifecycle.html#ca-succession

yilin
  • 1
0

We have had the same issue, and that was in our case because the Debian server was out to date, and the openSSL had this issue:

https://en.wikipedia.org/wiki/Year_2038_problem

The last version of OpenSSL available for Debian 6 brings this problem. All certificates created after 23.01.2018 produces a Vality: for 1901 year !

The solution is to update the OpenSSL. You can create again the config files (with the certificates) for the clients.

Tolsadus
  • 1,215
Manuel
  • 1