Site Tools


custom_ssl_cert_local_cert_authority

This is an old revision of the document!


Set up a Custom SSL Cert using Local CA & Cert-Signing Request

This HOWTO takes you through all the steps needed to create:

  1. A root certificate authority (CA)
  2. An intermediate certificate authority (ICA)
  3. An SSL key, certificate request (CS)
  4. A signed certificate.


It then shows you how to distribute these, and install them in FreshTomato NVRAM. The setup was done on an r2025.3 build, but should work fine on releases r2023 and later.​

The steps involved in creating the Local Certificate Authority, Certificates and Signing Request can be run only on Linux. If your main computing device isn't running Linux, it is recommended that you create a Live, bootable Linux USB flash drive on which to perform these tasks. This media should be configured with persistent storage. The FreshTomato team may attempt to find ways to allow all these tasks to be done directly in FreshTomato.

Each step is preceded above it by an explanation of what it does. If you just want to create the configuration without reading what each step does, simply download the script at the bottom of this page, and run it.

Thanks to Tomato forum user “ikunat33” for writing the original text.

Prerequisites


  1. A FreshTomato router with approximately 2.5 kB of free NVRAM.
    (Check “Used / Total NVRAM” in the Overview menu)
  1. Any Linux distro with which you are comfortable.
    The Certificate Authority will be built in the “/root/ca” directory.


Why Not Use FreshTomato's Built-in Certificates?


Before this was written, the author fell victim to a suspected Man-in-the-Middle attack. He had a device that only connected if WPA was disabled. In order to test that device, he enabled open WiFi. However, a distraction caused him to leave the open WiFi enabled. He failed to notice what happened in time to prevent problems. The author later realized the attacker would likely not have gained access to his router if he'd used SSL certificates and set up HTTPS. This led him to use SSL.

The author also decided to switch to the WPA2 Enterprise protocol, which requires a Radius server with value pairs with EAP-TLS. As well, the author uses MySQL and Cacti for monitoring a family member's blood sugar. All these technologies can incorporate SSL certificates to improve network security and thus mitigate, or even stop MTM attacks. Even more so when using MTLS.

The author soon realized that having his own Certificate Authority (CA) would allow him to install the single root CA cert in his browser, and in doing so, the browser would recognize any site using one of the CA signed certs as trusted.

On What Technology is this Based?


  • “OpenSSL Certificate Authority” by Jamie Nguyen
    The CA section was largely copied from his tutorial, modified slightly
    to work with FreshTomato.

  • The OpenSSL Manual

  • “Setting a custom HTTPS certificate in Tomato, AdvancedTomato, or FreshTomato”
    by Graham Sutherland. (This code installs the key and certificate into NVRAM).

  • The FreshTomato Admin Access wiki page (covering HTTPS)


Notes


  • This HOWTO is based largely on ideas taken from the above-cited works.
    Reviewing those original sources is strongly recommended.

  • The “/root/ca/openssl.cnf” and “/root/ca/intermediate/openssl.cnf” files
    are included as attachments to this wiki page.

  • This author built the CA in “/root/”.
    It's wise to create a symbolic link to the intermediate certificate so that
    regular users have read access to use it.

  • NOTE that starting with r2025.4, the CN (Common Name) Label
    has been changed to the proper SAN (Subject Alternative Name). While the
    NVRAM value still remains as is, reflecting CN as it would require changes
    to defaults and an update that would cause NVRAM clearing for everyone.
    Don't be confused if you see either value or the unchanged register.


How the Certificate Authority Functions


In the context of this tutorial, and the requirements of this setup, the CA is passive and doesn't directly interact with anything.
To be authenticated, certificates don't need an active connection to the issuing CA. Your browser, however, does need to authenticate the certificate on the site you are trying to access. This is done by either:

  • Importing the certificate offered by the site you are
    accessing to your browser as a trusted certificate or;
  • Having the certificate of the CA that signed that certificate
    imported to your browser.


In this case, the Intermediate CA certificate (the certificate of our CA) will be imported to our browser. Having the Intermediate CA certificate in our browser will allow us to access any website using a certificate signed by the intermediate CA, without having to export it from the site, verify it, and import it into our browser.

In cases when a CA is signing certificates for many different users/sites, you can also set up the CA to use Certificate Revocation Lists. However, revocation lists must be manually distributed and installed to the browsers or other applications that need them.


One alternative is to use Online Certificate Status Protocol, in which certificates distributed by the CA include an OCSP server address. When an application accesses a site whose certificate has an OCSP address, the application sends a request to that address for the status of that certificate. If the certificate was revoked, the application won't access the site. This is discussed more thoroughly in Jamie Nguyen's “OpenSSL Certificate Authority”.


Before Starting


  • Ensure your router has a suitable hostname, IP address, and
    domain name. Changing any of these after the certificate is created
    will invalidate the certificate, forcing you to change it back or create a new one.

  • Ensure your router's Common Name (CN) matches your FreshTomato
    hostname (or “hostname.domainname”). Otherwise, FreshTomato will
    overwrite your certificate.

  • If you don't know what a CN is, read the “Web Admin” section on:
    https://wiki.freshtomato.org/doku.php/admin-access

  • This HOWTO assumes you are connecting via the LAN side of
    your router and there is no firewall between you and the router.


Build the CA and Watch it Grow


Begin by opening a terminal window, about two inches wide and vertical. Move it to one side of the screen. Right click its window title and choose “always on top”.

Run an interactive login shell with root privileges allowing you to run multiple commands in a row as root:


sudo -i




Make the tree command run repeatedly at regular intervals, and display in colour:


watch tree -C /root/


Construct the Root CA


Open a second terminal window, and position it so you can see both terminal windows clearly (as well as this HOWTO).

Open an interactive root login shell to provide root privileges for more than one typed command:


sudo -i




Change to the /root directory:


cd /root




Now, create a directory structure for the CA (including the 5 directory names separated by commas):


mkdir -p ca/{certs,crl,newcerts,private,csr}




Change permission on the “private” directory so only you, not your group or others have access:


chmod 700 ca/private




Create the “index.txt” file that OpenSSL will use as the Certificate Database:


touch ca/index.txt




Run the echo command to output the text “1000”, use the “>” operator to redirect the output into file: “/ca/serial”:


echo 1000 > ca/serial




Change to the “/root/ca” directory:


cd /root/ca




Run the nano text editor and make it open the “openssl.cnf” file:


nano /root/ca/openssl.cnf



Create the Root Authority Private Key


Generate the root Authority 4096-bit RSA Private Key, encrypt it with a passphrase using AES256 encryption and output it to file: “/private/ca.key.pem”:


openssl genrsa -aes256 -out private/ca.key.pem 4096




Change permissions on the “ca.key.pem” file to read only, not your group and not others:


chmod 400 private/ca.key.pem



Create the Root Authority Self-signed Certificate


W A R N I N G: when prompted during creation of a signing request, enter a CN matching the hostname of the system on which the the cert will be used.

Run the cert request utility using config. file: “/root/ca/openssl.cnf”, and the key in “/ca/key.pem” to create a new x509 certificate valid for 20 years. Encrypt it with an SHA256 password, use v3_ca extensions defined in openssl.cfg and name the certificate in “certs/ca.cert.pem”:


openssl req -config /root/ca/openssl.cnf -key private/ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem




Change permissions on the “ca.cert.pem” file to read-only, and make it readable by everyone. Any browser with it installed will recognize valid certificates signed by the CA key as trusted:


chmod 444 certs/ca.cert.pem




Display the certificate text on the screen so you can inspect it for errors:


openssl x509 -noout -text -in certs/ca.cert.pem



Create the Intermediate Authority


Create a directory named: “intermediate”, and inside it, subdirectories named: “certs”, “crl”, “newcerts”, “private”, “csr” and “client keys”:


mkdir -p intermediate/{certs,crl,newcerts,private,csr,client_keys}




Change directories to “intermediate”:


cd intermediate




Set full permissions on the “private” directory for the user only:


chmod 700 private




Create a file: “index.txt” that will serve as the CA database. This plain text file records every certificate the CA has issued, including its status, expiration date, revocation date if applicable, serial number, and certificate subject. Each line corresponds to one certificate and helps the CA keep track of certificates it has issued and their current status:


touch index.txt




Generate the word “1000” and redirect the output to the “serial” file:


echo 1000 > serial




Print the words “1000” to the file: “crlnumber”:


echo 1000 > crlnumber




Run the nano editor and open the “openssl.cnf” file:


nano /root/ca/intermediate/openssl.cnf




After the file opens, you must copy the contents of the “IntermediateCA.openssl.cnf” file on this wiki page and paste it into the “openssl.cnf” file and save it.



Create the Intermediate Authority Key


Generate a new RSA private key, 4096 bits long and encrypted with AES-256, and output it to file: “private/intermediate.key.pem”:


openssl genrsa -aes256 \-out private/intermediate.key.pem 4096




Change the permissions on the “intermediate.key.pem” file to read for the user and no access for groups or others:


chmod 400 private/intermediate.key.pem


Creating an Intermediate Certificate Request to Root


Create a certifcate signing request for the intermediate CA, using settings in the “openssl.cnf” file, generate an SHA256 hash of the key so the CA signing the certificate can verify the integrity of the received key, and save the request to the: “csr” directory of the root CA:


openssl req -config /root/ca/intermediate/openssl.cnf \ -new -sha256 -key private/intermediate.key.pem \ -out /root/ca/csr/intermediate.csr.pem



Create the Intermediate Certificate


Change to the: “/root/ca” directory:


cd /root/ca




Run the configure routine on the “opensslf.cnf” configuration file, telling openssl to act as the CA. Prevent readable data being displayed on the screen. Tell openssh to encrypt with SHA256. Use the certificate signing request created in the previous step above and return the signed certificate to the intermediate CA certs folder:


openssl ca -config /root/ca/openssl.cnf -extensions v3_intermediate_ca -days 3650 -notext -md sha256 -in csr/intermediate.csr.pem -out intermediate/certs/intermediate.cert.pem




Change to the “intermediate” directory:


cd intermediate




Change permissions on the: “intermediate.cert.pem” file to read-only for the owner, group and others:


chmod 444 certs/intermediate.cert.pem




Run the OpenSSL tool to generate an X.509 certificate, using file: “/certs/intermediate.cert.pem” for input. Prevent output of the encoded version of the certificate, so no raw cert data is printed. Display the certificate in human-readable format:


openssl x509 -noout -text -in certs/intermediate.cert.pem




Perform a verification using the “ca.cert.pem” file to check the chain of trust and ensure ensure everything is correct:


openssl verify -CAfile /root/ca/certs/ca.cert.pem certs/intermediate.cert.pem



Create a Certificate Chain File


Use the concatenate command to combine the root and intermediate certificates, in ascending order of trust, into one file:


cat certs/intermediate.cert.pem /root/ca/certs/ca.cert.pem > certs/ca-chain.cert.pem




Change permissions on the “chain.cert.pem” file to read-only for the user, group and others:


chmod 444 certs/ca-chain.cert.pem


CONSTRUCTION

OF THE

CERTIFICATE AUTHORTY

IS NOW COMPLETE



Create a Key and Certificates for FreshTomato

Create a FreshTomato Key Using EC PRIME256V1


Change to the “/root/ca/intermediate” directory:


cd /root/ca/intermediate




Run the openssl tool and, using Prime256v1 elliptical curve cryptography, generate a private key. Output the private key to file: “/client_keys/FT.key.pem”. (EC cryptography is a good compromise between high security and key length-important for FreshTomato:


openssl ecparam -out client_keys/FT.key.pem -name prime256v1 -genkey


Create a Freshtomato Certificate Signing Request

(Modify these commands to match your own openssl.cnf files on your CA).


Run the OpenSSL cert signing request function. Using the private key in file “FT.key.pem”, sign the new certificate signing request with the SHA256 algorithm. Save the output in file: “/csr/FT.cert.csr.pem”. Use the following subject Distinguished Name settings for the signing request:

  • C=FI: Country = Finland
  • ST=Ohio: State = Ohio
  • L=Timbucktwo: Locality/City = Timbucktwo
  • O=Section 8: Organization = Section 8
  • OU=Section 8 IT: Organizational Unit = Section 8 IT
  • CN=FT_Router_Sane: Common Name = FT_Router_Sane



Add an X.509 extension to the CSR to specify Subject Alternative Names (SANs). This will define additional identities for the certificate:

  • IP address: 192.168.1.1
  • DNS names: sane, sane.nuts



openssl req -key client_keys/FT.key.pem -new -sha256 -out csr/FT.cert.csr.pem -subj “/C=FI/ST=Ohio/L=Timbucktwo/O=Section 8/OU=Section 8 IT/CN=FT_Router_Sane” -addext “subjectAltName=IP:192.168.1.1,DNS:sane,DNS:sane.nuts”




Now, verify your certificate signing request has all the correct information. (If not, repeat the process):


openssl req -noout -text -in csr/FT.cert.csr.pem



Sign the Certificate with the Intermediate Certificate Authority


Run the OpenSSL CA utility and sign the CSR in file: “/csr/Ft/cert.csr.pem”, using settings in the CA setup file: “openssl.cnf” and applying the certificate extensions in: “server_cert”. Set the validity period of the cert to 375 days and include only a PEM version cert (no ASCII version) in the output file. Use SHA-256 as the hash algorithm during signing. Output the result to the newly-signed certificate to file: “/newcerts/Ft.cert.pem”. Exit the script/terminal session.


openssl ca -config openssl.cnf -extensions server_cert -days 375 -notext -md sha256 -in csr/FT.cert.csr.pem -out newcerts/FT.cert.pem exit



Set up Root with Elliptical Curve SSH Keys


(This is done with root credentials because the certificates must be installed in FreshTomato. Using root access helps avoid unsecured steps in between).

Change to the: “/root” directory:


cd /root




Generate a public and private SSH key pair using the Ed25519 hashing algorithm. Add a comment containing a default email address to the key:


ssh-keygen -t ed25519 -C “your_email@example.com” FIXME




Display the contents of the public SSH key file: “/root/.ssh/id_ed25519.pub”:


cat /root/.ssh/id_ed25519.pub
(The contents should look similar to: “ssh-ed25519 AAA….Oo your_email@example.com” copy the whole thing) FIXME




Now, connect to the router's web interface and go to the Admin Access menu. In the SSH Server section, paste the output copied from the previous step in the “Authorized keys” section).


Uncheck:

  • “Allow password login”
  • “WAN access”


Now, check:

  • “Enable on Startup”, then “Save.”





Finally, click “Start Now” to restart the SSH server.



Upload the Custom Certificates / Key to the Router and Write them to NVRAM


Using the secure copy command (and legacy protocol), copy the: “FT.key.pem” file and the: ““FT.cert.pem” file from the Intermediate CA to the root user FreshTomato home directory (whose default name is assumed to be: “FT”)


scp -O /root/ca/intermediate/client_keys/FT.key.pem /root/ca/intermediate/newcerts/FT.cert.pem root@FT




Run the SSH command to connect via SSH to the root account on the host router named: “FT”:


ssh root@FT




Rename the file: “FT.key.pem” file to: “key.pem”:


mv FT.key.pem key.pem




Rename the file: “FT.key.pem” to cert.pem:


mv FT.cert.pem cert.pem




Concatenate the contents of the “key.pem” and “cert.pem” files and write the combined content into new file: ”/etc/server.pem“:


cat key.pem cert.pem > /etc/server.pem




Copy the ”./cert.pem“ file to the ”/etc“ directory:


cp ./cert.pem /etc/cert.pem




Copy the ”./key.pem“ file to the ”/etc“ directory:


cp ./key.pem /etc/key.pem




Run the stream text editor tool and make it open the “cert.pem” file in place, overwriting any changes and cutting off content after the words: “END CERTIFICATE”:



sed -i ”/END CERTIFICATE/q“ /etc/cert.pem




Run the tar command and compress the “cert.pem” and “key.pem files into the ”/tmp/cert.tar” file, preserving all /path/ information)


/bin/tar -C / -cf /tmp/cert.tar etc/cert.pem etc/key.pem




Run the gzip archive tool to further compress the “cert.tar” file (making it into a “tar.gzip” file):


/bin/gzip -f /tmp/cert.tar





Encode the “/tmp/cert.tar.gz” tarball archive of SSL certificate files, encode it in base64 using OpenSSL, remove any newline characters from the encoded string, and then set this base64-encoded string as the value of the “https_crt_file” variable in NVRAM.


nvram set https_crt_file=“$(/usr/sbin/openssl enc -base64 < /tmp/cert.tar.gz | tr -d '\n')”




Commit all the changes to NVRAM:


nvram commit




Finally, restart the HTTP daemon:


service httpd restart



The “intermediate.cert.pem” file is now ready to be imported into your browser. The author uses Brave, in which you can import the file via “Trusted certificates”, (not “Intermediate certificates”).

The process is now complete. Now, you should be able to access your FreshTomato web interface using the custom certificates you created with your own CA. If something isn't working, review all steps and double-check that they were properly completed.


Set up a Custom SSL Cert - Notes and Troubleshooting


Download the two configuration files needed to create the Custom Certificate Authority here:




The OpenSSL ccparam subcommand doesn't directly support adding a password to a key. However, it can be piped back through OpenSSL to give it an extra layer of protection. For example, typing:

openssl ecparam -genkey -name prime256v1 | openssl ec -aes256 -out yourkey.pem



Since r2025.3, FreshTomato doesn't require the CN to match the Hostname. The following steps will allow you test your setup to verify this. However, please note that testing this could cause FreshTomato to overwrite your custom cert. If it does happens, just upload your certificate again and SSH will still function fine.

  • In the web interface, go to the Admin Access menu and check the CN under “SSL Certificate”.

  • Connect via SSH to FreshTomato and run the following commands:
    • “nvram get https_crt_cn” . The result should match the CN displayed on the Admin Access page.
    • “nvram set https_crt_cn=some.random.thing”
    • “nvram commit”
    • Reboot the router.

  • When the router is accessible again you'll notice that the certificate is still working fine.
    Go back to the Admin Access page and you will see the CN is now “some.random.thing”

  • If you clicked “Save” at the bottom of the page, or reboot the router from the web interface, your cert would be overwritten.

  • At this point you can go back to SSH and change back your CN and commit or, of test it further.
custom_ssl_cert_local_cert_authority.1762989782.txt.gz · Last modified: by hogwild