Generating Intermediate CA Certificate

To create a “local” intermediate certificate authority for testing purposes

Prerequisites

To make sure we are in the right directory for generating certificates:

if [ -z "${TLS_CERTS_PATH}" ]; then
  echo "TLS_CERTS_PATH environment variable should be set by the caller."
  exit 1
fi

# Just change directories into the `tls-certs` path; this is because the
# paths (e.g. for the `private_key`) in the `.cnf` files are relative to `./`
# and making them absolute is not worth the trouble of worrying about a
# specific absolute path always being available.
cd "${TLS_CERTS_PATH}"

This is actually critical because some paths in the openssl configuration files (.cnf) are relative paths starting with ./.

Generate a Private Key

PRIVATE_KEY="./intermediate-ca-key.pem"
rm -f "${PRIVATE_KEY}"  # In cases where we are regenerating
openssl genrsa \
  -out "${PRIVATE_KEY}" \
  4096
chmod 400 "${PRIVATE_KEY}"

Notice we make sure to remove any lingering private key (if a previously generated one exists) and we make sure the generated file is readonly and only for the current user (permissions 0400).

Generate a CSR for the Root CA

A Certificate Signing Request (CSR) is typically used with public certificate authorities (e.g. think DigiCert) so that a customer can request a signed public certificate without having to share their private key with the CA.

CSR="./intermediate-ca-csr.pem"
CONFIG_FILE="./intermediate-ca.cnf"
rm -f "${CSR}"  # Clean up from previous runs
openssl req \
  -config "${CONFIG_FILE}" \
  -key "${PRIVATE_KEY}" \
  -new \
  -sha256 \
  -out "${CSR}"

Create “Temporary” Files Used by the Root CA for Tracking

ROOT_CA_SERIAL_ID=1000
ROOT_CA_DATABASE="./root-ca-database.txt"
ROOT_CA_SERIAL_TXT="./root-ca-serial.txt"
touch "${ROOT_CA_DATABASE}"
echo "${ROOT_CA_SERIAL_ID}" > "${ROOT_CA_SERIAL_TXT}"

Submit the CSR to the Root CA

PUBLIC_CERTIFICATE="./intermediate-ca-cert.pem"
ROOT_CA_CONFIG_FILE="./root-ca.cnf"
rm -f "${PUBLIC_CERTIFICATE}"  # In cases where we are regenerating
openssl ca \
  -batch \
  -config "${ROOT_CA_CONFIG_FILE}" \
  -extensions v3_intermediate_ca \
  -days 3650 \
  -notext \
  -md sha256 \
  -in "${CSR}" \
  -out "${PUBLIC_CERTIFICATE}"
chmod 444 "${PUBLIC_CERTIFICATE}"

Create Full Chain with Intermediate CA and Root CA

CERTIFICATE_CHAIN="./intermediate-ca-chain.pem"
ROOT_CA_PUBLIC_CERTIFICATE="./root-ca-cert.pem"
rm -f "${CERTIFICATE_CHAIN}"  # In cases where we are regenerating
cat "${PUBLIC_CERTIFICATE}" >> "${CERTIFICATE_CHAIN}"
cat "${ROOT_CA_PUBLIC_CERTIFICATE}" >> "${CERTIFICATE_CHAIN}"
chmod 444 "${CERTIFICATE_CHAIN}"

Example

After generating, the private key will resemble

-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

and the public certificate will resemble

-----BEGIN CERTIFICATE-----
MIIF1jCCA76gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEDAOBgNVBAoMB0luaXRlY2gxJjAkBgNVBAsM
HUluaXRlY2ggQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRgwFgYDVQQDDA9Jbml0ZWNo
IFJvb3QgQ0EwHhcNMjAxMjI2MDAxNjUwWhcNMzAxMjI0MDAxNjUwWjB+MQswCQYD
VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEQMA4GA1UECgwHSW5pdGVjaDEm
MCQGA1UECwwdSW5pdGVjaCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxIDAeBgNVBAMM
F0luaXRlY2ggSW50ZXJtZWRpYXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAsNuhq9kVb2wQxP+IW4rUXGwvzHl4u6Nm9FkugS1/cyCGO8zYMp2O
UzIWkTMIUB3UKiNCNljIv7k+0Vo3v4SidbkoM6+Yj1VLSY/YlaQnyrxRZNsR6LqG
0JdCcVfs6wwmN9UM/e7p/7+69SN/uu7GUaeAyPBKKBAs7LPfKpAAAYjmyiF/476u
/Z+BuIF7rDgD4jfjSp0uLu+dQfgKekyyM8vfk9ZMLyfNV9edlCC4SnMYwZFdzBBM
clXDfBe0gXNv2s0NLzJELYyucjsH8cNWNMsT8l10sMcrvMT8GfDcfgend/Ow13hE
+t2dgZ6M9H6fUxfwqquFbjxwaXeVUt653JnoCMf+w7b4hXqFdweM+ukzN0KM6V3F
B0j4qJ8hrk0t8EKR0cR7IhSEvwdW3IPR/+YkHN9H8DrLB13SNQfL2Yg3gvvtjU4C
pHXQXLxJQGD+aKnxYpnHomxyaC7UGdKKVPAKvOAqsurXthOGW5VzDvI3HhReX/jL
/m7haBXfONx8rurXbV2G3ba5OD5PxiupO8uGw+gCWcqeLjq3pcVBjvo+ZEUH1a4/
U+x/Vs79EIII62eBsqUCpuvJiAK9sBINh8PAVGUEA5WVMD5s0wKYXbJ+6T8aXpDc
m1ZJbfMrypvxbawR78DOFhX7k8fdHx0Stq5jHvJ2dloZOIcTRxzSticCAwEAAaNm
MGQwHQYDVR0OBBYEFPX+p6HeWc9R9IPCsGbA1x8gnCOFMB8GA1UdIwQYMBaAFEX4
eP8igvBiZvHbDuMDHAv1z6h2MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/
BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQC6f8AY88TksRTfSvNpGSftxDRKesB8
k7vnk2rDuwCvjJKtlXCa2mM8dD0NbV5G+EQuov38lEkcqH/bi0GWMLUrJziReORf
0gAWQQ5J4dJ4XaFF0D/rLVmK8+70GN3muGmmZaW21pR/bEPIfZSsmmxhN1fXpWB8
QMESqJ4JPmBDJBR/Em0G4EQOp/7SgW1c6huPUFINivNIV8Od5ZXNT7qBg34+7FLh
LwLsLjNxspNzZIZ+cGGU/Z6ipFkjDyKbT/Viu/DptF0JMyrZcCew7pRsst8AkzlG
ZnUx9lE8XByKC1U9eCR1ktak0v0DhGYIwlPKygVSEB4tjLHaTQ9vWuj6ESxerZH4
jPmFhJRUK4uUFWQpRgo/rdRU0dGJPKb7N4ntx3UGdoezRh6dVMmIpAHI/lP7ZybO
tm76UGD8yqY02RSIRfpHNKb+9pp0Hzklsg/YKzCj+L+QMZ3px3+sdtwxjCg+qIrH
v+oohnHLmkMv/2YqqLqxG3TAQ6AkKOjA1NekmWCQku4s1ThhM9TP0Ev4OFrEF0Ya
OGoG+OnB13JtAtvxG4u7Onr7+TGhiBhHFnyUwuroA3N7dRtlN9uDlC+U9/pt7Or6
t82CNeDSgeSDM0iFo8LmaGbVnVjzR6K5VWlk3sYEaQLSuisy2yvpMtgOOzxRFaiv
xE4+sjFZeMziTg==
-----END CERTIFICATE-----

Running openssl x509 -noout -text -in .../intermediate-ca-cert.pem on the public CA certificate should produce attributes of the form

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4096 (0x1000)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=California, O=Initech, OU=Initech Certificate Authority, CN=Initech Root CA
        Validity
            Not Before: Dec 26 00:16:50 2020 GMT
            Not After : Dec 24 00:16:50 2030 GMT
        Subject: C=US, ST=California, O=Initech, OU=Initech Certificate Authority, CN=Initech Intermediate CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    00:b0:db:a1:ab:d9:15:6f:6c:10:c4:ff:88:5b:8a:
                    d4:5c:6c:2f:cc:79:78:bb:a3:66:f4:59:2e:81:2d:
                    7f:73:20:86:3b:cc:d8:32:9d:8e:53:32:16:91:33:
                    08:50:1d:d4:2a:23:42:36:58:c8:bf:b9:3e:d1:5a:
                    37:bf:84:a2:75:b9:28:33:af:98:8f:55:4b:49:8f:
                    d8:95:a4:27:ca:bc:51:64:db:11:e8:ba:86:d0:97:
                    42:71:57:ec:eb:0c:26:37:d5:0c:fd:ee:e9:ff:bf:
                    ba:f5:23:7f:ba:ee:c6:51:a7:80:c8:f0:4a:28:10:
                    2c:ec:b3:df:2a:90:00:01:88:e6:ca:21:7f:e3:be:
                    ae:fd:9f:81:b8:81:7b:ac:38:03:e2:37:e3:4a:9d:
                    2e:2e:ef:9d:41:f8:0a:7a:4c:b2:33:cb:df:93:d6:
                    4c:2f:27:cd:57:d7:9d:94:20:b8:4a:73:18:c1:91:
                    5d:cc:10:4c:72:55:c3:7c:17:b4:81:73:6f:da:cd:
                    0d:2f:32:44:2d:8c:ae:72:3b:07:f1:c3:56:34:cb:
                    13:f2:5d:74:b0:c7:2b:bc:c4:fc:19:f0:dc:7e:07:
                    a7:77:f3:b0:d7:78:44:fa:dd:9d:81:9e:8c:f4:7e:
                    9f:53:17:f0:aa:ab:85:6e:3c:70:69:77:95:52:de:
                    b9:dc:99:e8:08:c7:fe:c3:b6:f8:85:7a:85:77:07:
                    8c:fa:e9:33:37:42:8c:e9:5d:c5:07:48:f8:a8:9f:
                    21:ae:4d:2d:f0:42:91:d1:c4:7b:22:14:84:bf:07:
                    56:dc:83:d1:ff:e6:24:1c:df:47:f0:3a:cb:07:5d:
                    d2:35:07:cb:d9:88:37:82:fb:ed:8d:4e:02:a4:75:
                    d0:5c:bc:49:40:60:fe:68:a9:f1:62:99:c7:a2:6c:
                    72:68:2e:d4:19:d2:8a:54:f0:0a:bc:e0:2a:b2:ea:
                    d7:b6:13:86:5b:95:73:0e:f2:37:1e:14:5e:5f:f8:
                    cb:fe:6e:e1:68:15:df:38:dc:7c:ae:ea:d7:6d:5d:
                    86:dd:b6:b9:38:3e:4f:c6:2b:a9:3b:cb:86:c3:e8:
                    02:59:ca:9e:2e:3a:b7:a5:c5:41:8e:fa:3e:64:45:
                    07:d5:ae:3f:53:ec:7f:56:ce:fd:10:82:08:eb:67:
                    81:b2:a5:02:a6:eb:c9:88:02:bd:b0:12:0d:87:c3:
                    c0:54:65:04:03:95:95:30:3e:6c:d3:02:98:5d:b2:
                    7e:e9:3f:1a:5e:90:dc:9b:56:49:6d:f3:2b:ca:9b:
                    f1:6d:ac:11:ef:c0:ce:16:15:fb:93:c7:dd:1f:1d:
                    12:b6:ae:63:1e:f2:76:76:5a:19:38:87:13:47:1c:
                    d2:b6:27
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                F5:FE:A7:A1:DE:59:CF:51:F4:83:C2:B0:66:C0:D7:1F:20:9C:23:85
            X509v3 Authority Key Identifier: 
                keyid:45:F8:78:FF:22:82:F0:62:66:F1:DB:0E:E3:03:1C:0B:F5:CF:A8:76

            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Key Usage: critical
                Certificate Sign
    Signature Algorithm: sha256WithRSAEncryption
         ba:7f:c0:18:f3:c4:e4:b1:14:df:4a:f3:69:19:27:ed:c4:34:
         4a:7a:c0:7c:93:bb:e7:93:6a:c3:bb:00:af:8c:92:ad:95:70:
         9a:da:63:3c:74:3d:0d:6d:5e:46:f8:44:2e:a2:fd:fc:94:49:
         1c:a8:7f:db:8b:41:96:30:b5:2b:27:38:91:78:e4:5f:d2:00:
         16:41:0e:49:e1:d2:78:5d:a1:45:d0:3f:eb:2d:59:8a:f3:ee:
         f4:18:dd:e6:b8:69:a6:65:a5:b6:d6:94:7f:6c:43:c8:7d:94:
         ac:9a:6c:61:37:57:d7:a5:60:7c:40:c1:12:a8:9e:09:3e:60:
         43:24:14:7f:12:6d:06:e0:44:0e:a7:fe:d2:81:6d:5c:ea:1b:
         8f:50:52:0d:8a:f3:48:57:c3:9d:e5:95:cd:4f:ba:81:83:7e:
         3e:ec:52:e1:2f:02:ec:2e:33:71:b2:93:73:64:86:7e:70:61:
         94:fd:9e:a2:a4:59:23:0f:22:9b:4f:f5:62:bb:f0:e9:b4:5d:
         09:33:2a:d9:70:27:b0:ee:94:6c:b2:df:00:93:39:46:66:75:
         31:f6:51:3c:5c:1c:8a:0b:55:3d:78:24:75:92:d6:a4:d2:fd:
         03:84:66:08:c2:53:ca:ca:05:52:10:1e:2d:8c:b1:da:4d:0f:
         6f:5a:e8:fa:11:2c:5e:ad:91:f8:8c:f9:85:84:94:54:2b:8b:
         94:15:64:29:46:0a:3f:ad:d4:54:d1:d1:89:3c:a6:fb:37:89:
         ed:c7:75:06:76:87:b3:46:1e:9d:54:c9:88:a4:01:c8:fe:53:
         fb:67:26:ce:b6:6e:fa:50:60:fc:ca:a6:34:d9:14:88:45:fa:
         47:34:a6:fe:f6:9a:74:1f:39:25:b2:0f:d8:2b:30:a3:f8:bf:
         90:31:9d:e9:c7:7f:ac:76:dc:31:8c:28:3e:a8:8a:c7:bf:ea:
         28:86:71:cb:9a:43:2f:ff:66:2a:a8:ba:b1:1b:74:c0:43:a0:
         24:28:e8:c0:d4:d7:a4:99:60:90:92:ee:2c:d5:38:61:33:d4:
         cf:d0:4b:f8:38:5a:c4:17:46:1a:38:6a:06:f8:e9:c1:d7:72:
         6d:02:db:f1:1b:8b:bb:3a:7a:fb:f9:31:a1:88:18:47:16:7c:
         94:c2:ea:e8:03:73:7b:75:1b:65:37:db:83:94:2f:94:f7:fa:
         6d:ec:ea:fa:b7:cd:82:35:e0:d2:81:e4:83:33:48:85:a3:c2:
         e6:68:66:d5:9d:58:f3:47:a2:b9:55:69:64:de:c6:04:69:02:
         d2:ba:2b:32:db:2b:e9:32:d8:0e:3b:3c:51:15:a8:af:c4:4e:
         3e:b2:31:59:78:cc:e2:4e

References