A Practical Guide to Creating Self-Signed Certificates with OpenSSL
Special thanks to the following resources for providing extremely helpful references:
Preparation
Tool used: openssl
Step 1: Root Certificate
The root certificate is the ultimate source of all certificate signings. You must have a root certificate in order to sign other certificates. Simply put, this root certificate acts as a guarantor for any certificates it signs, utilizing its authority to make those downstream certificates trusted. Standard operating systems come pre-installed with built-in globally trusted root certificates. Since this post describes self-signed certificates, the underlying cryptographic principles are identical to “official” certificates. The only difference is that root certificates you generate yourself must be manually imported and trusted on target machines, as they are not natively embedded in the OS.
OpenSSL allows for different configuration files to generate certificates for various purposes and types. For generating a root certificate, please download and use this configuration file: https://jamielinux.com/docs/openssl-certificate-authority/_downloads/root-config.txt
1.1 Set Up Directories and Configuration File
1 | mkdir /root/ca |
1.2 Generate Private Key and Certificate
1 | cd /root/ca |
1.3 Root Certificate Complete
After verifying the certificate and inspecting the certificate information along with the X509v3 extension details, your root certificate is officially complete.
Step 2: Intermediate Certificate Authority (CA)
As the name implies, an intermediate CA represents the root CA to sign end-user certificates, since commercial root CAs almost never sign final certificates directly. For example, Aliyun’s SSL certificate is typically issued by the GlobalSign Organization Validation CA on behalf of the GlobalSign Root CA; here, GlobalSign Organization Validation CA is the Intermediate CA. Intermediate CA certificates accompany the root certificate to form a chain of trust that validates the final certificate’s authenticity.

Again, we need a configuration file specifically for the intermediate CA: https://jamielinux.com/docs/openssl-certificate-authority/_downloads/intermediate-config.txt
2.1 Set Up Directories and Configuration File
1 | mkdir /root/ca/intermediate |
2.2 Generate Private Key and Certificate
1 | # Generate the intermediate private key |
At this stage, you should see that /root/ca/index.txt now contains a record similar to the following, which is the signing entry from the root CA:
V 250408122707Z 1000 unknown ... /CN=Alice Ltd Intermediate CA
2.3 Verify and Concatenate Certificates
1 | # Inspect the intermediate certificate |
Once verified, the intermediate certificate is complete. We now need to concatenate them to form the certificate chain:
1 | cat intermediate/certs/intermediate.cert.pem \ |
This simple command combines the intermediate certificate and the root certificate into a single file to build the full chain of trust.
Step 3: Signing the Final Certificate (Nginx SSL Certificate Example)
3.1 Generate Private Key, CSR, Sign, and Verify
Generate the server private key (the root of all server-side certificates)
1 | cd /root/ca |
Once signed, you will see a new entry added to intermediate/index.txt. Next, verify the generated certificate:
1 | # View the certificate |
3.2 Certificate Deployment
Because this is a self-signed certificate authority, clients (PCs/devices) must trust your root certificate. Distribute the ca-chain.cert.pem file to target machines and import it. (For Windows, you might need to split this chain file into separate root and intermediate certificates with .crt extensions for the certificate manager to accept them correctly).
In the Nginx SSL block, there are two primary lines relating to the certificate and key:
1 | ssl_certificate /your_nginx_conf_dir/www.example.com.cert.pem; |
ssl_certificate is simply the certificate file generated in the previous step, while ssl_certificate_key is the server’s private key. For convenience, it is highly recommended to export a version of the private key that does not require a passphrase. Otherwise, Nginx will prompt you for the passphrase every time the service starts or reloads.
Export the private key without a passphrase
1 | cd /root/ca |
3.3 Multi-Domain Certificates
Typically, a certificate’s Common Name is the primary domain name, such as www.example.com or *.example.com. However, if you need the certificate to secure multiple completely different domains, the Common Name alone cannot solve this. If you examine Taobao’s HTTPS certificate, you’ll see they leverage the Subject Alternative Name (SAN) extension to resolve this.
To implement this, modify Step 3 as follows:
a. First, edit intermediate/intermediate-ca.cnf
Change the [ req ] block to include the following lines:
1 | [ req ] |
b. Ensure there are no 0.xxx tags under req_distinguished_name. If there are, strip the 0. prefix, and then add this line at the bottom:
1 | subjectAltName = @alt_names |
c. Add the [ v3_req ] block
1 | [ v3_req ] |
d. Append the list of domains
1 | # Add the alt_names section. Be mindful of formatting. You can add as many DNS.x entries as required. |
e. Note: The Common Name used during CSR generation must also be included in the DNS.x list.
f. When signing the certificate, the command differs slightly from step 3.1:
1 | openssl ca -config intermediate/intermediate-ca.cnf \ |
Conclusion: Again, many thanks to the authors of the two references mentioned at the beginning of this article. Their guides made my entire implementation process incredibly smooth. Lastly, when working with certificates, I highly recommend mapping out the exact logical hierarchy of keys and authorities in your head before touching the terminal. This keeps you from falling into common configuration traps.