Self-Signed OpenSSL Certificates

13 May 2022

Creating Self-Signed Certificates with OpenSSL

#OpenSSL CA Configuration

Before creating my self signed certificate, I create a folder to store everything and configuration file

Create a folder with:

mkdir ggallCA
cd ggallCA 

Then create an openssl.cnf file to configure OpenSSL.

####################################
[ ca ]
default_ca                      = CA_default     # Go to default CA section

[ CA_default ]
dir             = /mnt/d/ggallCA                 # Where everything is kept
certificate     = $dir/CA/cacert.pem             # The CA certificate
database        = $dir/CA/index.txt              # The database
new_certs_dir   = $dir/CA/certs                  # default place for new certs.
private_key     = $dir/CA/privkey.pem            # The CA private key
serial          = $dir/CA/serial.txt             # The current serial number for certificates

policy          = policy_default
x509_extensions = certificate_extensions


# defaults for CA
default_days    = 90                    # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha512                # use public key default MD
preserve        = no                    # keep passed DN ordering


[ policy_default]
countryName             = optional
stateOrProvinceName     = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ certificate_extensions ]
basicConstraints = CA:false


####################################
[ req ]
default_bits            = 2048
default_keyfile         = privkey.pem
distinguished_name      = req_dn
x509_extensions         = v3_ext
attributes              = req_attributes

# extensions to add to certificate request
[ req_dn ]
countryName                     = Country Name (2 letter code)
countryName_default             = US
countryName_min                 = 2
countryName_max                 = 2

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = Iowa

localityName                    = Locality Name (eg, city)
localityName_default            = Iowa CIty

0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = Greg Gallardo

organizationalUnitName          = Organizational Unit Name (eg, section)

commonName                      = Common Name (e.g. server FQDN or YOUR name)
commonName_max                  = 64

emailAddress                    = greg@greggallardo.com
emailAddress_max                = 64

# request attributes
[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20

[ v3_ext ]
basicConstraints = critical,CA:true
keyUsage = critical,digitalSignature, cRLSign, keyCertSign

[ v3_client ]
basicConstraints = CA:false
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth

#CA Certs

Now that a base directory and configuration file exist, the next thing we need is the CA certificates for signing our client and server certificates.

Create a new folder to store the CA certificate files.

mkdir CA
cd CA

Next we create the folders files specified in openssl.cnf

touch index.txt
echo "01" > serial.txt
mkdir certs private

Run the following command to create the CA certificate and private key

openssl req -x509 -config ../openssl.cnf -newkey rsa:2048 -days 365 -out cacert.pem -outform PEM -subj /CN=GGAwsCA/ -nodes

-days means this one lasts a year

#Certificate Creation

One your CA files exist you can use them to self-sign certificates.

#Server Certificate Creation

Make a folder for your server certificates

cd /mnt/d/ggallCA
mkdir server
cd server

Next use OpenSSL to make a signing request:

openssl genrsa -out serverkey.pem 2048
openssl req -new -key serverkey.pem   -out req.pem -outform PEM -subj /CN=test.myfakeserver.local/ -nodes -config ../openssl.cnf

Then sign the request with your CA files.

cd ../CA
openssl ca -config ../openssl.cnf  -in ../server/req.pem  -out ../server/servercert.pem -notext  

If all went well OpenSSL will ask you if you want to sign the cert (just say yes) and commit (also say yes)

Using configuration from ../openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'testserver'
Certificate is to be certified until May 13 21:51:26 2023 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

This will create new server certificate files. One in the CA/certs folder and one named serversert.pem in the server folder.

#PKCS12

The server certificates are PEM files. You might need other formats. I often work with Microsoft Windows, so I sometimes need to convert PEM files to PKCS12 files. This can be done with OpenSSL.

openssl pkcs12 -export -out servercert.p12 -in servercert.pem  -inkey serverkey.pem  -passout pass:somepassword

#Validity Period

When signing a request, you can use the -days flag to set the validity period of the request you're signing.

openssl genrsa -out serverkey_2.pem 2048
openssl req -new -key serverkey_2.pem   -out serverreq_2.pem -outform PEM -subj /CN=test.myfakeserver.local/ -nodes -config ../openssl.cnf

Then sign the request with your CA files.

cd ../CA
openssl ca -config ../openssl.cnf  -in ../server/serverreq_2.pem  -out ../server/servercert_2.pem -notext  -days 10

#Client Certificate Creation

Make a folder for your client certificates

cd /mnt/d/ggallCA
mkdir client
cd client

Make client certificate signing requests with OpenSSL:

openssl genrsa -out clientkey.pem 2048
openssl req -new -key clientkey.pem -out req.pem -outform PEM -subj /CN=www.greggallardo.com/O=client/OU=test -nodes -config ../openssl.cnf 

Then sign the client request with your CA files.

cd ../CA
openssl ca -config ../openssl.cnf -in ../client/req.pem -out ../client/clientcert.pem -notext -batch -extensions v3_client

You will get copies of the certificate files in the CA/certs folder and clients folder

#Password Protection

Adding -des3 to the command will add a password to the key.

openssl genrsa -des3 -out clientkey_pw.pem 2048
openssl req -new -key clientkey_pw.pem -out clientreq_pw.pem -outform PEM -subj /CN=www.greggallardo.com/O=client/OU=testpw -config ../openssl.cnf 

Then sign the client request with your CA files.

cd ../CA
openssl ca -config ../openssl.cnf -in ../client/clientreq_pw.pem -out ../client/clientcert_pw.pem -notext -batch -extensions v3_client

#PKCS12 Format

If you want PKCS12 files, you can convert your PEM files with:

openssl pkcs12 -export -out clientcert.p12 -in clientcert.pem  -inkey clientkey.pem  -passout pass:clientpassword

#Certificate Testing

You can look at the contents of your certificates with openssl x509

 openssl x509 -in <PEM FILE> -text

For example, dumping the client lets me verify the dates and Subject fields I specified.

$ openssl x509 -in ./client/clientcert.pem -text | head
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 2 (0x2)
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: CN = GGAwsCA
        Validity
            Not Before: Mar 13 15:15:06 2023 GMT
            Not After : Jun 11 15:15:06 2023 GMT
        Subject: O = client, OU = test, CN = www.greggallardo.com

#Testing Server Certificates

The following python script can be used to test out the server certificate

from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl

class SecureHTTPRequestHandler(SimpleHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(b'Hello, secure world!')

server_address = ('172.19.120.67', 8443)  # Replace with your own IP and Port values
httpd = HTTPServer(server_address, SecureHTTPRequestHandler)

# Set up the SSL context:
httpd.socket = ssl.wrap_socket(
    httpd.socket,
    server_side=True,
    certfile='/path/to/servercert.pem',    # Replace this with the path to your server certificate
    keyfile='/path/to/serverkey.pem',      # Replace this with the path to your private key
    ssl_version=ssl.PROTOCOL_TLS,
    ca_certs="/path/to/cacert.pem", # Replace this with the path to your CA certificate
    # cert_reqs=ssl.CERT_REQUIRED  # Enable this to test client certificates
)

print('Serving HTTPS on port', server_address[1])
httpd.serve_forever()

You can use --resolve with curl to test against the server.

 curl --cacert ./cacert.pem --resolve test.myfakeserver.local:8443:172.1ps://test.myfakeserver.local:8443

adding -v to the command will give you more information

> curl --cacert ./cacert.pem --resolve test.myfakeserver.local:8443:172.19.120.67 https://test.myfakeserver.local:8443  -v
* Added test.myfakeserver.local:8443:172.19.120.67 to DNS cache
* Hostname test.myfakeserver.local was found in DNS cache
*   Trying 172.19.120.67:8443...
* Connected to test.myfakeserver.local (172.19.120.67) port 8443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: ./cacert.pem
*  CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=test.myfakeserver.local
*  start date: Mar 13 14:49:51 2023 GMT
*  expire date: Jun 11 14:49:51 2023 GMT
*  common name: test.myfakeserver.local (matched)
*  issuer: CN=GGAwsCA
*  SSL certificate verify ok.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: test.myfakeserver.local:8443
> User-Agent: curl/8.0.1
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.10.12
< Date: Thu, 16 Mar 2023 15:46:45 GMT
< Content-type: text/html
<
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, close notify (256):

Hello world!

If the common name for the certificate were wrong, you'd get a warning like this:

 curl --cert ./client/clientcert.pem  --key ./client/clientkey.pem --cacert ./CA/cacert.pem --resolve test.myfake.local:8443:172.19.120.67 https://test.myfake.local:8443
curl: (60) SSL: certificate subject name 'test.myfakeserver.local' does not match target host name 'test.myfake.local'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

#Testing Client Certificates

To test if the client certificate is valid, enable the ssl.CERT_REQUIRED option in the python server script

Then sign the client request with your CA files.
```bash
cd ../CA
openssl ca -config ../openssl.cnf -in ../client/req.pem -out ../client/clientcert.pem -notext -batch -extensions v3_client
cert_reqs=ssl.CERT_REQUIRED  # Enable this to test client certificates
You can specify the client key and certificate with the `--key` and `--cert` options.
```bash
curl --cert ./client/clientcert.pem  --key ./client/clientkey.pem --cacert ./CA/cacert.pem --resolve test.myfakeserver.local:8443:172.19.120.67 https://test.myfakeserver.local:8443

Hello world!