Skip to content

TLS by Hand

Certificate Authority

The root CA needs a private key for its certs.

Generate CA private key
openssl genpkey -algorithm ed25519 -out ca.key

This generates the CA cert using its private key.

Generate CA cert
openssl req \
    -x509 -new \
    -key ca.key \
    -noenc \
    -sha512 \
    -days 3650 \
    -out ca.crt \
    -subj "/CN=Homelab-Test"
openssl-req command explained

This command generates a certificate signing request (CSR) and signs it with a private key to create the certificate.

-x509
Output the signed certificate (skip outputting the CSR)
-new
Generate a new certificate request
-key ca.key
Path to the private key used to sign the request
-noenc
Do not encrypt the private key (not recommended, only used for example)
-sha512
Use SHA-512 as the message digest algorithm, may as well for the minimal overhead.
-days 3650
Number of days the certificate is valid for (10 years)
-out ca.crt
Path to the output file for the certificate
-subj "/CN=Homelab-Test"
Set certificate subject directly without prompting. Only uses CN (common name) in this case.
Check CA cert
openssl x509 -text -noout -in ca.crt
Example output

The CA cert is self-signed, so the Issuer and Subject match.

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            3a:10:80:35:78:1c:0d:0d:89:a7:21:fd:22:3c:9e:94:c9:5e:4b:cc
        Signature Algorithm: ED25519
        Issuer: CN=Homelab-Test
        Validity
            Not Before: Dec 13 02:57:00 2025 GMT
            Not After : Dec 11 02:57:00 2035 GMT
        Subject: CN=Homelab-Test
        Subject Public Key Info:
            Public Key Algorithm: ED25519
                ED25519 Public-Key:
                pub:
                    02:81:24:a0:f9:6b:ee:18:13:8c:a3:96:c2:fa:fe:
                    03:34:73:85:f8:10:31:7e:01:4d:1d:dc:68:7d:46:
                    66:fa
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                F4:BE:0F:25:44:FC:F6:DA:9E:94:56:71:61:71:3E:80:39:7A:20:CE
            X509v3 Authority Key Identifier:
                F4:BE:0F:25:44:FC:F6:DA:9E:94:56:71:61:71:3E:80:39:7A:20:CE
            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: ED25519
    Signature Value:
        dd:e0:4c:1e:a4:7e:70:89:79:77:df:c2:d3:b6:2b:33:2d:c1:
        9d:f0:03:b2:e1:ec:5c:ce:05:63:19:32:cc:4b:0e:e2:11:8a:
        8b:10:40:a2:46:63:67:fa:04:2c:40:3e:3f:09:84:f6:55:c0:
        f4:24:e9:05:d4:18:74:86:08:0a

Clients

Each client also needs a private key for its certs.

Generate new client key
openssl genpkey -algorithm ed25519 -out client.key

To get a signed certificate, the clients must first generate a certificate signing request (CSR). This example request uses app.internal.lan for the CN.

Generate client CSR
openssl req -new -key client.key -out client.csr -subj "/CN=app.internal.lan"

Transfer the CSR file to the system with the CA private key. This does not have to be done securely because the CSR is considered public. Then sign it using the CA private key and CA cert to produce the cert for the client.

Sign the client CSR
openssl x509 -req \
    -in client.csr \
    -CA ca.crt \
    -CAkey ca.key \
    -CAcreateserial \
    -sha512 \
    -days 3650 \
    -out client.crt
openssl-x509 command explained
-req
By default a certificate is expected on input. With this option a PKCS#10 certificate request is expected instead, which must be correctly self-signed.
-in client.csr
Path to the client CSR to be signed.
-CA ca.crt
Path to the CA certificate to use for signing.
-CAkey
Sets the CA private key to sign a certificate with. The private key must match the public key of the certificate given with -CA.
-CAcreateserial
With this option and the -CA option the CA serial number file is created if it does not exist.
-sha512
Use SHA-512 as the message digest algorithm, may as well for the minimal overhead.
-days 3650
Number of days the certificate is valid for (10 years)
-out client.crt
Path to the output file of the certificate
Check client cert
openssl x509 -text -noout -in client.crt
Example client cert content
client.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            1b:5e:05:be:31:6e:a8:56:cc:96:d6:45:1e:bf:27:f1:44:d8:01:9a
        Signature Algorithm: ED25519
        Issuer: CN=Homelab-Test
        Validity
            Not Before: Dec 13 02:59:19 2025 GMT
            Not After : Dec 11 02:59:19 2035 GMT
        Subject: CN=app.internal.lan
        Subject Public Key Info:
            Public Key Algorithm: ED25519
                ED25519 Public-Key:
                pub:
                    c3:62:05:9c:28:2d:f6:2d:44:da:3a:b7:8a:2c:e1:
                    ce:6e:ae:bd:e8:af:15:bd:49:dd:46:be:e0:2c:9c:
                    01:bd
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                E6:D7:94:19:9F:C2:6B:20:62:21:F9:DF:DF:86:83:17:15:19:E3:3B
            X509v3 Authority Key Identifier:
                F4:BE:0F:25:44:FC:F6:DA:9E:94:56:71:61:71:3E:80:39:7A:20:CE
    Signature Algorithm: ED25519
    Signature Value:
        ee:c8:e0:c0:4d:3b:b5:d1:75:f1:f5:b8:e2:9b:37:65:f2:7c:
        e7:95:a9:92:20:56:06:e0:d7:0e:c6:29:20:2d:86:18:58:69:
        10:16:a1:32:74:2a:9d:89:6b:89:62:a1:29:71:ac:9a:07:23:
        3b:3c:18:85:ed:de:a2:c0:21:01

In order to be installed into browsers, the client private key and (public) cert need to be encoded into a single file in the pkcs12 format.

Convert to pkcs12 file
openssl pkcs12 -export \
    -in client.crt \
    -inkey client.key \
    -certfile ca.crt \
    -out client.p12 \
    -name "Test Client Certificate" \
    -passout pass:
openssl-pkcs12 command explained
-export
Specifies that a PKCS#12 file will be created rather than parsed.
-in client.crt
Path to the signed client cert to include in the bundle.
-inkey client.key
Path to the client private key to include in the bundle.
-certfile ca.crt
Path to additional certs to be included
-out client.p12
Path to the bundled output file
-name "Test Client Certificate"
This specifies the "friendly name" for the certificates and private key. This name is typically displayed in list boxes by software importing the file.
-password pass:

Creates the bundle with an empty password (which is different than having no password).

This is only done for simplicity in the demo. Use a password in a real situation

This creates the bundle with an empty password

PKCS#12 is not a text format, so the file contents aren't human-readable with cat.

Check cert content in pkcs12 file
openssl x509 -text -noout -in client.p12
Example cert content in pkcs12 file

The data section should exactly match the one from checking the cert file itself (above).

client.p12
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            1b:5e:05:be:31:6e:a8:56:cc:96:d6:45:1e:bf:27:f1:44:d8:01:9a
        Signature Algorithm: ED25519
        Issuer: CN=Homelab-Test
        Validity
            Not Before: Dec 13 02:59:19 2025 GMT
            Not After : Dec 11 02:59:19 2035 GMT
        Subject: CN=app.internal.lan
        Subject Public Key Info:
            Public Key Algorithm: ED25519
                ED25519 Public-Key:
                pub:
                    c3:62:05:9c:28:2d:f6:2d:44:da:3a:b7:8a:2c:e1:
                    ce:6e:ae:bd:e8:af:15:bd:49:dd:46:be:e0:2c:9c:
                    01:bd
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                E6:D7:94:19:9F:C2:6B:20:62:21:F9:DF:DF:86:83:17:15:19:E3:3B
            X509v3 Authority Key Identifier:
                F4:BE:0F:25:44:FC:F6:DA:9E:94:56:71:61:71:3E:80:39:7A:20:CE
    Signature Algorithm: ED25519
    Signature Value:
        ee:c8:e0:c0:4d:3b:b5:d1:75:f1:f5:b8:e2:9b:37:65:f2:7c:
        e7:95:a9:92:20:56:06:e0:d7:0e:c6:29:20:2d:86:18:58:69:
        10:16:a1:32:74:2a:9d:89:6b:89:62:a1:29:71:ac:9a:07:23:
        3b:3c:18:85:ed:de:a2:c0:21:01
No Trusted Uses.
No Rejected Uses.
Alias: Test Client Certificate
Key Id: C0:4A:11:83:9A:6F:9F:E7:FC:09:42:F6:DC:AE:02:32:B5:38:80:C8
Check all encoded content
openssl pkcs12 -in client.p12 -info -passout pass:
Example encoded pkcs12 file content
Encoded client.p12 content
MAC: sha256, Iteration 2048
MAC length: 32, salt length: 8
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Certificate bag
Bag Attributes
    localKeyID: C0 4A 11 83 9A 6F 9F E7 FC 09 42 F6 DC AE 02 32 B5 38 80 C8
    friendlyName: Test Client Certificate
subject=CN=app.internal.lan
issuer=CN=Homelab-Test
-----BEGIN CERTIFICATE-----
MIIBNTCB6KADAgECAhQbXgW+MW6oVsyW1kUevyfxRNgBmjAFBgMrZXAwFzEVMBMG
A1UEAwwMSG9tZWxhYi1UZXN0MB4XDTI1MTIxMzAyNTkxOVoXDTM1MTIxMTAyNTkx
OVowGzEZMBcGA1UEAwwQYXBwLmludGVybmFsLmxhbjAqMAUGAytlcAMhAMNiBZwo
LfYtRNo6t4os4c5urr3orxW9Sd1GvuAsnAG9o0IwQDAdBgNVHQ4EFgQU5teUGZ/C
ayBiIfnf34aDFxUZ4zswHwYDVR0jBBgwFoAU9L4PJUT89tqelFZxYXE+gDl6IM4w
BQYDK2VwA0EA7sjgwE07tdF18fW44ps3ZfJ855WpkiBWBuDXDsYpIC2GGFhpEBah
MnQqnYlriWKhKXGsmgcjOzwYhe3eosAhAQ==
-----END CERTIFICATE-----
Certificate bag
Bag Attributes: <No Attributes>
subject=CN=Homelab-Test
issuer=CN=Homelab-Test
-----BEGIN CERTIFICATE-----
MIIBQjCB9aADAgECAhQ6EIA1eBwNDYmnIf0iPJ6UyV5LzDAFBgMrZXAwFzEVMBMG
A1UEAwwMSG9tZWxhYi1UZXN0MB4XDTI1MTIxMzAyNTcwMFoXDTM1MTIxMTAyNTcw
MFowFzEVMBMGA1UEAwwMSG9tZWxhYi1UZXN0MCowBQYDK2VwAyEAAoEkoPlr7hgT
jKOWwvr+AzRzhfgQMX4BTR3caH1GZvqjUzBRMB0GA1UdDgQWBBT0vg8lRPz22p6U
VnFhcT6AOXogzjAfBgNVHSMEGDAWgBT0vg8lRPz22p6UVnFhcT6AOXogzjAPBgNV
HRMBAf8EBTADAQH/MAUGAytlcANBAN3gTB6kfnCJeXffwtO2KzMtwZ3wA7Lh7FzO
BWMZMsxLDuIRiosQQKJGY2f6BCxAPj8JhPZVwPQk6QXUGHSGCAo=
-----END CERTIFICATE-----
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Bag Attributes
    localKeyID: C0 4A 11 83 9A 6F 9F E7 FC 09 42 F6 DC AE 02 32 B5 38 80 C8
    friendlyName: Test Client Certificate
Key Attributes: <No Attributes>
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGjMF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBDUJfsK0aRbxXvsJbbH
jQIaAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQXLdguXFiSd1hx7k7
9KsrKARARkp3r5YAq4kDjxBd63PtrOagY8+9yAoWNsjVl2Lh78Ps3EOLgjSYOEgQ
0uLlSongFRtg1SKiYa2fBOK9G6AlXA==
-----END ENCRYPTED PRIVATE KEY-----
Check all unencoded content
openssl pkcs12 -in client.p12 -info -noenc

These keys are not real and only shown for demo purposes. Never reveal a private key like this in practice, and store it securely.

Example unencoded pkcs12 file content
pkcs12 bundle contents with exposed private key
MAC: sha256, Iteration 2048
MAC length: 32, salt length: 8
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Certificate bag
Bag Attributes
    localKeyID: C0 4A 11 83 9A 6F 9F E7 FC 09 42 F6 DC AE 02 32 B5 38 80 C8
    friendlyName: Test Client Certificate
subject=CN=app.internal.lan
issuer=CN=Homelab-Test
-----BEGIN CERTIFICATE-----
MIIBNTCB6KADAgECAhQbXgW+MW6oVsyW1kUevyfxRNgBmjAFBgMrZXAwFzEVMBMG
A1UEAwwMSG9tZWxhYi1UZXN0MB4XDTI1MTIxMzAyNTkxOVoXDTM1MTIxMTAyNTkx
OVowGzEZMBcGA1UEAwwQYXBwLmludGVybmFsLmxhbjAqMAUGAytlcAMhAMNiBZwo
LfYtRNo6t4os4c5urr3orxW9Sd1GvuAsnAG9o0IwQDAdBgNVHQ4EFgQU5teUGZ/C
ayBiIfnf34aDFxUZ4zswHwYDVR0jBBgwFoAU9L4PJUT89tqelFZxYXE+gDl6IM4w
BQYDK2VwA0EA7sjgwE07tdF18fW44ps3ZfJ855WpkiBWBuDXDsYpIC2GGFhpEBah
MnQqnYlriWKhKXGsmgcjOzwYhe3eosAhAQ==
-----END CERTIFICATE-----
Certificate bag
Bag Attributes: <No Attributes>
subject=CN=Homelab-Test
issuer=CN=Homelab-Test
-----BEGIN CERTIFICATE-----
MIIBQjCB9aADAgECAhQ6EIA1eBwNDYmnIf0iPJ6UyV5LzDAFBgMrZXAwFzEVMBMG
A1UEAwwMSG9tZWxhYi1UZXN0MB4XDTI1MTIxMzAyNTcwMFoXDTM1MTIxMTAyNTcw
MFowFzEVMBMGA1UEAwwMSG9tZWxhYi1UZXN0MCowBQYDK2VwAyEAAoEkoPlr7hgT
jKOWwvr+AzRzhfgQMX4BTR3caH1GZvqjUzBRMB0GA1UdDgQWBBT0vg8lRPz22p6U
VnFhcT6AOXogzjAfBgNVHSMEGDAWgBT0vg8lRPz22p6UVnFhcT6AOXogzjAPBgNV
HRMBAf8EBTADAQH/MAUGAytlcANBAN3gTB6kfnCJeXffwtO2KzMtwZ3wA7Lh7FzO
BWMZMsxLDuIRiosQQKJGY2f6BCxAPj8JhPZVwPQk6QXUGHSGCAo=
-----END CERTIFICATE-----
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Bag Attributes
    localKeyID: C0 4A 11 83 9A 6F 9F E7 FC 09 42 F6 DC AE 02 32 B5 38 80 C8
    friendlyName: Test Client Certificate
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIP2gM3spbjAAWUumu0GkDghgT/bXY+tlarZjvP8HK9kg
-----END PRIVATE KEY-----