Creating Fullchain SSL Certificate With Bash
/ 8 min read
Updated:Table of Contents
Overview
After you follow this guide, you will became a local certificate authority that signs certs for web applications.
You will follow this steps:
- Create a rootCA key and crt.
- Create a intermediateCA (subCA) key and crt that is signed by rootCA.
- Create a application key and crt that is signed by intermediateCA.
- After all, you will have root, sub and app (fullchain) cert and key that is ready to be used in nginx or other stuf.
📚 If you want to automate this certificate creation process, you can
check the crtforge
post.
Before Starting
- Create a localCA folder in /etc/ssl
sudo mkdir -p /etc/ssl/localCA && \cd /etc/ssl/localCA- For non sudo commands, chown the directory.
sudo chown -R $USER:$USER /etc/ssl/localCACreate Root CA
- Create a folder inside
/etc/ssl/localCAnamedrootCA
mkdir -p /etc/ssl/localCA/rootCA && \cd /etc/ssl/localCA/rootCA- Create rootCA.key file
openssl genrsa -aes256 -out rootCA.key 4096- Create a cnf file named
rootCA.cnf
vim rootCA.cnf[ ca ]# `man ca`default_ca = CA_default
[ CA_default ]# Directory and file locations.dir = /etc/ssl/localCA/rootCAcerts = $dir/certscrl_dir = $dir/crlnew_certs_dir = $dir/newcertsdatabase = $dir/index.txtserial = $dir/serialRANDFILE = $dir/private/.rand
# The root key and root certificate.private_key = $dir/rootCA.keycertificate = $dir/rootCA.crt
# For certificate revocation lists.crlnumber = $dir/crlnumbercrl = $dir/crl/ca.crl.pemcrl_extensions = crl_extdefault_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.default_md = sha256
name_opt = ca_defaultcert_opt = ca_defaultdefault_days = 3650preserve = nopolicy = policy_strict
[ policy_strict ]# The root CA should only sign intermediate certificates that match.# See the POLICY FORMAT section of `man ca`.countryName = matchstateOrProvinceName = matchorganizationName = matchorganizationalUnitName = optionalcommonName = suppliedemailAddress = optional
[ policy_loose ]# Allow the intermediate CA to sign a more diverse range of certificates.# See the POLICY FORMAT section of the `ca` man page.countryName = optionalstateOrProvinceName = optionallocalityName = optionalorganizationName = optionalorganizationalUnitName = optionalcommonName = suppliedemailAddress = optional
[ req ]# Options for the `req` tool (`man req`).default_bits = 2048distinguished_name = req_distinguished_namestring_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.default_md = sha256
# Extension to add when the -x509 option is used.x509_extensions = v3_ca
[ req_distinguished_name ]# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.countryName = Country Name 2 LetterstateOrProvinceName = State or Province NamelocalityName = Locality Name0.organizationName = Organization NameorganizationalUnitName = Organizational Unit NamecommonName = Common NameemailAddress = Email Address
# Optionally, specify some defaults.countryName_default = TRstateOrProvinceName_default = IstanbullocalityName_default = Istanbul0.organizationName_default = SafderunorganizationalUnitName_default = Safderun ROOT CAcommonName_default = Safderun ROOT CA
[ v3_ca ]# Extensions for a typical CA (`man x509v3_config`).subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerbasicConstraints = critical, CA:truekeyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]# Extensions for a typical intermediate CA (`man x509v3_config`).subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerbasicConstraints = critical, CA:true, pathlen:0keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]# Extensions for client certificates (`man x509v3_config`).basicConstraints = CA:FALSEnsCertType = client, emailnsComment = "OpenSSL Generated Client Certificate"subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuerkeyUsage = critical, nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuth, emailProtection
[ server_cert ]# Extensions for server certificates (`man x509v3_config`).basicConstraints = CA:FALSEnsCertType = servernsComment = "OpenSSL Generated Server Certificate"subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuer:alwayskeyUsage = critical, digitalSignature, keyEnciphermentextendedKeyUsage = serverAuth
[ crl_ext ]# Extension for CRLs (`man x509v3_config`).authorityKeyIdentifier=keyid:always
[ ocsp ]# Extension for OCSP signing certificates (`man ocsp`).basicConstraints = CA:FALSEsubjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuerkeyUsage = critical, digitalSignatureextendedKeyUsage = critical, OCSPSigningCaution ⚠️
Change the values lines in the rootCA.cnf file above:
- countryName_default
- stateOrProvinceName_default
- localityName_default
- 0.organizationName_default
- organizationalUnitName_default
- commonName_default
- emailAddress_default
- Create the rootCA.crt file
openssl req -config rootCA.cnf \ -key rootCA.key \ -new -x509 -days 7305 -sha256 -extensions v3_ca \ -out rootCA.crt- Create a
newcertsdirectory
mkdir /etc/ssl/localCA/rootCA/newcerts- Create a
index.txtfile
touch /etc/ssl/localCA/rootCA/index.txt && \chmod 600 /etc/ssl/localCA/rootCA/index.txt- Create a
serialfile
echo "1000" > /etc/ssl/localCA/rootCA/serial && \chmod 600 /etc/ssl/localCA/rootCA/serialCreate Intermediate CA
- Create a intermediateCA folder
mkdir -p /etc/ssl/localCA/intermediateCA && \cd /etc/ssl/localCA/intermediateCA- Create intermediateCA.key file
openssl genrsa -aes256 -out intermediateCA.key 4096- Create a
intermediateCA.cnffile
vim intermediateCA.cnf[ ca ]# `man ca`default_ca = CA_default
[ CA_default ]# Directory and file locations.dir = /root/ca/intermediatecerts = $dir/certscrl_dir = $dir/crlnew_certs_dir = $dir/newcertsdatabase = $dir/index.txtserial = $dir/serialRANDFILE = $dir/private/.rand
# The root key and root certificate.private_key = $dir/private/intermediate.key.pemcertificate = $dir/certs/intermediate.cert.pem
# For certificate revocation lists.crlnumber = $dir/crlnumbercrl = $dir/crl/intermediate.crl.pemcrl_extensions = crl_extdefault_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.default_md = sha256
name_opt = ca_defaultcert_opt = ca_defaultdefault_days = 375preserve = nopolicy = policy_loose
[ policy_strict ]# The root CA should only sign intermediate certificates that match.# See the POLICY FORMAT section of `man ca`.countryName = matchstateOrProvinceName = matchorganizationName = matchorganizationalUnitName = optionalcommonName = suppliedemailAddress = optional
[ policy_loose ]# Allow the intermediate CA to sign a more diverse range of certificates.# See the POLICY FORMAT section of the `ca` man page.countryName = optionalstateOrProvinceName = optionallocalityName = optionalorganizationName = optionalorganizationalUnitName = optionalcommonName = suppliedemailAddress = optional
[ req ]# Options for the `req` tool (`man req`).default_bits = 2048distinguished_name = req_distinguished_namestring_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.default_md = sha256
# Extension to add when the -x509 option is used.x509_extensions = v3_ca
[ req_distinguished_name ]# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.countryName = Country Name 2 LetterstateOrProvinceName = State or Province NamelocalityName = Locality Name0.organizationName = Organization NameorganizationalUnitName = Organizational Unit NamecommonName = Common NameemailAddress = Email Address
# Optionally, specify some defaults.countryName_default = TRstateOrProvinceName_default = IstanbullocalityName_default = Istanbul0.organizationName_default = SafderunorganizationalUnitName_default = Safderun Intermediate CAcommonName_default = Safderun Intermediate CA
[ v3_ca ]# Extensions for a typical CA (`man x509v3_config`).subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerbasicConstraints = critical, CA:truekeyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]# Extensions for a typical intermediate CA (`man x509v3_config`).subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerbasicConstraints = critical, CA:true, pathlen:0keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]# Extensions for client certificates (`man x509v3_config`).basicConstraints = CA:FALSEnsCertType = client, emailnsComment = "OpenSSL Generated Client Certificate"subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuerkeyUsage = critical, nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuth, emailProtection
[ server_cert ]# Extensions for server certificates (`man x509v3_config`).basicConstraints = CA:FALSEnsCertType = servernsComment = "OpenSSL Generated Server Certificate"subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuer:alwayskeyUsage = critical, digitalSignature, keyEnciphermentextendedKeyUsage = serverAuth
[ crl_ext ]# Extension for CRLs (`man x509v3_config`).authorityKeyIdentifier=keyid:always
[ ocsp ]# Extension for OCSP signing certificates (`man ocsp`).basicConstraints = CA:FALSEsubjectKeyIdentifier = hashauthorityKeyIdentifier = keyid,issuerkeyUsage = critical, digitalSignatureextendedKeyUsage = critical, OCSPSigningCaution ⚠️
Change the values lines in the intermediateCA.cnf file above:
- countryName_default
- stateOrProvinceName_default
- localityName_default
- 0.organizationName_default
- organizationalUnitName_default
- commonName_default
- emailAddress_default
- Create intermediateCA.csr file
openssl req -config intermediateCA.cnf \ -new -sha256 \ -keyout intermediateCA.key \ -out intermediateCA.csr- Sign the intermediateCA.csr with rootCA.key file
openssl ca -config ../rootCA/rootCA.cnf \ -extensions v3_intermediate_ca \ -days 3650 -notext -md sha256 \ -in intermediateCA.csr \ -out intermediateCA.crtCreating a SSL cert for your web app with domain
- Create a directory named your application
export appname=exampleApp && \mkdir -p /etc/ssl/localCA/$appname && \cd /etc/ssl/localCA/$appname- Create a
exampleApp.keyfile
openssl genpkey -algorithm RSA -out "${appname}.key"- Create a
exampleApp.cnffile
vim "${appname}.cnf"[ req ]default_bits = 2048prompt = nodefault_md = sha256distinguished_name = dn
[ dn ]countryName = TRstateOrProvinceName = IstanbullocalityName = IstanbulorganizationName = SafderunorganizationalUnitName = Safderun WebappcommonName = example.com
[ v3_ext ]subjectAltName = @alt_names
[ alt_names ]DNS.1 = api.example.comDNS.2 = app.example.com[!caution]
Change the values lines in the exampleApp.cnf file above:
- countryName_default
- stateOrProvinceName_default
- localityName_default
- 0.organizationName_default
- organizationalUnitName_default
- commonName_default
- emailAddress_default
- DNS.1
- Create a
exampleApp.csrfile
openssl req -new -key "${appname}.key" \ -config "${appname}.cnf" \ -out "${appname}.csr"- Sign the
exampleApp.csrwithintermediateCA.key
openssl x509 -req -in "${appname}.csr" \ -CA ../intermediateCA/intermediateCA.crt \ -CAkey ../intermediateCA/intermediateCA.key \ -CAcreateserial \ -days 365 -extensions v3_ext \ -extfile "${appname}.cnf" \ -out "${appname}.crt"- Create Fullchain Cert
cat ./${appname}.crt >> fullchain.crt && \cat ../intermediateCA/intermediateCA.crt >> fullchain.crt && \cat ../rootCA/rootCA.crt >> fullchain.crtThe Final
You have 3 folder. rootCA, intermediateCA and application directory. In the application directory, you have 5 files.
- exampleApp.cnf: cert conf file
- exampleApp.crt: public cert file
- exampleApp.csr: cert signing request file
- exampleApp.key: private key
- fullchain.crt: Contains rootCA, intermediateCA and app cert which is ready to use.
You can use the fullchain cert and key file in a nginx server.
You can keep creating new certs for new web applications. All you need to do is repeating this step