Building an OpenSSL Certificate Authority - Creating Your Root Certificate
Creating Your Root Certificate Authority
In our previous article, Introductions and Design Considerations for Eliptical Curves we covered the design requirements to create a two-tier ECC certificate authority based on NSA Suite B's PKI requirements. We can now begin creating our CA's root configuration. Creating the root CA requires us to generate a certificate and private key, since this is the first certificate we're creating, it will be self-signed. The root CA will not sign client and server certificates, it's job it only to create intermeidary certificates and act as the root of our chain of trust. This is standard practice across the public and private PKI configurations and so too should your lab environments.
Create Your Directory Structure
Create a directory to store your root CA pair and config files.
# sudo bash # mkdir /root/ca
Yep, I did that. This is for a test lab and permissions may not match real world requirements. I sudoed into bash and created everything under root; aka playing with fire. This affects ownership down the line if you
chmod
private key files and directories to user access only so determine for yourself what user/permission will be accessing files for certificate creation. I have a small team and trust them with root within a lab environment (snapshots allow me to be this trusting).
Create your CA database to keep track of signed certificates
# cd /root/ca # mkdir private certs crl # touch index.txt # echo 1000 > serial
We begin by creating a working root directory with sub directories for the various files we'll be creating. This will allow you to apply your preferred security practices should you choose to do so. Since this is a test lab and I am operating as root, I won't be chmod'ing anything today.
Create Your OpenSSL Config File
OpenSSL uses configuration files to simplify/template the components of a certificate. Copy the GIST openssl_root.cnf file to
/root/ca/openssl_root.cnf
which is already prepared for this demo. For the root CA certificate creation, the [ CA ]
section is required and will gather it's configuration from the [ CA_default ]
section.
[ ca ] # `man ca` default_ca = CA_default
The
[CA_default]
section in the openssl_root.cnf file contains the variables OpenSSL will use for the root CA. If you're using alternate directory names from this demo, update the file accordingly. Note the long values for default days (10 years) as we don't care about renewing the root certificate anytime soon.
[ CA_default ] # Directory and file locations. dir = /root/ca certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/certs database = $dir/index.txt serial = $dir/serial RANDFILE = $dir/private/.rand # The root key and root certificate. private_key = $dir/private/ca.cheese.key.pem certificate = $dir/certs/ca.cheese.crt.pem # For certificate revocation lists. crlnumber = $dir/crlnumber crl = $dir/crl/ca.cheese.crl.pem crl_extensions = crl_ext default_crl_days = 3650 # SHA-1 is deprecated, so use SHA-2 or SHA-3 instead. default_md = sha384 name_opt = ca_default cert_opt = ca_default default_days = 3650 preserve = no policy = policy_strict
For the root CA, we define
[policy_strict]
which will later force the intermediary's certificate to match country, state/province, and organization name fields.
[ policy_strict ] The root CA should only sign intermediate certificates that match. # See POLICY FORMAT section of `man ca`. countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional
The
[ req ]
section is used for OpenSSL certificate requests. Some of the values listed will not be used since we are manually specifying them during certificate creation.
[ req ] # Options for the `req` tool (`man req`). default_bits = 4096 distinguished_name = req_distinguished_name string_mask = utf8only # SHA-1 is deprecated, please use SHA-2 or greater instead. default_md = sha384 # Extension to add when the -x509 option is used. x509_extensions = v3_ca
I pre-populate the
[ req_distinguished_name
] section with values I'll commonly used to save typing down the road.
[ req_distinguished_name ] countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name localityName = Locality Name 0.organizationName = Organization Name organizationalUnitName = Organizational Unit Name commonName = Common Name emailAddress = Email Address # Optionally, specify some defaults. countryName_default = US stateOrProvinceName_default = WA localityName_default = Seattle 0.organizationName_default = Grilled Cheese Inc. organizationalUnitName_default = Grilled Cheese Root CA emailAddress_default = grilledcheese@yummyinmytummy.us
The
[ v3_ca ]
section will further define the Suite B PKI requirements, namely basicConstraints
and acceptable keyUsage
values for a CA certificate. This section will be used for creating the root CA's certificate.
[ v3_ca ] # Extensions for a typical CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, digitalSignature, cRLSign, keyCertSign
Selecting the Suite B compliant elliptical curve
We're creating a Suite B infrastructure so we'll need to pick an acceptable curve following P-256 or P-384. To do this, run the following OpenSSL command:
openssl ecparam -list_curves
This will give you a long list of options but which one to pick? Let's isolate the suites within the 256 & 384 prime fields; we can grep the results for easier curve identification.
openssl ecparam -list_curves | grep '256\|384'
And we get the following results (your results may vary depending on the version of OpenSSL running):
# openssl ecparam -list_curves | grep '256\|384' secp256k1 : SECG curve over a 256 bit prime field secp384r1 : NIST/SECG curve over a 384 bit prime field prime256v1: X9.62/SECG curve over a 256 bit prime field brainpoolP256r1: RFC 5639 curve over a 256 bit prime field brainpoolP256t1: RFC 5639 curve over a 256 bit prime field brainpoolP384r1: RFC 5639 curve over a 384 bit prime field brainpoolP384t1: RFC 5639 curve over a 384 bit prime field
I am going to use secp384r1 as my curve of choice. It's good to mention that RFC5480 notes secp256r1 (not listed) is referred to as prime256v1 for this output's purpose. Why not use something larger than 384? Thank Google. People absolutely were using secp521r1 then Google dropped support for it (read Chromium Bug 478225 for more). The theory is since NSA Suite B PKI did not explicitly call out anything besides 256 or 384, the Chromium team quietly decided it wasn't needed and dropped support for it. Yea... it kinda annoyed a few people. So to avoid future browser issues, we're sticking with what's defined in public standards.
Create the Root CA's Private Key
Using the names defined in the
openssl_root.cnf's
private_key value and our selected secp384r1 ECC curve we will create and encrypt the root certificates private key.
# openssl ecparam -genkey -name secp384r1 | openssl ec -aes256 -out private/ca.cheese.key.pem read EC key writing EC key Enter PEM pass phrase: ****** Verifying - Enter PEM pass phrase: ******
Note:The ecparam function within OpenSSL does not encrypt the private key like genrsa/gendsa/gendh does. Instead we combined the private key creation (openssl ecparam) with a secondary encryption command (openssl ec) to encrypt private key before it is written to disk. Keep the password safe.
Create the Root CA's Certificate
Using the new private key, we can now generate our root's self-signed certificate. We do this because the root has no authority above it to request trust authority from; it is the absolute source of authority in our certificate chain.
# openssl req -config openssl_root.cnf -new -x509 -sha384 -extensions v3_ca -key private/ca.cheese.key.pem -out certs/ca.cheese.crt.pem Enter pass phrase for private/ca.cheese.key.pem: ****** You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [US]: State or Province Name [WA]: Locality Name [Seattle]: Organization Name [Grilled Cheese Inc.]: Organizational Unit Name [Grilled Cheese Root CA]: Common Name []:Grilled Cheese Root Certificate Authority Email Address [grilledcheese@yummyinmytummy.us]:
Using OpenSSL we can validate the Certificate contents to ensure we're following the NSA Suite B requirements.
# openssl x509 -noout -text -in certs/ca.cheese.crt.pem Certificate: Data: Version: 3 (0x2) Serial Number: ff:bd:f5:2f:c5:0d:3d:02 Signature Algorithm: ecdsa-with-SHA384 Issuer: C = US, ST = WA, L = Seattle, O = Grilled Cheese Inc., OU = Grilled Cheese Root CA, CN = Grilled Cheese Inc. Root Certificate Authority, emailAddress = grilledcheese@yummyinmytummy.us Validity Not Before: Aug 22 23:53:05 2017 GMT Not After : Aug 20 23:53:05 2027 GMT Subject: C = US, ST = WA, L = Seattle, O = Grilled Cheese Inc., OU = Grilled Cheese Root CA, CN = Grilled Cheese Inc. Root Certificate Authority, emailAddress = grilledcheese@yummyinmytummy.us Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (384 bit) pub: 04:a6:b7:eb:8b:9f:fc:95:03:02:20:ea:64:7f:13: ea:b7:75:9b:cd:5e:43:ca:19:70:17:e2:0a:26:79: 0a:23:2f:20:de:02:2d:7c:8f:62:6b:74:7d:82:fe: 04:08:38:77:b7:8c:e0:e4:2b:27:0f:47:01:64:38: cb:15:a8:71:43:b2:d9:ff:ea:0e:d1:c8:f4:8f:99: d3:8e:2b:c1:90:d6:77:ab:0b:31:dd:78:d3:ce:96: b1:a0:c0:1c:b0:31:39 ASN1 OID: secp384r1 NIST CURVE: P-384 X509v3 extensions: X509v3 Subject Key Identifier: 27:C8:F7:34:2F:30:81:97:DE:2E:FC:DD:E2:1D:FD:B6:8F:5A:AF:BB X509v3 Authority Key Identifier: keyid:27:C8:F7:34:2F:30:81:97:DE:2E:FC:DD:E2:1D:FD:B6:8F:5A:AF:BB X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign Signature Algorithm: ecdsa-with-SHA384 30:65:02:30:77:a1:f9:e2:ab:3a:5a:4b:ce:8d:6a:2e:30:3f: 01:cf:8e:76:dd:f6:1f:03:d9:b3:5c:a1:3d:6d:36:04:fb:01: f7:33:27:03:85:de:24:56:17:c9:1a:e4:3b:35:c4:a8:02:31: 00:cd:0e:6c:e0:d5:26:d3:fb:88:56:fa:67:9f:e9:be:b4:8f: 94:1c:2c:b7:74:19:ce:ec:15:d2:fe:48:93:0a:5f:ff:eb:b2: d3:ae:5a:68:87:dc:c9:2c:54:8d:04:68:7f
Reviewing the above we can verify the certificate details:
- The Suite B Signature Algorithm: ecdsa-with-SHA384
- The certificate date validity when we specificed -days 3650:
- Not Before: Aug 22 23:53:05 2017 GMT
- Not After : Aug 20 23:53:05 2027 GMT
- The Public-Key bit length: (384 bit)
- The Issuer we defined in the openssl_root.cnf: C = US, ST = WA, L = Seattle, O = Grilled Cheese Inc., OU = Grilled Cheese Root CA, CN = Grilled Cheese Inc. Root Certificate Authority
- The Certificate Subject, since this is self-signed, refers back to itself: Subject: C = US, ST = WA, L = Seattle, O = Grilled Cheese Inc., OU = Grilled Cheese Root CA, CN = Grilled Cheese Inc. Root Certificate Authority
- The eliptical curve used when we created the private key: NIST CURVE: P-384
Verify the X.509 v3 extensions we defined within the
openssl_root.cnf
for a Suite B CA use:
X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign
The root certificate and private key are now compete and we have the first part of our CA complete. Step 1 complete! In our next article we will create the intermediary certificate to complete the chain of trust in our two-tier hierarchy.
- tty72_359339Nimbostratus
Very helpful series. Thanks for taking the time. Maybe it's a bug in my version of openssl (1.1.0f), maybe I've got an error somewhere in my root.conf file, maybe it's just a feature of openssl I don't know about - but while creating the root CA certificate, I must specify the -days option on the command line. Otherwise openssl completely ignores the 'default_days' configuration value, and generates a certificate that is valid for exactly one month.
- Chase_AbbottEmployee
Hi tty72. I deleted my VPC with this CA to move to a new AWS account. I'll rebuild this all and let you know what I run into with 1.1.0f. I am using this root CA config file and calling it out in my openssl req command at -config openssl_root.cnf.
- fesoliveiraNimbostratus
Hello Chase, thanks for the great series. I got the same issue highlighted by tty72, where the root CA validity was set to exactly 1 month after the issue date, instead of 10 years as is specified in the config file. I am using OpenSSL 1.1.0f on Ubuntu 18.04 as well.
- Chase_AbbottEmployee
@fesoliveira I still have some more updates to do on this and will get that fixed.
- Cap_SoulNimbostratus
Regarding the default_days entry. My root CA was also created with only a one month longevity. This link may be relevant:
- Brian_RaaenNimbostratus
The github link is not working anymore, where can we get the openssl_root.dnf example?
if you want to have something simple on Windows / MacOS with a GUI have a look at xca
- privatenet7Nimbostratus
Hi Chase,
Thanks very much for excellent and detailed lessons on how to create Root CA and Intermediate Certificates. You've covered all the revelant variables in each scenario, of which I still coudn't find after extensive searches from other websites.
The Root CA lesson was easy to follow.
However, the Intermediate Certificate lesson was a bit curly to follow, as detailed below:
1. Uncertain whether to use "[CA]" and "[CA_default]" sections OR "[int_ca]", but I used the former sections and they work:
"/root/ca/intermediate/openssl_intermediate.cnf
and modify the contents for your own naming conventions. Similar to the
root_ca.cnf
, the
[CA]
is required and will gather it's configuration from the
[CA_default]
section. Changes to the
[int_ca]
include:
[ CA_default ]
# Directory and file locations.
dir = /root/ca/intermediate
private_key = $dir/private/int.cheese.key.pem
certificate = $dir/cers/int.cheese.crt.pem
crlnumber = $dir/crlnumber
crl = $dir/crl/int.cheese.crl.pem
crl_extensions = crl_ext
policy = policy_looseWe have new certificate names for our intermediary use and define
policy_loose"
2. v3_intermediate_ca:
Does not matched with Root CA.
Output: [root@ca ca]# openssl ca -config ca.cnf -extensions v3_intermediate_ca -days 256 -md sha384 -in intermediate/csr/int.sansui.csr -out intermediate/certs/int.sansui.crt.pem
Using configuration from ca.cnf
Enter pass phrase for /root/ca/private/ca.sansui.key.pem:
Error Loading extension section v3_intermediate_ca
140437868918672:error:02001002:system library:fopen:No such file or directory:bss_file.c:175:fopen('/root/ca/index.txt.attr','rb')
140437868918672:error:2006D080:BIO routines:BIO_new_file:no such file:bss_file.c:182:
140437868918672:error:0E078072:configuration file routines:DEF_LOAD:no such file:conf_def.c:195:
140437868918672:error:0E06D06C:configuration file routines:NCONF_get_string:no value:conf_lib.c:324:group=CA_default name=email_in3. Missing Distinguished Name in "intermediate.cnf" file:
Output:
[root@ca ca]# openssl req -config intermediate/intermediate.cnf -new -newkey ec:<(openssl ecparam -name secp384r1) -keyout intermediate/private/int.sansui.key.pem -out intermediate/csr/int.sansui.csr
Generating a 384 bit EC private key
writing new private key to 'intermediate/private/int.sansui.key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
unable to find 'distinguished_name' in config
problems making Certificate Request
140128708568976:error:0E06D06C:configuration file routines:NCONF_get_string:no value:conf_lib.c:324:group=req name=distinguished_nameI got it working with some minor changes.
Can you please clarify where do I put the following certificates:
Chain
Intermediate
Can you please explain the next Step with regards to the End Entity Certifate, ie which Certificate is used to sign it or what do I need to do with it to complete the chain of trust, etc..?
Regards
Phuc Le