6 :tags: hint, openssl, x509, expiration
10 Recently, I ran some tests to see what would happen when my root CA cert expired, and what I'd need to do to update the cert.
12 Spoiler alert: Updating the CA cert was not that hard...
14 First I created a CA that expired in 2 hours using the new-ca.py code below:
16 .. code-block:: python
18 from OpenSSL import crypto
20 #Following script will create a self signed root ca cert.
21 from OpenSSL import crypto, SSL
22 from os.path import join
26 pubkey = "%s.crt" % CN #replace %s with CN
27 privkey = "%s.key" % CN # replcate %s with CN
29 pubkey = join(".", pubkey)
30 privkey = join(".", privkey)
33 k.generate_key(crypto.TYPE_RSA, 2048)
35 # create a self-signed cert
37 cert.get_subject().CN = CN
38 cert.set_serial_number(0)
39 cert.gmtime_adj_notBefore(0)
40 cert.gmtime_adj_notAfter(7200) # CA is only good for 2 hours
41 cert.set_issuer(cert.get_subject())
42 cert.set_subject(cert.get_subject())
44 xt = crypto.X509Extension(b'basicConstraints',1,b'CA:TRUE')
45 cert.add_extensions((xt,))
48 cert.sign(k, 'sha512')
49 pub=crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
50 priv=crypto.dump_privatekey(crypto.FILETYPE_PEM, k)
51 open(pubkey,"wt").write(pub.decode("utf-8"))
52 open(privkey, "wt").write(priv.decode("utf-8") )
54 This block is based on how the ancient certmaster program `created its CA`_.
56 Then I created a expired-ca-test.srl with contents "01"
60 > echo 01 > expired-ca-test.srl
62 Then I issued a cert against this CA:
66 > openssl genrsa -out pre-expired-example.key 4096
67 > openssl req -new -key pre-expired-example.key -out pre-expired-example.csr
68 > openssl x509 -req -days 365 -in pre-expired-example.csr -CA expired-ca-test.crt -CAkey expired-ca-test.key -CAserial expired-ca-test.srl -out pre-expired-example.crt
69 > openssl x509 -in pre-expired-example.crt -text
70 > openssl verify -verbose -CAfile expired-ca-test.crt pre-expired-example.crt
71 pre-expired-example.crt: OK
73 Then I `waited 2 hours`_ and went back to check the certs:
77 > openssl x509 -in expired-ca-test.crt -noout -enddate
78 notAfter=Oct 2 17:41:11 2022 GMT
80 Then I tested what would happen if I tried verifying the cert signed with the expired CA:
84 > openssl verify -verbose -CAfile expired-ca-test.crt pre-expired-example.crt
86 error 10 at 1 depth lookup: certificate has expired
87 error pre-expired-example.crt: verification failed
90 THIS FAILED. I thought previously signed keys would continue to verify against the expired CA but new certs wouldn't be created. Instead previously signed certs won't validate against the expired CA.
92 Then I tried signing a new cert with the expired CA. Certainly this fail, right ?
96 > openssl genrsa -out expired-example.key 4096
97 > openssl req -new -key expired-example.key -out expired-example.csr
98 > openssl x509 -req -days 365 -in expired-example.csr -CA expired-ca-test.crt -CAkey expired-ca-test.key -CAserial expired-ca-test.srl -out expired-example.crt
100 THIS WORKED in that it created the cert, though verification still fails:
104 > openssl verify -verbose -CAfile expired-ca-test.crt expired-example.crt
106 error 10 at 1 depth lookup: certificate has expired
107 error expired-example.crt: verification failed
110 Now lets see what happens if we update the CA cert with the update-ca.py script below.
112 This is almost the same as the new-ca.py script above, except **the original CA key is reused instead of generating a new key**. Also **the CN and serial number need to be the same as the original expired CA cert**.
114 Verification will fail if the CN or serial number values are not the same as the original CA, but unfortuanetly I didn't save the errors from when I tried using 'updated-ca-test' as the CN, or when I tried bumping up the serial number to 1.
116 .. code-block:: python
118 from OpenSSL import crypto
120 #Following script will create a self signed root ca cert.
121 from OpenSSL import crypto, SSL
122 from os.path import join
126 pubkey = "%s.crt" % CN #replace %s with CN
127 privkey = "%s.key" % CN # replcate %s with CN
129 pubkey = join(".", pubkey)
130 privkey = join(".", privkey)
132 # Instead of creating a new key, use the old CA's key
133 # nope: k = crypto.PKey()
134 # nope: k.generate_key(crypto.TYPE_RSA, 2048)
135 st_key=open('expired-ca-test.key', 'rt').read()
136 k = crypto.load_privatekey(crypto.FILETYPE_PEM, st_key)
138 # create a self-signed cert
140 cert.get_subject().CN = 'expired-ca-test' # keep the same CN as the old CA cert
141 cert.set_serial_number(0) # keep the same serial number as the old CA cert
142 cert.gmtime_adj_notBefore(0)
143 cert.gmtime_adj_notAfter(63072000) # CA is only good for 2 years
144 cert.set_issuer(cert.get_subject())
145 cert.set_subject(cert.get_subject())
147 xt = crypto.X509Extension(b'basicConstraints',1,b'CA:TRUE')
148 cert.add_extensions((xt,))
151 cert.sign(k, 'sha512')
152 pub=crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
153 priv=crypto.dump_privatekey(crypto.FILETYPE_PEM, k)
154 open(pubkey,"wt").write(pub.decode("utf-8"))
155 open(privkey, "wt").write(priv.decode("utf-8") )
157 Note that this code creates a updated-ca-test.key that's the same as expired-ca-test.key, so I could have continued using expired-ca-test.key in the cert creation below.
161 > diff expired-ca-test.key updated-ca-test.key
166 Next I created an updated-ca-test.srl file. I could have continuned using expired-ca-test.srl
170 > cp expired-ca-test.srl updated-ca-test.srl
172 Now let's see if the new CA can be used to create a new cert:
176 > openssl genrsa -out post-expired-example.key 4096
177 > openssl req -new -key post-expired-example.key -out post-expired-example.csr
178 > openssl x509 -req -days 365 -in post-expired-example.csr -CA updated-ca-test.crt -CAkey updated-ca-test.key -CAserial updated-ca-test.srl -out post-expired-example.crt
179 > openssl x509 -in post-expired-example.crt -text
180 > openssl verify -verbose -CAfile updated-ca-test.crt post-expired-example.crt
181 post-expired-example.crt: OK
183 Now verify the old cert verifies using the new CA:
187 > openssl verify -verbose -CAfile updated-ca-test.crt pre-expired-example.crt
188 pre-expired-example.crt: OK
190 THIS WORKED. The updated CA could be used to verify both new and previous created certs Hurray !!
195 An expired/expiring root CA may be a hassle, but it's not catastrophic. The biggest pain should be pushingout the updated root CA everywhere the cert is being used in your environment. If you're using an orchestration/CM tool like Salt or Ansible, updating the root CA cert shouldn't be too bad, but remember to reload or restart any services using the cert to force the updated CA cert to read.
199 - https://serverfault.com/questions/306345/certification-authority-root-certificate-expiry-and-renewal
200 - https://gist.github.com/mohanpedala/468cf9cef473a8d7610320cff730cdd1
203 .. _`created its CA`: https://github.com/jude/certmaster/blob/master/certmaster/certs.py#L92
204 .. _`waited 2 hours`: https://store.steampowered.com/app/1366540/Dyson_Sphere_Program/