Digitally signing PDF documents in Linux: with hardware token & Okular

We are living in 2022. And it is now possible to digitally sign a PDF document using libre software. This is a love letter to libre software projects, and also a manual.

For a long time, one of the challenges in using libre software in ‘enterprise’ environments or working with Government documents is that one will eventually be forced to use a proprietary software that isn’t even available for a libre platform like GNU/Linux. A notorious use-case is digitally signing PDF documents.

Recently, Poppler (the free software library for rendering PDF; used by Evince and Okular) and Okular in particular has gained a lot of improvements in displaying digital signature and actually signing a PDF document digitally (see this, this, this, this, this and this). When the main developer Albert asked for feedback on what important functionality would the community like to see incorporated as part this effort; I had asked if it would be possible to use hardware tokens for digital signature. Turns out, poppler uses nss (Network Security Services, a Mozilla project) for managing the certificates, and if the token is enrolled in NSS database, Okular should be able to just use it.

This blog post written a couple of years ago about using hardware token in GNU/Linux is still actively referred by many users. Trying to make the hardware token work with Okular gave me some more insights. With all the other prerequisites (token driver installation etc.) in place, follow these steps to get everything working nicely.


  1. There are 2 options to manage NSSDB: (i) manually by setting up $HOME/.pki/nssdb, or (ii) use the one automatically created by Firefox if you already use it. Assuming the latter, the nssdb would be located in the default profile directory $HOME/.mozilla/firefox/<random.dirname>/ (check for existence of the file pkcs11.txt in that directory to be sure).
  2. Open Okular and go to SettingsConfigure backendPDF and choose/set the correct certificate database path, if not already set by default.
Fig. 1: Okular PDF certificate database configuration.
  1. Start the smart card service (usually auto-started, you won’t have to do this): either pcsc_wd.service (for WatchData keys) or pcscd.service.
  2. Plug in the hardware token.
  3. Open a PDF in Okular. Add digitial signature using menu ToolsDigitally Sign
  4. This should prompt for the hardware token password.
Fig. 2: Digital token password prompt when adding digital sign in the PDF document.
  1. Click & drag a square area where you need to place the signature and choose the certificate. Note that, since Poppler 22.03, it is also possible to insert signature in a designated field.
Fig. 3: Add digital signature by drawing a rectangle.
  1. Signature will be placed on a new PDF file (with suffix -signed) and it will open automatically.
Fig. 4: Digitally signed document.
  1. You can also see the details of the hardware token in PDF backend settings.
Fig. 5: Signature present in hardware token visible on the PDF backend settings.

Thanks to the free software projects & developers who made this possible.

RIT Malayalam fonts are available & default in Fedora 36+, ELN

The upcoming Fedora release 36 (due end of April 2022) and beyond, and ELN (Enterprise Linux Next, what would become RHEL) will have default Malayalam script fonts as RIT Rachana and Meera New fonts. In addition, Sundar, TNJoy, Panmana and Ezhuthu fonts are now available in the official repositories. This brings Malayalam fonts that are modern (Unicode 13 compatible), well-maintained, having perfect complex-script shaping and good metadata to the users of Fedora, RHEL, CentOS & downstream OSen. I have made all the necessary updates in the upstream projects (which I maintain) and packaged them for Fedora (which also I maintain).

Update: thanks to Norbert Preining, all these fonts are also available for ArchLinux!

RIT Malayalam fonts available in Fedora.

RIT Rachana and Meera New fonts will be default serif and sans-serif fonts for Malayalam. smc-rachana-fonts and smc-meera-fonts are deprecated as they are unmaintained.

All the fonts can be installed from your favourite package managers (GNOME Software, Discover, dnf etc.).

RIT fonts in GNOME software of upcoming Fedora 36.

The packages can be installed using dnf via:

sudo dnf install -y rit-*-fonts

This change in Fedora required many well orchestrated steps:

  1. Packaging & building RIT fonts according to latest font packaging guidelines
  2. Set as default serif/sans-serif fonts for Malayalam in langpacks
  3. Set as default serif/sans-serif fonts for Malayalam in fedora-comps
  4. Propose the ChangeRequest which is then discussed & approved by Fedora Engineering Steering Committee (FESCO).

I would like to especially thank Parage Nemade for coordinating all the changes and relevant engineering procedures, and Pravin Satpute for initial discussions; in helping to complete these updates in time for Fedora 36.

Letsencrypt certificate renewal: Nginx with reverse-proxy

Let’s Encrypt revolutionized the SSL certificate management for websites in a short span of time — it directly improved the security of users of the world wide web by: (1) making it very simple to deploy SSL certificates to websites by administrators and (2) make the certificates available free of cost. To appreciate their efforts, compare to what hoops one had to jump through to obtain a certificate from a certificate authority (CA) and how much money and energy one would have to spend on it.

I make use of letsencrypt in all the servers I manitain(ed) and in the past used the certbot tool to obtain & renew certificates. Recent versions of certbot are only available as a snap package, which is not something I’d want to or able to setup in many cases.

Enter acme. It is shell script that works great. Installing acme will also setup a cron job, which would automatically renew the certificate for the domain(s) near its expiration. I have recently setup using nginx as a reverse proxy to a lexonomy service and acme for certificate management. The cron job is supposed to renew the certificate on time.

Except it didn’t. Few days ago received a notification from about imminent expiry of the certificate. I have searched the interweb quite a bit, but didn’t find a simple enough solution (“make the proxy service redirect the request”…). What follows is the troubleshooting and a solution, may be someone else find it useful.


acme was unable to renew the certificate, because the HTTP-01 authentication challenge requests were not answered by the proxy server where all traffic was being redirected to. In short: how to renew letsencrypt certificates on an nginx reverse-proxy server?

Certificate renewal attempt by acme would result in errors like:

# --cron --home "/root/" -w /var/www/html/
[Sat 08 May 2021 07:28:17 AM UTC] <strong>===Starting cron===</strong>
[Sat 08 May 2021 07:28:17 AM UTC] <strong>Renew: ''</strong>
[Sat 08 May 2021 07:28:18 AM UTC] Using CA:
[Sat 08 May 2021 07:28:18 AM UTC] Single domain=''
[Sat 08 May 2021 07:28:18 AM UTC] Getting domain auth token for each domain
[Sat 08 May 2021 07:28:20 AM UTC] Getting webroot for domain=''
[Sat 08 May 2021 07:28:21 AM UTC] Verifying:
[Sat 08 May 2021 07:28:24 AM UTC] <strong> error:Invalid response from https://<strong>my.domain</strong>.org/.well-known/acme-challenge/Iyx9vzzPWv8iRrl3OkXjQkXTsnWwN49N5aTyFbweJiA [NNN.NNN.NNN.NNN]:</strong>
[Sat 08 May 2021 07:28:24 AM UTC] <strong>Please add '--debug' or '--log' to check more details.</strong>
[Sat 08 May 2021 07:28:24 AM UTC] <strong>See:</strong>
[Sat 08 May 2021 07:28:25 AM UTC] <strong>Error renew <strong>my.domain</strong>.org.</strong>


The key error to notice is

Verify error:Invalid response from [NNN.NNN.NNN.NNN]

Sure enough, the resource .well-known/acme-challenge/… is not accessible. Let us try to make that accessible, without going through proxy server.


First, create the directory if it doesn’t exist. Assuming the web root as /var/www/html:

# mkdir -p /var/ww/html/.well-known/acme-challenge

Then, edit /etc/nginx/sites-enabled/ and before the proxy_pass directive, add the .well-known/acme-challenge/ location and point it to the correct location in web root. Do this on both HTTPS and HTTP server blocks (otherwise it didn’t work for me).

 6 server {
 7   listen 443 default_server ssl;
43   server_name;
44   location /.well-known/acme-challenge/ {
45     root /var/www/html/;
46   }
48   location / {
49     proxy_pass http://myproxyserver;
50     proxy_redirect off;
51   }
83 server {
84   listen 80;
85   listen [::]:80;
87   server_name;
89   location /.well-known/acme-challenge/ {
90     root /var/www/html/;
91   }
93   # Redirect to HTTPS
94   return 301 https://$server_name$request_uri;

Make sure the configuration is valid and reload the nginx configuration

nginx -t && systemctl reload nginx.service

Now, try to renew the certificate again:

# --cron --home "/root/" -w /var/www/html/
[Sat 08 May 2021 07:45:01 AM UTC] Your cert is in  /root/ 
[Sat 08 May 2021 07:45:01 AM UTC] Your cert key is in  /root/ 
[Sat 08 May 2021 07:45:01 AM UTC] v2 chain.
[Sat 08 May 2021 07:45:01 AM UTC] The intermediate CA cert is in  /root/ 
[Sat 08 May 2021 07:45:01 AM UTC] And the full chain certs is there:  /root/ 
[Sat 08 May 2021 07:45:02 AM UTC] _on_issue_success