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:
- macOS: OpenSSL comes pre-installed
- Windows: You can download it from the official OpenSSL website or use WSL
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:
- Each line shows the SHA-256 hash of the RSA modulus extracted from each file
- If all three hashes are identical, it proves that the private key, CSR, and certificate are all tied to the same RSA key pair β
- If any hash differs, that file was generated with a different key β
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:
- Portability: Single file containing both certificate and private key
- Windows compatibility: Easy import into Windows Certificate Store
- Application support: Compatible with IIS, Tomcat, browsers, and many other applications
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
- Private Key Security: Always password-protect your private keys and store them securely
- File Formats:
.key
files are PEM-encoded private keys.crt
files are PEM-encoded certificates.pfx
files are binary PKCS#12 bundles containing both
- Verification: Always verify that your certificate chain components belong together
- 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:
- Import it into the Windows Certificate Store
- Configure it with your web server (IIS, Apache, Nginx)
- Use it for client certificate authentication with APIs
- Deploy it to your applications that require SSL/TLS certificates
This complete certificate generation process forms the foundation for secure communications in modern applications and APIs.