This script checks if the FRITZ!Box has a valid ssl certificate and renews it with certbot if necessary.
In order to upload the certificate to the FRITZ!Box, a separate user must be created in the webinterface beforehand.

It makes sense to call the script regularly (e.g. every 7 days) by a cronjob. A further development could be the dispatch of a mail when the certificate is renewed.

#!/bin/bash

# parameters
USERNAME="fritzuser"
PASSWORD="*********"
CERTPATH="/root"
CERTPASSWORD=""
FQDN=fritzbox.zaage.it

# Check if the certificate is expiring soon
echo | openssl s_client -servername $FQDN -connect $FQDN:443 2>/dev/null | openssl x509 -noout -checkend 2592000

if [ "$?" == "1" ]; then
  # Expiring in less than one month. We need to renew
  certbot certonly --csr /root/fritzbox.csr -d fritzbox.zaage.it --nginx

  # make and secure a temporary file
  TMP="$(mktemp -t XXXXXX)"
  chmod 600 "$TMP"

  # login to the box and get a valid SID
  CHALLENGE=$(wget -q -O - $FQDN/login_sid.lua | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')
  HASH="$(echo -n "$CHALLENGE"-"$PASSWORD" | iconv -f ASCII -t UTF16LE |md5sum|awk '{print $1}')"
  SID=$(wget -q -O - "$FQDN/login_sid.lua?sid=0000000000000000&username=$USERNAME&response=$CHALLENGE-$HASH"| sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')

  # generate our upload request
  BOUNDARY="---------------------------"$(date +%Y%m%d%H%M%S)
  printf -- "--$BOUNDARY\r\n" >> "$TMP"
  printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n$SID\r\n" >> "$TMP"
  printf -- "--$BOUNDARY\r\n" >> "$TMP"
  printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n$CERTPASSWORD\r\n" >> "$TMP"
  printf -- "--$BOUNDARY\r\n" >> "$TMP"
  printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> "$TMP"
  printf "Content-Type: application/octet-stream\r\n\r\n" >> "$TMP"
  cat $CERTPATH/fritzbox.key >> "$TMP"
  cat $CERTPATH/0001_chain.pem >> "$TMP"
  printf "\r\n" >> "$TMP"
  printf -- "--$BOUNDARY--" >> "$TMP"

  # upload the certificate to the box
  wget -q -O - $FQDN/cgi-bin/firmwarecfg --header="Content-type: multipart/form-data boundary=$BOUNDARY" --post-file "$TMP" | grep SSL

  # clean up
  rm -f "$TMP" 0000_cert.pem 0000_chain.pem 0001_chain.pem
fi