I wrote an article about installing a Let’s Encrypt TLS certificate on an HP LaserJet printer a while ago. Since then, I’ve been annoyed by having to install updated certificates manually, so I decided to look at how I could automate it.
TechRadar has a great article on securing printers, but how do you automate it? Well, with a certificate authority like Let’s Encrypt for starters, but there’s no mechanism for the printer to automatically update its certificate after it expires.
I’ve set my desktop machine to certbot and renew the certificate automatically. An evening’s hacking around the web interface showed it’s really easy to install a certificate automatically.
This is the magic command to install the certificate:
curl -v --insecure https://HOSTNAME/hp/device/Certificate.pfx --form upload=@/tmp/cert.pfx --form Password=password
Replace HOSTNAME with the hostname of your printer and change /tmp/cert.pfx as required. If you want to know how to create the PFX file, see my original post.
Has anyone else found out how to do this? If they have, they’ve not posted about it!
Works great on some devices, but I have one laserjet that might be too old for that, and they apparently use different URLs for the InkJets
I did try your command, but it fails for me.
I changed the hostname accordingly, the location of the certificate file and the password to the export password.
The error I receive is:
HTTP error before end of send, stop sending
<
* Closing connection 0
In the interest of paying it forward for others that may run across this page. Different HP printers have different ways of getting the certificates to them. You can (as I did) simply open up the certificate interface in Safari or Chrome and enable developer tools to figure out what the right call is. It seems to change every two or three printer generations. If you have a modicum of developer / debugging expertise, you can quickly deduce what the right one is for you if the above (or below) doesn’t work.
The magic incantation for some of the more modern HP printers is:
curl -silent --show-error --insecure "https://HOSTNAME/Security/DeviceCertificates/NewCertWithPassword/Upload?fixed_response=true" --form certificate=@"YourCert.pfx" --form password="PASSWORD"
Replace HOSTNAME with the printer’s hostname/IP.
Replace YourCert.pfx with the path to the PFX-encoded certificate.
Replace PASSWORD with the password you used to encrypt the PFX-encoded certificate.
On newer Laserjet models (eg: my M479fdw) which have a more “Javascripty” web interface, the upload URL is different – but a bit of snooping in the Chrome console reveals that it’s basically the same mechanism, just a different URL.
curl -v --insecure -u admin:ADMIN_PASSWORD --form certificate=@cert.pfx --form password=PFX_PASSWORD https://PRINTER_HOSTNAME/Security/DeviceCertificates/NewCertWithPassword/Upload
(that’s all one line).
ADMIN_PASSWORD is the password for the ‘admin’ user you set in the printer’s web UI.
PFX_PASSWORD is the password you set on the PFX certificate file.
PRINTER_HOSTNAME is the network name or IP address of the printer.
Hope this helps!
Superb, thank you very much! I may do a further post aggregating this, and another post.
Worked a charm on my ENVY Pro 6420 – I did find it better to use fullchain.pem rather than cert.pem in the PFX file so that curl doesn’t even need the –insecure on subsequent uploads
Thank you – and to people posting here as well – this is just what I’m looking for.
Thank you very much. This works perfectly for me on a HP LaserJet Pro MFP M426dfw, with the URL: https://HOSTNAME/hp/device/Certificate.pfx
One note. I needed to add credentials in order to complete the request; otherwise, I was receiving 401 (Unauthorized):
curl -v --insecure -u admin:HP_WEB_PORTAL_ADMIN_PASSWD https://HOSTNAME/hp/device/Certificate.pfx
be careful copying the commands here as a hyphen is turned into an em dash and the curl command will fail. Make sure all “-” are literal hyphens
That’s a good point. I’ll see if I can edit those comments.
Hi i just try to Upload the pfx cert on my envy 4520. I have tried on web UI but then i get error code 0XB92E35D0. Than i have found your solution and try it with curl but comes the same error on printer display after upload. Here the answer from printer
C:\curl\bin>curl -v –insecure -u admin:password –form certificate=@cert.pfx –form password=password https://192.168.178.60/Security/DeviceCertificates/NewCertWithPassword/Upload
* Trying 192.168.178.60:443…
* Connected to 192.168.178.60 (192.168.178.60) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / AES256-GCM-SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
* subject: CN=HP23A06A; L=Vancouver; ST=Washington; C=US; O=HP; OU=HP-IPG
* start date: Jun 2 09:20:56 2020 GMT
* expire date: May 31 09:20:56 2030 GMT
* issuer: CN=HP23A06A; L=Vancouver; ST=Washington; C=US; O=HP; OU=HP-IPG
* SSL certificate verify result: self-signed certificate (18), continuing anyway.
* using HTTP/1.x
* Server auth using Basic with user ‘admin’
> POST /Security/DeviceCertificates/NewCertWithPassword/Upload HTTP/1.1
> Host: 192.168.178.60
> Authorization: Basic YWRtaW46VGVsaTA3MSEh
> User-Agent: curl/8.0.1
> Accept: */*
> Content-Length: 5705
> Content-Type: multipart/form-data; boundary=————————c96492a1322c6340
>
* We are completely uploaded and fine
HTTP/1.1 500 Internal Server Error
< Server: HP HTTP Server; HP ENVY 4520 series – K9T09B; Serial Number: TH85H5K0QN0660; Built:Tue Jun 02, 2020 09:20:56AM {CFP1FN2023BR}
< Content-Length: 0
< Cache-Control: must-revalidate, max-age=0
< Pragma: no-cache
<
* Connection #0 to host 192.168.178.60 left intact
Have a HP LaserJet color flow MFP M575 | HP FutureSmart 4 | 4.12.0.1
For anyone else struggling (or just me in a few months when it breaks and I loose my config)
Needed to disable CSRF protection 🙁
https://HOSTNAME/hp/device/GeneralSecurity/Index
I could be less lazy and hunt the page for the CSRF token and then submit, but I don’t have the energy for that right now
then:
curl https://YOURHOSTNAME/hp/device/SignIn/Index -v \
-c ./cookies.txt \
–form agentIdSelect=hp_EmbeddedPin_v1 \
–form PinDropDown=AdminItem \
–form PasswordTextBox=YOURPASSWORD \
–form signInOk=Sign+In \
–insecure && \
curl -v -b ./cookies.txt –insecure \
–form .Import_FileName_handle=@certificate.pfx \
–form Finish=Finish \
–form CSRFToken= \
–form Hide=Hide \
–form password=CERTPASSWORD \
https://YOURHOSTNAME/websecurity/cert_import.htm/config
or for a collected package:
https://github.com/chrisns/infra/blob/main/devices-tls/hp-m575m.cns.me.yaml
I found this blog today, and it was enough for me to get this to work with my own printer. So, I thought, why not move it closer to full automation? Here’s a bash script I put together with the intention of eventually adding it to a system timer (much like the one certbot uses to renew the certificate).
P.S. I hope that the blog comment doesn’t mess up the formatting:
#!/bin/bash
# Name: installhpcert.sh
# Date: 2024-03-15
# Author: Chuck Renner
# License: MIT License https://opensource.org/license/mit (Use as you want and author is not liable)
# Version 0.1.1 (alpha)
# Inspired by and uses information from Peter Hicks's Blog:
# https://blog.poggs.com/2020/03/18/printer-security-installing-tls-certificates-on-hp-printers-automatically/
#
# I have done limited testing on my own HP OfficeJet 5200 Series Printer. Other HP printers will
# likely need different settings.
#
# If customizing, tweaking or troubleshooting, set VERBOSE=1 and comment out the lines at the end
# that delete the temporary files (so that they can be examined).
#
# This script sources CONF (by default /etc/installhpcert.ini) for its settings.
#
# Script configuration parameters (set in CONF below; by default /etc/installhpcert.ini):
# * INSECURE=0 - Set to 1 if the current printer certificate is expired, self-signed, or invalid.
# * VERBOSE=0 - Set to 1 if you want to pass the -v option to curl. Helps with troubleshooting.
# When set to 1 also provides additional output from this script.
# * PHOST=hpabc123 - The hostname (without domain name) of the printer.
# * PDOM=example.org - The domain name (without hostname) of the printer.
# * NOTE: The combined PHOST.PDOM should be the printer Fully Qualified Domain Name (FQDN).
# * NOTE: The printer FQDN should DNS resolve in your network. If it does not you may have to
# fix DNS or tamper with your hosts file (fixing DNS is the correct way).
# * PUSER=admin - This should be the administrative username for the printer.
# * PPASS=password - This should be the password for the administrative user for the printer.
# * LELIVE=/etc/letsencrypt/live - Location of the Let's Encrypt live folder
# This script must be run as root or sudo (because access to the certificate key is restricted by
# permissions and ownership by design).
# This script assumes all of the following:
# * The user has a compatible HP printer and wants to install a valid security certificate
# * The user has a domain name that can be used for CA issued TLS certificates
# * The DNS server for the user's network resolves PHOST.PDOM to the same HP printer
# * The user already has Let's Encrypt certbot setup for PHOST.PDOM and it has issued a current
# valid certificate for PHOST.PDOM
# * (optional) Certbot is setup to automatically renew the same certificate
# * The user has the administrator name and password for the printer in the configuration file
# * openssl is current, correctly setup, and in the PATH
# * curl is current, correctly setup, and in the PATH (I had to compile a current version of curl
# manually because the one in apt for Ubuntu 22.04 was old and the one in snap for the same is
# somewhat broken).
# This script when configured correctly and run for a compatible HP printer should do all of the
# following:
# * Create a temporary, password encrypted PKCS12 file containing the certificate fullchain and
# private key for PHOST.PDOM issued by Let's Encrypt certbot.
# * Use curl to connect and authenticate to the printer (NOTE: if the printer currently has an
# invalid, self-issued, or expired certificate, you must set the INSECURE configuration option
# to 1 so that curl will connect anyway; Once the certificate is valid, subsequent runs can
# have the INSECURE configuration option set back to 0.).
# * Use a temporary cookie jar for maintaining a session for subsequent connections to the printer.
# * Send the printer the encrypted PKCS12 file (and its random password) for it to install
# * Dump the installed printer certificate information to stdout so that the user can verify the
# certifate was installed on the printer.
# * Delete all of the temporary files used by the script.
# * Exit.
# This script is intended to be automated and it should be possible to add this to a timer
# (like the one certbot uses to update the certificate), but I haven't tried putting it on
# a timer yet.
# Configuration file location (change this setting if your configuration file is elsewhere)
CONF=/etc/installhpcert.ini
# Exit if user is script is not running with root privileges.
if [ `id -u` -ne 0 ]
then echo Run this script using sudo!
exit
fi
# Load (source) the configuration file
source $CONF
# Define functions
function str_random() {
# str_random from https://www.baeldung.com/linux/generate-random-string-using-random
array=()
for i in {a..z} {A..Z} {0..9};
do
array[$RANDOM]=$i
done
printf %s ${array[@]::8}
}
function urlencode() {
# urlencode and urldecode from https://gist.github.com/cdown/1163649
# urlencode
old_lc_collate=$LC_COLLATE
LC_COLLATE=C
local length="${#1}"
for (( i = 0; i < length; i++ )); do
local c="${1:$i:1}"
case $c in
[a-zA-Z0-9.~_-]) printf '%s' "$c" ;;
*) printf '%%%02X' "'$c" ;;
esac
done
LC_COLLATE=$old_lc_collate
}
function urldecode() {
# urldecode
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\\x}"
}
INSECURE_OPTION=
VERBOSE_OPTION=
if [ $INSECURE -eq 1 ]
then INSECURE_OPTION=--insecure
fi
if [ $VERBOSE -eq 1 ]
then VERBOSE_OPTION=--verbose
fi
# Temporary directory
DUMPDIR=/tmp
# Generate random password to encrypt the temporary PFX file (and to be passed to printer)
PFXPASS=$(str_random)
# Printer Fully Qualified Domain Name (FQDN)
PFQDN=$PHOST.$PDOM
# Encrypted temporary PKCS12 PFX file to make, encrypt, and upload
PFX=$DUMPDIR/$PHOST.pfx
# Cookie Jar file
JAR=$DUMPDIR/$PHOST.cjar
# Curl output files prefix
OUT=$DUMPDIR/$PHOST
# URL encode the printer username and password
PUSER=$(urlencode $PUSER)
PPASS=$(urlencode $PPASS)
echo Creating encrypted PFX
if [ $VERBOSE -eq 1 ]
then echo Running openssl pkcs12 -export -out $PFX -inkey $LELIVE/$PFQDN/privkey.pem -in $LELIVE/$PFQDN/fullchain.pem -password pass:\
fi
openssl pkcs12 -export -out $PFX -inkey $LELIVE/$PFQDN/privkey.pem -in $LELIVE/$PFQDN/fullchain.pem -password pass:$PFXPASS
chmod 666 $PFX
echo Completed creating PFX
echo Pushing encrypted PFX to printer \(with password\)
if [ $VERBOSE -eq 1 ]
then echo Running curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u \"$PUSER:\\" \"https://$PFQDN/\" --output $OUT-1.html
fi
curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u "$PUSER:$PPASS" "https://$PFQDN/" --output $OUT-1.html
chmod 666 $JAR
chmod 666 $OUT-1.html
if [ $VERBOSE -eq 1 ]
then echo Running curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u \"$PUSER:\\" \"https://$PFQDN/Security/DeviceCertificates/1/Info\" --output $OUT-2.html
fi
curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u "$PUSER:$PPASS" "https://$PFQDN/Security/DeviceCertificates/1/Info" --output $OUT-2.html
chmod 666 $OUT-2.html
if [ $VERBOSE -eq 1 ]
then echo Running curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u \"$PUSER:\\" --form \"certificate=@$PFX\" --form \"password=\\" --referer \"https://$PFQDN/Security/DeviceCertificates/1/Info\" \"https://$PFQDN/Security/DeviceCertificates/NewCertWithPassword/Upload\" --output $OUT-3.html
fi
curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u "$PUSER:$PPASS" --form "certificate=@$PFX" --form "password=$PFXPASS" --referer "https://$PFQDN/Security/DeviceCertificates/1/Info" "https://$PFQDN/Security/DeviceCertificates/NewCertWithPassword/Upload" --output $OUT-3.html
chmod 666 $OUT-3.html
if [ $VERBOSE -eq 1 ]
then echo Running curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u \"$PUSER:\\" --referer "https://$PFQDN/Security/DeviceCertificates/NewCertWithPassword/Upload" \"https://$PFQDN/Security/DeviceCertificates/1/Info\" --output $OUT-4.html
fi
curl $VERBOSE_OPTION $INSECURE_OPTION --cookie-jar $JAR --cookie $JAR -u "$PUSER:$PPASS" --referer "https://$PFQDN/Security/DeviceCertificates/NewCertWithPassword/Upload" "https://$PFQDN/Security/DeviceCertificates/1/Info" --output $OUT-4.html
chmod 666 $OUT-4.html
echo Completed pushing PFX to printer
if [ $VERBOSE -eq 1 ]
then echo Deleting files $PFX, $JAR, $OUT-1.html, $OUT-2.html, $OUT-3.html
echo PFX=$PFX
fi
rm $PFX
rm $JAR
rm $OUT-1.html
rm $OUT-2.html
rm $OUT-3.html
if [ $VERBOSE -eq 1 ]
then echo Completed deleting files
fi
echo Completed printer certificate push script
echo Results:
cat $OUT-4.html
rm $OUT-4.html