First, create a directory for the PKI server to work in. This step may be optional if the router is going to be using some network-based storage for all of its elements, but I find it handy to have, and it's easy to move things around afterward. I like using removable media when keeping things on routers, so that it's easy to snag the critical data if there's a hardware failure.
Next, generate an RSA keypair. It needs to be exportable, which is the reason I'm doing it manually, rather than let the router generate it automatically at CA startup. Name it the same as the CA will be named in the crypto pki server <whatever> configuration section.
crypto key generate rsa label MY_ROOT_CA modulus 2048 exportable storage nvram:
Now export the keys. Having a copy of them squirreled away somewhere will be absolutely critical if you ever need to replace the CA. Replacing the CA will be absolutely critical if your certificate authentication includes CRL checking, because CRLs tend to have short lifetimes, need to be re-generated regularly. I tend to export the keys twice: once to the terminal (and I save the session) and once to a .pub and .priv file pair. The export command forces protecting the exported keys with a passphrase in both cases, but a saved terminal session will show the passphrase, so be careful with it.
crypto key export rsa MY_ROOT_CA pem url usbflash:/MY_ROOT_CA/ 3des <secret> crypto key export rsa MY_ROOT_CA pem terminal 3des <secret>
Now configure and start the PKI server. Line-by-line commentary follows.
1: crypto pki server MY_ROOT_CA 2: database level complete 3: database archive pkcs12 password <secret> 4: lifetime crl 336 5: lifetime certificate 1826 6: lifetime ca-certificate 7305 7: lifetime enrollment-request 240 8: cdp-url http://my.public.webserver/MY_ROOT_CA.crl 9: ! database url usbflash:/MY_ROOT_CA/ 10: ! database url tftp://tftp.fragmentationneeded.net/db 11: database username router password <secret> 12: database url scp://scp.fragmentationneeded.net//path/to/db/ 13: database username router password <secret> 14: database url crl publish scp://www.fragmentationneeded.net//var/www/html/ username router password <secret> 15: database url crl publish tftp://tftp.fragmentationneeded.net/crl/ 16: no shut 17: shut
1) Server name must match the RSA key created earlier.
2) Specify how much info we keep on issued certificates. I use complete, which produces two files in the database for every issued certificate: .cnm files are plain text, include the subject name, expiration and serial number (in the filename) for each certificate. .crt files are the actual DER-encoded certificates. The name keyword only produces the .cnm files. Use minimal (the default) to keep no records of issued certificates.
3) On initial startup, the CA service tries to export the CA certificate and keypair. You need these to make a clone CA. We've already got two copies of the RSA keys, and the certificate will show up in the running-config, but having a pkcs12 bundle is handy, so I like the export feature. This export only works if the RSA key is exportable or if you let the CA generate it's own key on initial startup. I generate the keys manually (and make them exportable) because auto-generation creates a 1024-bit key. The archive command is totally optional. It's really just a place to keep the passphrase for non-interactive startup. If you omit this line, you'll be prompted for the passphrase at initial startup. I prefer to do it this way.
4 - 7) These are pretty self-explanatory.
8) Specifies the CDP URL you'll stamp into issued certificates. Frustratingly, Cisco's CA implementation only allows you to list a single CDP URL.
9-13) The main database URL specifies where the router will keep the following types of data:
- .ser file - it records the serial number of the last issued certificate
- .p12 file - bundle containing the CA keys and certificate (assuming it got exported in #3)
- .cnm and .crt files - explained in (#2)
- .crl - signed revocation list. This file updates with each revocation and when it reaches 50% of the age specified on line 4.
A few things about the database URL directive:
- Only a single database url directive is allowed.
- The paths are always interpreted as directory names, regardless of whether you specify a trailing slash. The filenames are pre-determined.
- If you specify a location which requires username/password, as in line #12, then the username/password must be specified on a separate line (#13).
- If you specify a location which does not require authentication (as in comment lines #9 and #10), then a single line will suffice.
- You can specify an alternate location for specific file types with database url <type> <url> Put the username/password directly on this line if the storage location requires it.
- The SCP directive on lines 12 and 14 includes two forward slashes. The first one is used by the copy command as a delimiter between hostname and path. The second one specifies that scp on the target machine should copy to an absolute path, rather than one relative to the user's home directory. TFTP urls (lines 10 and 15) have no such requirement because everything is relative to the root of the TFTP service.
14-15) In addition to the database url directive, it is possible to specify additional locations for the crl/cnm/crt file types to be held by using database url <type> publish. Like the alternate storage directives, these directives require you to specify any username/password on the same line as the storage location (line #14). You can publish to lots of different places if needed. The key difference between this directive and the previous one is that the publish location of the CRL is not used by the CA service. It's write only. Some file types (.ser and .crl) are both read and written, but they're only read from the main database location, not from publish locations.
16) Enable the service! The first time the CA is enabled, it will:
- Generate an RSA key (except that we've already done that).
- Generate a self-signed certificate (serial number 1).
- Archive the certificate and RSA keys according to the database archive and database [p12|pem] url directives.
- Store the self-signed certificate 1.crt according to the database level and database [crt] [publish] url directives.
- Store the self-signed certificate 1.cnm according to the database level and database [cnm] [publish] url directives.
- Sign a revocation list, and store it according to the database url [crl] [publish] directives.
- Create the .ser file to track serial numbers, store it according the the database url [ser] directive.
17) Next, I immediately disable the service. I do this because I don't want the passphrase used for the p12 file hanging around in the configuration, nor do I want the CA's public keys to be exportable.
The final steps are to destroy and re-import the CA's keys, remove the archive directive from the CA configuration, and re-enable the CA service:
! Destroy the exportable keys crypto key zeroize rsa MY_ROOT_CA ! ! Re-import the keys, this time as non-exportable crypto key import rsa MY_ROOT_CA pem storage nvram: url usbflash:/MY_ROOT_CA/ <secret> ! Reconfigure the CA server because 'database archive' is nonsense ! with non-exportable keys, then start it back up. crypto pki server MY_ROOT_CA no database archive no shut
At this point, the CA is ready to go. We have three copies of the private key:
- our terminal export
- our usbflash export
- the p12 file resulting from database archive
- nvram (referenced by startup-config, visible in running-config certificate chain section)
- the p12 file resulting from database archive
- database location(s)
Those are super-important to keep track of for the purposes of cloning the CA if it's ever lost.
Other files that will prove important:
- .cnm (also .crt) files contain the names of all the certificates you've issued. If a device is ever lost, you'll refer to these to figure out which certificate serial number needs to be revoked. You can also use these as a backup to the .ser file.
- .ser file keeps track of the last issued certificate. If the CA is ever replaced, you'll want to populate this file with a value higher that the last issued certificate. It's okay to skip numbers, but don't issue two certificates with the same serial. Edit this file with a hex editor, because it doesn't end with any sort of newline and most text editors will screw it up.
- I've never needed the .crt files, but I suppose they could be helpful in some situations.
- Lastly, there's the .crl file. This file isn't important to save (it expires quickly), but it's important to be able to produce a new one on demand. In order to do that, you need the keys, the CA certificate, and you need to know which certificates have been revoked. You don't need the CA router to generate a new CRL. In a pinch, you can do it with openssl, or with a virtual router in dynamips, so long as you have the required ingredients. Then you'd throw the fresh CRL wherever the CDP is pointing.