Using Scute for digital signatures

What’s Scute?

Scute is a component of the GnuPG cryptographic software suite. It provides a PKCS#11 interface to other GnuPG’s components, especially Scdaemon, the component that manages cryptographic tokens.

Scute allows PKCS#11-compatible applications, such as Mozilla Firefox (and more generally any application using Mozilla’s NSS library), to use a X.509 certificate backed up by a private key stored on a OpenPGP smartcard or a similar token.

The primary use case of Scute is TLS client authentication: that is, to allow a user to authenticate to a website using a token-based certificate. However, it can also be used to digitally sign PDF or OpenDocument documents, which is what I’ll describe here.

Preliminary steps

Setting up Scute

If you’re using GNU/Linux, Scute may be available in your distribution’s package repository, typically under the (predictable) name scute. As of this writing, it is available at least in Debian and several Debian-based distributions, in ArchLinux, and Gentoo.

If you’re using Mac OS, Scute is also available in the MacPorts tree.

On other systems, you may need to compile Scute from source. Make sure you have build tools for your system, as well as the libgpg-error and libassuan libraries. On most Unix-like systems, building Scute should then be straightforward with the usual process for autoconf-iscated projects:

$ wget
$ tar xf scute-1.7.0.tar.bz2
$ cd scute-1.7.0
$ ./configure
$ make
# make install-strip

No matter how you installed Scute, take note of where the actual library ( on Unix-like systems, libscute.dylib on Mac OS) ends up being located, you will need that later. In the rest of this note, I will assume the library is at /usr/local/lib/

Beyond Scute itself, you will need a functional GnuPG system, including the GpgSM component (needed to manipulate X.509 certificates), the GnuPG Agent (needed to access private keys), and Scdaemon (needed to access cryptographic tokens). If you have installed Scute using a package manager, those components will likely have been pulled automatically as run-time dependencies of Scute.

Setting up a cryptographic token

Once you have the required software on your machine, then you need the cryptographic token that Scute will use.

You may use any token that is supported by GnuPG’s Scdaemon. It can be an actual OpenPGP smartcard, or any token that can behave as an OpenPGP smartcard, such as a FST-01/GnuK, a Yubikey, a Nitrokey, etc.

If you have any interest in Scute at all, it is quite likely that you are already familiar with cryptographic tokens and even that your OpenPGP private keys are already stored on such a token. :) You may skip that entire section if that’s the case.

Check whether GnuPG can access your token. Make sure your token is plugged in, then call gpg --card-status (if you’re using GnuPG ≥ 2.3, you may also use the new gpg-card tool):

$ gpg --card-status
Reader ...........: 234B:0000:FSIJ-1.2.19-6879436D:0
Application ID ...: D276000124010200FFFE6879436D0000
Application type .: OpenPGP

Then you need to generate a signing key (if you don’t already have one) and to load it on token.1 For now at least, it is recommended to stick to a RSA key, as other key types are less well supported by applications across the board.

In the rest of this note, we’ll use the following key:

$ gpg --edit-key alice
Secret key is available.

sec  rsa2048/54B4CC7749CAE7C3
     created: 2020-05-13  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa2048/45EDD81BCE62E9BD
     created: 2020-05-13  expires: never       usage: E
ssb  rsa2048/6D733C967F54A189
     created: 2022-09-22  expires: never       usage: S
[ultimate] (1). Alice <>

The signing subkey (6D733C967F54A189) is the one we’ll transfer to the token for use with Scute. Select it…

gpg> key 2

sec  rsa2048/54B4CC7749CAE7C3
     created: 2020-05-13  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa2048/45EDD81BCE62E9BD
     created: 2020-05-13  expires: never       usage: E
ssb* rsa2048/6D733C967F54A189
     created: 2022-09-22  expires: never       usage: S
[ultimate] (1). Alice <>

… and send it to the token:

gpg> keytocard
Signature key ....: [none]
Encryption key ...: [none]
Authentication key: [none]

Please select where to store the key:
  (1) Signature key
  (3) Authentication key
Your selection?

Store the key in the first slot (the signature slot).

sec  rsa2048/54B4CC7749CAE7C3
     created: 2020-05-13  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa2048/45EDD81BCE62E9BD
     created: 2020-05-13  expires: never       usage: E
ssb* rsa2048/6D733C967F54A189
     created: 2022-09-22  expires: never       usage: S
     card-no: FFFE 6879436D
[ultimate] (1). Alice <>

Note the card-no, which confirms that the private key is now stored on the token with serial number FFFE 6879436D.

Save the modifications to the keyring (otherwise, the signing private subkey, even after having been sent to the token, would still reside in the GnuPG Agent’s store, in $GNUPGHOME/private-keys-v1.d) and quit the key editor:

gpg> save

The token is now ready (at least for signing; of course, while you’re at it, you may want to also move your encryption subkey to it, but this note is only about signatures). You may want to check that it is working as expected, by unplugging it and then trying to sign anything with GnuPG: you should be prompted to insert the token back and to unlock it with your PIN.

Setting up a X.509 certificate

Now that we have a signing key on a cryptographic token, we need to obtain a X.509 certificate out of it. With the token plugged in, use GpgSM to generate a certificate signing request (CSR):

$ gpgsm --armor --output request.pem --gen-key

Please select what kind of key you want:
   (1) RSA
   (2) Existing key
   (3) Existing key from card
Your selection?

Choose (3) Existing key from card, then select the signing key you have just transferred to the token:

Serial number of the card: D276000124010200FFFE6879436D0000
Available keys:
   (1) 352393AF8BE1BE756418703681D0DC0D94DA6B1D OPENPGP.1
Your selection? 1

Then you need to fill in the details that will go into the certificate signing request, starting by what the certificate will be used for (this will be the value of the X509v3 Key Usage extension):

Possible actions for a RSA key:
   (1) sign, encrypt
   (2) sign
   (3) encrypt
Your selection? 2

We want a certificate that can be used to emit digital signatures, so choose (2) sign.

Fill in the other details as you see fit:

Enter the X.509 subject name: CN=Alice,O=Example Corp,DC=example,DC=net
Enter email addresses (end with an empty line):
Enter DNS names (optional; end with an empty line):
Enter URIs (optional; end with an empty line):
Create self-signed certificate? (y/N) N
Parameters to be used for the certificate request:
    Key-Type: card:OPENPGP.1
    Key-Length: 1024
    Key-Usage: sign
    Name-DN: CN=Alice,O=Example Corp,DC=example,DC=net

Really create request? (y/N) y
Now creating certificate request. This may take a while ...
gpgsm: about to sign CSR for key: &352393AF8BE1BE756418703681D0DC0D94DA6B1D
gpgsm: certificate request created
Ready.  You should now send this request to your CA.

The certificate signing request is now in request.pem. Now, as suggested by GpgSM, you need to get it signed by a certification authority (CA) to obtain the desired certificate.

It’s up to you to choose which certification authority to send your request to (unless maybe this decision is forced upon you, for example by your employer), and every CA may have its own procedure to deliver the certificate, so I won’t cover that step here.2 What matters is that at the end, you should have your signed certificate as well as the certificate(s) of the CA that signed it.

Import your new certificate and the CA’s certificate into the GpgSM keyring:

$ gpgsm --import ca-certificate.pem
$ gpgsm --import certificate.pem

Unplug your token and try using GpgSM to sign any file:

$ gpgsm --output test.signed --sign test.txt

You should be prompted to insert your token back in and to unlock it.

You are now ready to emit CMS signatures with your token-based X.509 certificate. Now let’s see how to do that with some common applications.

Signing with NSS-based applications

Many applications that can sign documents rely on Mozilla’s NSS library for the cryptographic operations. This makes things quite easy for us, because once you have configured one such application to use Scute, it’s basically done for all the other applications as well, as long as they use the same certificate database.

Here, before using any application, let’s first create a brand new NSS certificate database, that will be specifically intended for use with Scute:

$ mkdir ~/.local/share/scute-nss
$ certutil -N -d ~/.local/share/scute-nss
Enter a password which will be used to encrypt your keys.
The password should be at least 8 characters long,
and should contain at least one non-alphabetic character.

Enter new password:
Re-enter password:

Since the database will only be used with Scute, it will never actually contain any private key (our private signing key will remain on the cryptographic token), so it’s OK not to protect it with a password (by entering an empty one). You can choose to have a password, but then signing applications will have to ask for the password to open the database before they can even send any signing request to the token.

Import the certificate of your CA into the database. This is not strictly required for signing, but applications will also use that database to verify signatures, so having the CA certificate in there will ensure that your signatures can be verified and treated as valid.

$ certutil -A -i ca.pem -n TheCA -t ,T, -d ~/.local/share/scute-nss

Then add the Scute dynamic module to the database:3

$ modutile -add Scute -libfile /usr/local/lib/ -db ~/.local/share/scute-nss

WARNING: Performing this operation while the browser is running could cause
corruption of your security databases. If the browser is currently running,
you should exit browser before continuing this operation. Type
‘q <enter>’ to abort, or <enter> to continue:

Module "Scute" added to database.

Insert your token if it was not already plugged in and list the available certificates in the database:

$ certutil -d ~/.local/share/scute-nss -L -h Scute

Certificate Nickname                                         Trust Attributes

TheCA                                                        ,c,
FFFE 6879436D:DummyLabel                                     u,u,u

Observe that your token-based certificate is there (under a nickname made up from the serial number of your token). The u trust attribute indicates that the private key is available, as expected.

Signing with pdfsig

The pdfsig console program is provided with the Poppler PDF library. Use it to sign a PDF file as follows:

$ pdfsig -nssdir ~/.local/share/scute-nss -add-signature \
  -nick "FFFE 6879436D:DummyLabel" file.pdf file-signed.pdf

Signing with Okular

Okular is the document viewer of the KDE/Plasma desktop environment. To make it use the dedicated NSS database we have just created, go to Settings > Configure Backends, and in the PDF tab, set the “Custom Certificate Database” to the ~/.local/share/scute-nss directory, then restart the application.

Open a PDF file, and make sure your token is inserted.4 Then select Tools > Digitally Sign…. Okular creates visible signatures, so you need to manually draw a rectangle over your document where you want the signature to appear. You will be prompted to select the certificate to use (in our example there would be only one), then to enter your token’s PIN, and finally to choose where to save the signed document.

Signing with LibreOffice

LibreOffice can use Scute to sign OpenDocument files or PDF files. The procedure is the same in both cases.

As for Okular, you need to instruct LibreOffice to use the dedicated NSS certificate database we have created. In LibreOffice’s Options window, go to the LibreOffice > Security and click on the Certificate… button to bring the Certificate Path dialog, where you can select a custom NSS path. Set the path to our database, then restart the application.

Open a document to sign, make sure your token is plugged in, then go to File > Digital Signature > Digital Signatures…. The Digital Signatures dialog list the current signatures (none if the document has never been signed). Click on the Sign Document… button, select your signing certificate, then enter your token’s PIN when prompted.

When signing OpenDocument files (instead of PDF files), you can choose between an OpenPGP signature and a X.509 signature – the OpenDocument format supports both, where the PDF format only supports X.509. The choice between the two is made when you select your signing certificate. If you select the OpenPGP certificate, then you don’t need Scute at all, LibreOffice will know how to use GnuPG directly.

Signing with JSignPdf

JSignPdf is a Java application for signing PDF files. It doesn’t use NSS, but does support PKCS#11 modules all the same and therefore can use Scute – but it requires its own configuration.

After unpacking the JSignPdf archive, go to the conf directory and edit the file. Feel free to explore that file and tweak the parameters at your convenance, but the important thing to do if you want to use Scute is to make sure the following line is uncommented:


Then edit the pkcs11.cfg file so that it contains the following:


Then insert your token, and start the JSignPdf application using the launcher script in the top-level directory ( Select PKCS11 as the Keystore type, then click on the Load Keys button. JSignPdf will fetch the available signing certificates from the token and list them in the Key alias menu. Then select the PDF file to sign, and click on Sign it to proceed with the actual signing.

JSignPdf has a lot of options that you can explore (especially if you enable the Advanced view). It allows to create both unvisible and visible signatures, and for the latter, it allows to customize almost every aspect of the generated signature.

If you’re using Mac OS on a M1-powered Apple machine, you must make sure to use a Java virtual machine that is really targeting your architecture (arm64), instead of a x86_64 JVM that is running under emulation. That is because your libscute.dylib library, assuming you have installed it through MacPorts, will be a arm64 library — a x86_64 virtual machine would not be able to dynamically load such a library.

  1. It is also possible to generate the key directly on the token. I won’t cover that case here.
  2. Alternatively, you may choose to generate a self-signed certificate. To do that, just answer y when GpgSM asks you whether you want such a certificate. Then the file produced by GpgSM will be the certificate itself, ready to use.
  3. On MacOS, remember that the dynamic module is called libscute.dylib
  4. NSS cannot prompt you to insert a token, as GnuPG does; when the token is absent, it’s as if you didn’t have a signing certificate at all.

You can add a comment by replying to this message on the Fediverse.