β˜€οΈ πŸŒ™

Complete Guide: Generating SSL Certificates and PFX Files with OpenSSL

Working with secure APIs and application servers often requires SSL certificates in various formats. In this comprehensive guide, I'll walk you through the complete process of generating a private key, creating a Certificate Signing Request (CSR), generating a self-signed certificate (CRT), and finally bundling everything into a PFX file using OpenSSL.

This process is commonly required when working with secure APIs, application servers, or Windows-based systems that require .pfx files. Let's dive into each step with practical examples. permalink: "posts/certificate-process/index.html"

Before we begin, ensure you have OpenSSL installed:

Step 1: Generate a Private Key

First, we'll generate an RSA private key that will be used for certificate generation. We'll use AES-256-CBC encryption to protect the private key with a password.

openssl genpkey -algorithm RSA -aes-256-cbc -out privatekey.key -pkeyopt rsa_keygen_bits:2048

When you run this command, you'll see output similar to this:

akar@192 certificate_training % openssl genpkey -algorithm RSA -aes-256-cbc -out privatekey.key -pkeyopt rsa_keygen_bits:2048
......+......+....+..+....++++++++++++++++++++++++++++++++
+++++++*.............+...+...+...++
+++++++++++++++++++++++++++++++++++++++*
.....+............+.........+......+.+..+
...+...+....+....
+++++++++++++++++++++++++++++++++++++++*
....+..++++++
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

The system will prompt you to enter a password twice to protect your private key. Let's verify what we've created:

ls -l
akar@192 certificate_training % ls -l
total 24
-rw-------  1 akar  staff  1886 Aug 23 15:25 privatekey.key

Great! Now let's examine the contents of our private key:

head -n 5 privatekey.key
akar@192 certificate_training % head -n 5 privatekey.key
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQbtOC63hqb9wIddZ5
TWRWJQICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEJTAQxSy1FjP77uE
RxBkBUIEggTQONWHnx2/6d7axrNNX08aCWt+jUau+RI4xi7XeNZ0Rezb4R/Ctm08
cINkbaYWATdfuqfM3xLZ412U5xl+l56yZUHPURzkpl18uHtKxtK0epWnFGV7mCJU

Notice the "ENCRYPTED" in the first line? This confirms that our private key is password-protected as intended.

Inspecting the Private Key Details

To examine the detailed structure of our private key, we can use the following command (you'll need to enter your password again):

openssl rsa -in privatekey.key -noout -text

This will display extensive details about your private key, including the modulus, private exponent, and other RSA components:

Private-Key: (2048 bit, 2 primes)
modulus:
    00:92:32:46:ca:ba:d7:cf:56:37:16:bc:33:58:d2:
    81:66:3b:6b:38:10:47:68:4b:66:2a:8a:97:d6:7a:
    72:cd:9b:7c:2b:5e:73:37:37:e6:0c:88:dc:64:4a:
    32:6d:fd:97:bc:b7:ec:1d:d3:e0:e1:73:b6:9b:33:
    59:7c:3a:b0:ed:eb:26:e3:43:0d:5c:e1:a0:8a:fd:
    a0:66:b5:72:85:9f:ca:7d:d9:1f:ff:6b:cf:00:46:
    86:6f:85:df:5b:7b:d3:b0:82:be:e9:aa:50:28:ff:
    d2:c2:05:0a:15:d2:c8:31:cc:68:52:cd:e7:f9:de:
    30:29:9c:97:e5:86:c9:f6:1e:8d:af:c8:15:46:ec:
    7a:03:0f:9e:c8:fd:00:0f:ec:34:d1:7f:70:ef:94:
    e6:4f:03:21:de:c4:96:5b:e5:7d:42:3a:d4:e8:e2:
    60:00:aa:89:5a:fd:77:0d:f6:27:ac:0d:9f:ad:cc:
    b5:d7:bc:fb:7b:80:16:08:6b:4f:16:4c:61:a6:3a:
    2a:3d:d8:9b:2e:16:2a:78:3e:3f:a8:a3:a3:13:bd:
    c7:b2:66:31:ca:7b:f3:a9:1b:3f:8b:0d:9e:a0:a7:
    3b:b1:60:51:00:fc:5f:10:ce:7d:8e:8c:bc:90:ec:
    7b:b0:47:7b:0c:57:a0:f9:8d:30:e1:3d:7a:39:cb:
    81:a5
publicExponent: 65537 (0x10001)
privateExponent:
    04:62:95:2c:82:07:b4:9c:9f:a8:e3:2a:c0:1c:8a:
    4c:94:76:2e:17:90:e8:c0:30:37:90:f2:26:79:04:
    46:06:06:a6:af:5d:d1:d7:6f:a3:4b:1c:9f:36:79:
    86:42:8f:c1:55:9b:f3:b6:15:fd:f5:30:52:3d:eb:
    4e:76:5d:35:b9:b2:b0:28:e3:f6:f7:fe:8e:84:b6:
    81:b6:2a:11:f7:7d:c5:a2:14:5b:5f:32:b5:8d:a7:
    fd:22:dd:14:fb:a5:19:1b:86:d9:ef:e5:99:21:39:
    43:60:0e:d2:22:41:26:7b:3c:da:d4:f9:c3:0d:26:
    d1:2c:b5:02:a9:b5:17:98:7d:e6:bd:1e:c3:59:c0:
    82:e7:0d:e5:3c:7d:b6:a5:ab:2f:67:41:b9:f4:84:
    b6:8f:ef:87:2f:51:f0:cb:35:d4:ae:93:b4:8e:9b:
    0f:cb:8b:78:8c:4c:e5:cf:6c:7b:d1:95:1b:79:11:
    2c:62:f6:4b:07:77:b5:13:74:cc:ab:a4:60:c0:ec:
    66:b7:72:67:b3:7d:c1:a4:b2:db:74:d7:32:3a:2b:
    34:22:fb:1e:6a:c0:40:dd:fc:12:a9:eb:9f:a8:5c:
    45:47:eb:8d:9e:95:92:e6:73:94:e0:1e:d9:60:60:
    8f:db:44:fa:a0:63:5a:f3:c4:a6:ab:c2:20:3a:7d:
    d1
prime1:
    00:c9:12:3c:44:18:4e:cb:06:bb:c8:00:b6:de:58:
    93:0e:f6:7e:f8:71:c2:e6:ba:64:6b:23:00:8d:b1:
    37:c0:5c:c3:fa:8a:b6:8a:99:ec:f0:f6:fd:bf:a6:
    b8:eb:79:7f:ef:0c:78:97:a2:d8:39:6b:1b:be:a6:
    af:71:18:4c:cb:f1:5a:ad:50:56:f4:b9:12:91:bd:
    af:d1:c0:8c:a5:9f:70:cc:3f:24:1b:d4:8a:a4:44:
    6a:b1:e4:f5:a9:e6:71:42:ea:a0:4b:a7:d9:53:e6:
    42:b7:65:4f:41:50:23:10:9d:c9:a4:db:c7:b2:04:
    93:79:cb:2a:be:af:ae:32:75
prime2:
    00:ba:22:69:24:a1:82:73:6a:f3:12:5b:7b:3d:56:
    ed:99:7d:91:b9:bd:e4:ec:c8:ca:54:43:84:98:6d:
    0f:64:f0:9c:68:12:ca:e4:f1:b7:04:3d:df:09:24:
    10:4c:cd:84:cf:b7:26:e8:b8:79:3a:c9:de:f4:6f:
    07:51:b7:bf:2d:fd:e4:94:51:82:ad:1c:b2:ec:ad:
    58:06:8c:7a:ca:a7:db:5d:89:f5:93:28:64:26:a6:
    00:6c:bf:10:eb:2d:88:1f:b2:60:74:1a:93:70:8d:
    6b:83:0c:ca:86:29:05:da:63:7b:ef:ec:78:1a:8a:
    2b:ad:75:05:d5:6c:aa:cc:71
exponent1:
    4c:78:a7:fd:09:f5:a5:97:a9:01:56:ec:35:c2:20:
    23:f7:b1:b0:91:ab:9b:8e:d6:34:3c:fa:af:e8:ef:
    98:34:6f:c0:b0:d2:de:01:ce:b0:92:6f:4d:5d:7e:
    44:e8:4f:b4:bd:dc:7b:67:4e:ae:ff:c2:6c:8e:fe:
    78:97:d2:55:5a:34:af:67:b7:ba:99:4c:ab:ab:25:
    dc:33:4c:0a:8d:8a:1b:d4:03:ca:a5:da:18:c1:80:
    86:a9:da:98:c4:70:dd:0f:58:e9:8d:2b:c5:91:f4:
    5b:c5:a7:0f:e3:07:83:01:4f:b8:68:72:19:8d:4d:
    fd:6b:3c:95:8b:41:48:25
exponent2:
    0e:ee:f7:d4:92:88:ae:cd:e3:d6:93:34:5e:05:7d:
    66:95:26:7d:ea:78:55:6e:fe:9c:a8:16:19:4c:96:
    b1:98:84:53:ed:3e:0e:04:3e:94:be:8a:08:67:b6:
    4f:13:26:16:ac:31:18:f6:c3:f3:2d:83:0f:c5:60:
    a6:f3:c1:08:e5:97:d3:36:7e:ef:78:9f:fa:51:26:
    00:d0:80:81:85:da:19:b5:19:d8:c6:a2:d2:5e:fb:
    25:be:66:93:17:c2:16:18:ef:98:21:11:b7:34:01:
    6e:30:36:87:70:ff:21:38:5e:17:2f:b7:ab:19:6e:
    cb:a9:d9:df:f7:a9:f4:61
coefficient:
    06:b2:8d:cf:3f:5d:f9:6e:53:f6:73:d4:c6:f1:ce:
    fb:77:d0:3a:90:86:16:83:0c:3f:9f:a7:83:09:80:
    8e:93:e7:27:84:df:29:9f:8b:f6:40:a8:c5:0f:c9:
    87:65:98:97:1f:28:20:b8:10:be:65:93:1a:c5:f3:
    cb:86:a0:af:e4:b5:d4:8d:1c:a4:7d:30:7c:c6:1b:
    62:fa:8f:6c:4a:96:30:bc:c5:25:d2:ee:78:01:fa:
    bc:7e:0a:ee:81:4e:4d:d1:81:7a:e7:7c:9c:12:74:
    1c:de:7b:3d:46:e3:aa:18:c5:47:5b:ab:69:de:4f:
    f8:a1:3c:af:62:28:fb:91

Step 2: Generate a Certificate Signing Request (CSR)

Next, we'll create a Certificate Signing Request using our private key. This CSR will contain information about your organization and the certificate you're requesting.

openssl req -new -key privatekey.key -out request.csr

You'll be prompted to enter your private key password:

akar@192 certificate_training % openssl req -new -key privatekey.key -out request.csr
Enter pass phrase for privatekey.key:

During this process, you'll also be asked to provide information such as country, state, organization, etc. Let's verify our new file:

ls -l
akar@192 certificate_training % ls -l
total 32
-rw-------  1 akar  staff  1886 Aug 23 15:25 privatekey.key
-rw-------  1 akar  staff   887 Aug 23 15:26 request.csr

Perfect! We now have both our private key and CSR file.

Step 3: Create a Subject Alternative Name (SAN) Extension File

Before generating our certificate, we need to create an extension file that defines the Subject Alternative Names. This is crucial for modern SSL certificates:

touch san.ext
nano san.ext

Add the following content to the san.ext file:

subjectAltName = @alt_names
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[alt_names]
DNS.1 = localhost
DNS.2 = dev.local

Let's verify our directory contents:

ls -l
akar@192 certificate_training % ls -l
total 40
-rw-------  1 akar  staff  1886 Aug 23 15:25 privatekey.key
-rw-------  1 akar  staff   887 Aug 23 15:26 request.csr
-rw-------  1 akar  staff   123 Aug 23 15:27 san.ext

Step 4: Generate a Self-Signed Certificate

Now we'll create a self-signed certificate using our CSR, private key, and SAN extension file:

openssl x509 -req \
  -in request.csr \
  -signkey privatekey.key \
  -out certificate.crt \
  -days 365 -sha256 \
  -extfile san.ext

The system will prompt for your private key password and then generate the certificate:

akar@192 certificate_training % openssl x509 -req \
  -in request.csr \
  -signkey privatekey.key \
  -out certificate.crt \
  -days 365 -sha256 \
  -extfile san.ext
Enter pass phrase for privatekey.key:
Certificate request self-signature ok
subject=C=TR, ST=Istanbul, L=Istanbul, O=Soner Uzeyir, OU=Soner Uzeyir Tech, CN=example123.com

Let's check our directory again:

ls -l
akar@192 certificate_training % ls -l
total 40
-rw-------  1 akar  staff  1886 Aug 23 15:25 privatekey.key
-rw-------  1 akar  staff   887 Aug 23 15:26 request.csr
-rw-------  1 akar  staff   123 Aug 23 15:27 san.ext
-rw-------  1 akar  staff  1234 Aug 23 15:28 certificate.crt

Excellent! We now have our certificate file.

Step 5: Verify Key Pair Consistency

Before proceeding, let's verify that our private key, CSR, and certificate all belong to the same key pair. This is a crucial security check:

echo "Private Key:   $(openssl rsa -in privatekey.key -noout -modulus | openssl sha256)" && \
echo "CSR:           $(openssl req -in request.csr -noout -modulus | openssl sha256)" && \
echo "Certificate:   $(openssl x509 -in certificate.crt -noout -modulus | openssl sha256)"
akar@192 certificate_training % echo "Private Key:   $(openssl rsa -in privatekey.key -noout -modulus | openssl sha256)" && \
echo "CSR:           $(openssl req -in request.csr -noout -modulus | openssl sha256)" && \
echo "Certificate:   $(openssl x509 -in certificate.crt -noout -modulus | openssl sha256)"
Enter pass phrase for privatekey.key:
Private Key:   SHA2-256(stdin)= 7c986b02a629df5c05b22826b5230ad0bced69d33a7ecadc16857f8fcded878f
CSR:           SHA2-256(stdin)= 7c986b02a629df5c05b22826b5230ad0bced69d33a7ecadc16857f8fcded878f
Certificate:   SHA2-256(stdin)= 7c986b02a629df5c05b22826b5230ad0bced69d33a7ecadc16857f8fcded878f

What this verification means:

Why the modulus matters:

In RSA cryptography, the public key consists of (n, e) where n is the modulus. Your CSR and certificate contain the public key, while your private key also contains the same modulus. Matching moduli confirm they belong together.

Why we use SHA-256:

The openssl ... | openssl sha256 technique is used for convenience. The modulus is a large integer (hundreds of hex digits), so hashing it makes visual comparison much easier. We rely on the fact that identical inputs produce identical hashes.

Step 6: Create a PFX Bundle

Finally, we need to combine our certificate and private key into a PFX (PKCS#12) file. This format is essential for:

openssl pkcs12 -export \
  -in certificate.crt \
  -inkey privatekey.key \
  -out certificate.pfx \
  -passin pass:123456 \
  -passout pass:102030

Let's verify our final directory structure:

ls -l
akar@192 certificate_training % ls -l
total 48
-rw-r--r--  1 akar  staff  1432 Aug 23 16:08 certificate.crt
-rw-------  1 akar  staff  2771 Aug 23 16:15 certificate.pfx
-rw-------  1 akar  staff  1886 Aug 23 15:25 privatekey.key
-rw-r--r--  1 akar  staff  1037 Aug 23 15:35 request.csr
-rw-r--r--  1 akar  staff   162 Aug 23 16:04 san.ext

Perfect! We now have our complete certificate bundle ready for use.

Understanding the Complete Workflow

Here's a visual representation of the entire process we've completed:

                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ 1) Generate Private Key  β”‚
                 β”‚   openssl genrsa ...     β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ 2) Create CSR            β”‚
                 β”‚   openssl req -new ...   β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                         send to CA
                              β”‚
                              β–Ό
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ 3) CA signs CSR          β”‚
                 β”‚  Returns:                β”‚
                 β”‚   β€’ certificate.crt      β”‚
                 β”‚   β€’ ca-chain.crt         β”‚
                 β”‚     (optional)           β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ 4) Build PFX bundle      β”‚
                 β”‚   openssl pkcs12 -export β”‚
                 β”‚   Inputs:                β”‚
                 β”‚    β€’ privatekey.key      β”‚
                 β”‚    β€’ certificate.crt     β”‚
                 β”‚    β€’ ca-chain.crt        β”‚
                 β”‚   Output: bundle.pfx     β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ 5) Install PFX on client β”‚
                 β”‚   (Windows / app store)  β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚ 6) Client calls API      β”‚
                 β”‚   β€’ Sends cert           β”‚
                 β”‚   β€’ Proves key possessionβ”‚
                 β”‚   β€’ Server validates via β”‚
                 β”‚     CA trust chain       β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           

Key Takeaways

  1. Private Key Security: Always password-protect your private keys and store them securely
  2. File Formats:
    • .key files are PEM-encoded private keys
    • .crt files are PEM-encoded certificates
    • .pfx files are binary PKCS#12 bundles containing both
  3. Verification: Always verify that your certificate chain components belong together
  4. Production Usage: In production environments, use certificates signed by trusted Certificate Authorities rather than self-signed certificates

Next Steps

With your PFX file ready, you can now:

This complete certificate generation process forms the foundation for secure communications in modern applications and APIs.