Certificates and pfSense, how to sign cert requests

pfSense-logopfSense is a neat firewall based on the FreeBSD operating system. The firewall will by default act as a CA (Certificate Authority) issuing certificates to firewall users and services. Certificates typically used by “internal” VPN clients and for running a secure https management session to the firewall. But you may have external boxes that needs certificates too. The most common way to issue and install a certificate on a device, are to generate a certificate signing request on the device that needs a certificate, “paste” the certificate request to a CA, save the issued certificate as a file and install it on the device.

The pfSense firewall have however no menu option for issuing certificates. I have searched pfSense discussion forums, but can´t find any useful information about issuing certificates to external systems.

To sign a certificate, we need a CA certificate, it´s private key and a tool to sign the certificate request.

(If you have not created a CA certificate on your pfSense firewall yet, you need to do this now selecting System > Cert manager > Ca´s, click the + button and select method: Create an internal Certificate Authority.)

Both the pfSense´s CA certificate and the CA private key can be downloaded using the pfSense WEB management interface. You can download it from System > Cert Manager > CAs. One down-arrow button will download the CA Certificate and another button will download the private key. It´s saved to PEM formatted base64 encoded files. Now we need a tool/program for signing new certificates.

One nice and common tool for handling certificates, is the OpenSSL command line tool. The OpenSSL library is in fact used by pfSense for it´s certificate operations. We can use the openssl command line tool on the pfSense firewall or optionally download and install the tool on a PC.

The OpenSSL syntax for issuing a certificate from a CSR (certificate signing request), is:

openssl ca -keyfile <ca-private-key-file> -cert <ca-certificate-file> -extensions usr_cert
-notext -md sha256 -in <cert-signing-request-file> -out <certificate-file>

When we are using openssl on the pfSense firewall, we need to use the command line interface. Let´s star by running a ssh session to the firewall and select option 8) shell.

We need to check if openssl is present and find the openssl executable and it´s configuration file:

[2.2.4-RELEASE][x@fw.lan.x]/root: find / -name openssl –ls
1625226 1056 -r-xr-xr-x 1 root   wheel   512864 Jul 26 03:17 /usr/bin/openssl
1742987    4 drwxr-xr-x 2 root   wheel      512 Aug  7 09:25 /usr/local/openssl
[2.2.4-RELEASE][x@fw.lan.x]/root: find / -name openssl.cnf –ls
1884422   20 -rw-r--r-- 1 root   wheel     9925 Jul 26 02:59 /etc/ssl/openssl.cnf
1742997    0 lrwxr-xr-x 1 root   wheel       28 Jul 26 02:59 /usr/local/openssl/
openssl.cnf -> ../../../etc/ssl/openssl.cnf

The executable is located in /usr/bin and config file is located in /etc/ssl.

Let´s check the openssl configuration file and look at the default directories:

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: more openssl.cnf
:
[ CA_default ]
dir             = ./demoCA              # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
crl_dir         = $dir/crl              # Where the issued crl are kept
database        = $dir/index.txt        # database index file.
#unique_subject = no                    # Set to 'no' to allow creation of
#several ctificates with same subject.
new_certs_dir   = $dir/newcerts         # default place for new certs.
certificate     = $dir/cacert.pem       # The CA certificate
serial          = $dir/serial           # The current serial number
#crlnumber      = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/cakey.pem# The private key
RANDFILE        = $dir/private/.rand    # private random number file
:

This config file describes the default installation of openssl with demoCA as “root” catalogue for the openssl files. Let´s not mess to much with the default installation, and make a new openssl root catalogue we can call myca. We can comment out the line dir = ./demoCA and adding a new line dir=./myCA

#dir            = ./demoCA              # Where everything is kept
dir             = ./myca              # Where everything is kept

Then create the necessary openssl directories:

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mkdir myca
[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mkdir myca/newcerts
[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mkdir myca/private

We need to create an empty index.txt file and a new serial file:

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: touch myca/index.txt
[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: echo '1001' >myca/serial

Now we need to download the pfSense firewall CA certificate and CA private key to a local drive. Select System > Cert Manager > CAs and click the down arrow buttons and save it to the local drive. Then we need to copy the files back to the firewall to store them in our new myca folder structure. Select Diagnostics > Command prompt > Upload and select the local CA files. These files are uploaded to the firewalls /tmp directory. After they are uploaded to the firewalls tmp directory, we need to move them to the correct openssl myca directories.

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mv /tmp/myCA.crt myca
[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mv /tmp/myCA.key myca/private

If we look in the openssl.cnf file, the CA cert is expected to be named cacert.pem and the private key named cakey.pem.

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mv myca/myCA.crt myca/cacert.pem
[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mv myca/private/myCA.key myca/private/cakey.pem

Just to make extra sure the private key is kept safe:

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: chmod 0700 myca/private/cakey.pem

Now we are ready to use openssl. Let´s start to look at the structure of our CA certificate file (I have x´ed and zeroed out information in the output):

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: openssl x509 -in myca/cacert.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 0 (0x0)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=XX, ST=Xxx, L=Xxxx, O=Xxx/emailAddress=x@x.x, CN=ca.x.x
        Validity
            Not Before: Jun 12 16:08:42 2014 GMT
            Not After : Jun  9 16:08:42 2024 GMT
        Subject: C=XX, ST=Xxx, L=Xxx, O=Xxx/emailAddress=x@x.x, CN=ca.x.x
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
            X509v3 Authority Key Identifier:
                keyid:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
                DirName:/C=XX/ST=Xxx/L=Xxx/O=Xxx/emailAddress=x@x.x/CN=ca.x.x
                serial:00
            X509v3 Basic Constraints:
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:
                    00:00:00:00:00:00:00:00:00:00:00:00:00:00:00

Let´s try to issue a certificate / sign a certificate request (CSR).

I have generated a cert signing request (.CSR) at a Cisco switch, saved it as certreq.csr on my local computer, uploaded the file to the tmp directory on the firewall and moved it to the myca directory.

The CSR file have the format below  (It´s not the true cert request displayed):

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: mv /tmp/certreq.csr .
[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: more certreq.csr
-----BEGIN CERTIFICATE REQUEST-----
MIIBsDCsdfjsadfkqwrjh23y8rfasdjh238ryhwfsdvjkcuwFGAGUISDUIWAefr823riHwERYHWUIAETHAHEG823TU
HWEGAHIF923t4uwegoisdv09342qthciovjergsadfUJKDSKJSFMAFJHUTIEGKNDSKNUTJKEDPKFIJEDYUGEYIY89Y
FDTSDFKj89YSFOISDHFSDoGUHGYw==u597y6FtGTuFD2ShtJEU18rI+CYw==
-----END CERTIFICATE REQUEST-----

This CSR is problematic because the request have no line shifts. OpenSSL expects a maximum line length of 64 characters. Vi need to format the request using a command like fold or just edit the file in vi and insert line shifts manually. I inserted line shifts after 40 characters to make it more visible in this tutorial.

-----BEGIN CERTIFICATE REQUEST-----
MIIBsDCsdfjsadfkqwrjh23y8rfasdjh238ryhwf
sdvjkcuwFGAGUISDUIWAefr823riHwERYHWUIAET
HAHEG823TUHWEGAHIF923t4uwegoisdv09342qth
ciovjergsadfUJKDSKJSFMAFJHUTIEGKNDSKNUTJ
KEDPKFIJEDYUGEYIY89YFDTSDFKj89YSFOISDHFS
DoGUHGYw==u597y6FtGTuFD2ShtJEU18rI+CYw==
-----END CERTIFICATE REQUEST-----

Let´s issue the certificate, i.e. sign the certificate signing request/CSR.

[2.2.4-RELEASE][x@fw.lan.x]/etc/ssl: openssl ca -extensions usr_cert -notext -md sha256 -in
 certreq.csr -out newcert.pem
Using configuration from /etc/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 4097 (0x1001)
        Validity
            Not Before: Nov  1 08:40:41 2015 GMT
            Not After : Oct 31 08:40:41 2016 GMT
        Subject:
            countryName               = NO
            stateOrProvinceName       = Xx
            organizationName          = Xx
            organizationalUnitName    = Xx
            commonName                = sw1.lan.x.x
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage:
                Digital Signature, Non Repudiation, Key Encipherment
            Netscape Comment:
                OpenSSL Generated User Certificate
            X509v3 Subject Key Identifier:
                AA:74:CE:32:31:9E:01:12:CE:D0:51:FC:CE:46:40:6A:A1:75:45:0D
            X509v3 Authority Key Identifier:
                keyid:FF:CE:28:43:01:85:EF:27:CE:73:99:18:04:99:3E:AE:7C:D4:B7:4A
                DirName:/C=XX/ST=Xx/L=Xx/O=Xx/emailAddress=x@x.x/CN=ca.x.x
                serial:00

            X509v3 Extended Key Usage:
                TLS Web Client Authentication
Certificate is to be certified until Oct 31 08:40:41 2016 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

That´s it. We have managed to issue a new certificate and stored it in the file newcert.pem. This certificate can be downloaded from the firewall and installed on the Cisco switch.

If you are getting errors issuing a certificate, it´s usually that directory and/or filenames don´t match with the configuration in the openssl.cnf file.Or the CSR file have lines longer than 64 characters. Or you are violating the policy match in the openssl.cnf file. Like requesting a certificate for country=NO and the CA have Country=US and the configuration have match for countryName.

# For the CA policy
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

 

You can check the content of any CSR, with the command:
openssl req -text -noout -verify -in <csr file>

This command is nice to use if you need to issue a public certificate from a trusted root CA provider. Check the request before you commit it and paying for the certificate.

And you can check a base64 encoded certificate file with the command:
openssl x509 -in <cert-file> -text -noout

PS. Since this “tutorial” describes unsupported pfSense use, openssl.cnf and demoCA folder may be overwritten when the firewall software is updated. The only change we made however, is basically changing the pointer to the openssl base directory. It´s easy to change it back if the file is overwritten. But if you are doing more alterations in the configuration file, make a backup copy. The directory myca, or what you may call it, should not be deleted during the firewall updates.

If you choose to install openssl locally on your computer, it’s the same procedure and configuration. Even on the Microsoft Windows platform, the configuration file using the slash / as a directory delimiter.

And remember, your CA is not a trusted CA on your devices. To trust certificates issued by your CA, your CA certificate needs to be imported on your devices as a trusted root CA.