Skip to content

Instantly share code, notes, and snippets.

@davidbalbert
Last active April 27, 2025 02:06
Show Gist options
  • Save davidbalbert/6815258 to your computer and use it in GitHub Desktop.
Save davidbalbert/6815258 to your computer and use it in GitHub Desktop.
How to install custom SSL certificates on an ASUS RT-N66U running asuswrt-merlin
###########################################
# IMPORTANT NOTE:
#
# As of asuswrt-merlin 380.67 Beta, you
# can now configure SSL certificates from
# the Webui, making these instructions
# unnecessary.
###########################################
# First, enable SSH in the Administration->System tab.
# Then log in to the device.
# Verify that https_crt_save is off
admin@RT-N66U:/tmp/home/root# nvram get https_crt_save
0
# Enable https_crt_save and verify that it was set correctly
admin@RT-N66U:/tmp/home/root# nvram set https_crt_save=1
admin@RT-N66U:/tmp/home/root# nvram get https_crt_save
1
# Write your custom key and certificate to the ephemeral file system.
# Note that these files will not be preserved on restart.
admin@RT-N66U:/tmp/home/root# cat >/etc/key.pem
# paste in key
admin@RT-N66U:/tmp/home/root# cat >/etc/cert.pem
# paste in cert
# Verify https_crt_file is empty
admin@RT-N66U:/tmp/home/root# nvram get https_crt_file
admin@RT-N66U:/tmp/home/root#
# Restart httpd. When httpd starts up with https_crt_save enabled, it does the
# following: If /etc/cert.pem and /etc/key.pem exist, it tars them together and
# saves them in https_crt_file. If they do not exist (this would be the case
# on reboot) and https_crt_file exists, httpd will extract the contents of
# https_crt_file. You can see how this works in the start_ssl function here:
# https://github.com/RMerl/asuswrt-merlin/blob/master/release/src/router/httpd/httpd.c
admin@RT-N66U:/tmp/home/root# service restart_httpd
# Ensure https_crt_file is now full
admin@RT-N66U:/tmp/home/root# nvram get https_crt_file
# ...snip...
# Reboot AP to make sure cert is put back on boot
admin@RT-N66U:/tmp/home/root# reboot
@jmontleon
Copy link

Something changed from some point after I wrote my comment above. But with what is current for my router (3.0.0.4.388_24329-g5906523) I was able to replace the certs.

I have this written down now.

mkdir tmp/etc
cd tmp

cat << EOF > etc/cert.pem
...
EOF

cat << EOF > etc/key.pem
...
EOF

tar -C / -czf /jffs/cert.tgz etc/cert.pem etc/key.pem
nvram set https_crt_save=1
service restart_httpd

Mostly, it looks like the difference is nvram set https_crt_save=1.

@janedenone
Copy link

This is what I do (after creating cert.pem and key.pem in /jffs/):

cd /jffs/
rm cert.tgz
tar -czf cert.tgz cert.pem key.pem
nvram set https_crt_save=1

At this point nvram get https_crt_file returns nothing, and as soon as I execute service restart_httpd, the file cert.tgz gets overwritten by the system.

@janedenone
Copy link

janedenone commented Sep 3, 2024

Maybe try updating your firmware? I have RT-AC58U with 3.0.0.4.382_52504 firmware and here is the full script that works for me:

...

If I execute the steps manually, this happens:

admin@ZenWiFi_XT8-A5C0:/tmp/etc# ls *.pem
cacert.pem      cacert_gen.pem  cakey.pem       cakey_gen.pem   cert.pem        cert_gen.pem    key.pem         key_gen.pem     server.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# rm *.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# nvram set https_crt_save=0
admin@ZenWiFi_XT8-A5C0:/tmp/etc# nvram unset https_crt_file
admin@ZenWiFi_XT8-A5C0:/tmp/etc# service restart_httpd

Done.
admin@ZenWiFi_XT8-A5C0:/tmp/etc# nvram unset https_crt_file
admin@ZenWiFi_XT8-A5C0:/tmp/etc# service restart_httpd

Done.
admin@ZenWiFi_XT8-A5C0:/tmp/etc# nvram get https_crt_file
admin@ZenWiFi_XT8-A5C0:/tmp/etc# sleep 20
admin@ZenWiFi_XT8-A5C0:/tmp/etc# ls *.pem
cacert.pem      cacert_gen.pem  cakey.pem       cakey_gen.pem   cert.pem        cert_gen.pem    key.pem         key_gen.pem     server.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# rm *.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# nvram set https_crt_save=1
admin@ZenWiFi_XT8-A5C0:/tmp/etc# cp /jffs/cert.pem .
admin@ZenWiFi_XT8-A5C0:/tmp/etc# cp /jffs/key.pem .
admin@ZenWiFi_XT8-A5C0:/tmp/etc# ll *.pem
-rw-rw-rw-    1 admin  root          1249 Sep  3 08:12 cert.pem
-rw-rw-rw-    1 admin  root          1704 Sep  3 08:12 key.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# cat key.pem > server.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# cat cert.pem >> server.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# ls *.pem
cert.pem    key.pem     server.pem
admin@ZenWiFi_XT8-A5C0:/tmp/etc# service restart_httpd

Done.
admin@ZenWiFi_XT8-A5C0:/tmp/etc# nvram get https_crt_file
admin@ZenWiFi_XT8-A5C0:/tmp/etc# ls *.pem
cacert.pem      cacert_gen.pem  cakey.pem       cakey_gen.pem   cert.pem        cert_gen.pem    key.pem         key_gen.pem     server.pem

/jffs/cert/tgz is recreated after all the steps above, but nvram get https_crt_file still returns nothing (and the router still presents the Asus certificate).

@stephen-dunne
Copy link

Yep, I've got an RT-AX88U Pro running Merlin 3004.388.8_2 and I can't replace the ss cert via the cli either.

I can manually update it via the web UI so I know my cert.pem and key.pem are good, but when I follow the steps suggested, i.e.
With etc/cert.pem etc/key.pem being my validated pem based cert and private key

steph@xxx:/tmp# tar -C / -czf /jffs/cert.tgz etc/cert.pem etc/key.pem
steph@xxx:/tmp# tar -tzf /jffs/cert.tgz
etc/cert.pem
etc/key.pem
steph@xxx:/tmp# nvram set https_crt_save=1
steph@xxx:/tmp# service restart_httpd

Done.

I see that /jffs/cert.tgz is overwritten with a server cert issued by a local CA on the router and the archive contents look like this:
steph@xxx:/tmp# tar -tzf /jffs/cert.tgz
etc/cacert.pem
etc/cakey.pem
etc/cert.pem
etc/key.pem
etc/cacert_gen.pem
etc/cakey_gen.pem
etc/cert_gen.pem
etc/key_gen.pem
steph@xxx:/tmp#

@jmontleon
Copy link

jmontleon commented Sep 12, 2024

Maybe extract and retain the other files. The archive looks like this for me, which suggests in december I replaced the key and crt, but kept the rest. Perhaps if other files are missing it regenerates / restores it. I should have kept better notes...

 tar tzvf cert.tgz
-rw-rw-rw- 0/0      1517 2018-05-05 01:05:31 etc/cacert.pem
-rw------- 0/0      1679 2018-05-05 01:05:31 etc/cakey.pem
-rw-rw-rw- 0/0      1838 2023-12-19 22:53:50 etc/cert.pem
-rw------- 0/0      1704 2023-12-19 22:53:51 etc/key.pem
-rw-rw-rw- 0/0      1517 2018-05-05 01:05:31 etc/cacert_gen.pem
-rw------- 0/0      1679 2018-05-05 01:05:31 etc/cakey_gen.pem
-rw-rw-rw- 0/0      1838 2018-05-05 01:05:25 etc/cert_gen.pem
-rw------- 0/0      1675 2018-05-05 01:05:25 etc/key_gen.pem

@stephen-dunne
Copy link

Yep, that was the missing piece of the puzzle.

Extracting the existing /jffs/cert.tgz into a temp subdir, overwriting etc/key.pem and etc/cert.pem and recreating /jffs/cert.tgz then running nvram set https_crt_save=1 and service restart_httpd seems to do the trick.

Finally !! Thanks for your help !

@cristit
Copy link

cristit commented Sep 21, 2024

Yep, that was the missing piece of the puzzle.

Extracting the existing /jffs/cert.tgz into a temp subdir, overwriting etc/key.pem and etc/cert.pem and recreating /jffs/cert.tgz then running nvram set https_crt_save=1 and service restart_httpd seems to do the trick.

Finally !! Thanks for your help !

I tryed exactly this and checked the md5 sum:
Before copy cert.tgz to /jffs/cert.tgz md5 was: ef7ba5b1ec34074a56c1349653860d82
after copy: 899f0ec767378c8e5a84079d4e6315d3 /jffs/cert.tgz
then I ran following commands:

nvram set https_crt_save=1
service restart_httpd

after last command, the md5 was ef7ba5b1ec34074a56c1349653860d82 (as initial).
I'm using: 3.0.0.4.388_24328-g1e6e634
any idea?

@janedenone
Copy link

after last command, the md5 was ef7ba5b1ec34074a56c1349653860d82 (as initial). I'm using: 3.0.0.4.388_24328-g1e6e634 any idea?

Same here. I am happy for everyone who managed to upload and activate their own certificate, but lost hope for myself.

@andrewtcrooks
Copy link

andrewtcrooks commented Apr 27, 2025

I am using firmware 3004.388.9 and I'm having the same issue.

My sys log says this after i uploaded my own certificates:

  Apr 26 18:58:45 httpd: Can't get basic constrain from /jffs/.cert/cert.pem
  Apr 26 18:58:45 httpd: Delete uploaded certificate
  Apr 26 18:58:48 admin: Waiting 5 seconds for VPN...

and ChatGPT says this:

  this exact error:
  
  httpd: Can't get basic constrain from /jffs/.cert/cert.pem
  httpd: Delete uploaded certificate

  means:
  your uploaded SSL certificate is being rejected by the Asuswrt-Merlin firmware because it doesn’t contain a required X.509 extension called the Basic Constraints field.
  


  Specifically, Merlin expects certificates to include:
  
  ✅ X509v3 Basic Constraints
  
  ✅ X509v3 Subject Key Identifier
  
  ✅ X509v3 Authority Key Identifier
  
  If these aren't present exactly right, httpd (the web interface server) auto-deletes your uploaded cert.
  
  ✍ Why this happens:
  Since 2024 and even stronger with firmware builds 3004.388.6 → 3004.388.9,
  Merlin strictly enforces proper X.509 standards.
  They're trying to block bad SSL setups that could crash or expose the router.
  
  Old simple OpenSSL certs (without these extensions) get rejected now.
  
  🛠 How you fix it:
  You need to regenerate your certs with an openssl.cnf that includes:
  
  bash
  Copy
  Edit
  basicConstraints = CA:FALSE
  subjectKeyIdentifier = hash
  authorityKeyIdentifier = keyid,issuer
  ✅ This will make the router happy.
  ✅ It will keep your Root CA as is, no need to redo that.
  ✅ Only the server certs (e.g., for rt.datanovaconsulting.com) need updating.

I hope this clears things up for everyone concerning their custom certificates issues!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment