Skip to content

Instantly share code, notes, and snippets.

@pyllyukko
Last active January 19, 2026 07:59
Show Gist options
  • Select an option

  • Save pyllyukko/a1ed6baa6b638b805a30b16960399e94 to your computer and use it in GitHub Desktop.

Select an option

Save pyllyukko/a1ed6baa6b638b805a30b16960399e94 to your computer and use it in GitHub Desktop.
FINEID notes

FINEID

Card

pcsc_scan (from pcsc-tools) output:

3B 7F 96 00 00 80 31 B8 65 B0 85 04 02 1B 12 00 F6 82 90 00
	Finnish ID-card v5.0(?) (eID)
	https://dvv.fi/en/fineid-specifications

S/MIME with Mutt, gnupg-pkcs11, GPGME & mpollux

The communication flow is as follows: mutt -> gpgme -> (gpgsm) -> gpg-agent -> gnupg-pkcs11-scd -> libcryptoki -> smartcard

Prerequisites

Configuration

~/.gnupg/gpg-agent.conf:

scdaemon-program /usr/bin/gnupg-pkcs11-scd

~/.gnupg/gnupg-pkcs11-scd.conf:

providers p1
provider-p1-library /usr/lib64/libcryptoki.so

muttrc:

set crypt_use_gpgme
set smime_default_key="12345678.0" <- check your ID with the commands described below in "Usage"
set nohonor_disposition

Usage

gpgsm --learn-card

Run gpgsm --list-keys and check the ID of your cert with key usage: digitalSignature keyEncipherment dataEncipherment.

You can test whether your card is identified by running gpg-agent --server gpg-connect-agent and issuing the SCD LEARN command.

S/MIME with Mutt, OpenSSL & mpollux

Prerequisites

Determine your private key ID with pkcs11-tool --module /usr/lib64/libcryptoki.so --list-objects --type privkey. It should look like this:

Private Key Object; RSA 
  label:      todentamis- ja salausavain
  ID:         45
  Usage:      decrypt, sign

Determine the IDs of your "todentamis- ja salausvarmenne" certificate and the intermediate CA "VRK Gov. CA for Citizen Certificates - G3" certificate with the following command:

pkcs11-tool --module /usr/lib64/libcryptoki.so --list-objects --type cert

Extract the "todentamis- ja salausvarmenne" and "VRK Gov. CA for Citizen Certificates - G3" certificates from the card into separate files with (repeat and save into different files):

pkcs11-tool --module /usr/lib64/libcryptoki.so --read-object --id KEY_ID --type cert --output-file cert.der

Convert them from DER to PEM (repeat for both certificates):

openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM

Configuration

OpenSSL

Create the following OpenSSL configuration (see Using the engine from the command line):

openssl_conf = openssl_init

[openssl_init]
engines = engine_section

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib64/engines-1.1/pkcs11.so
MODULE_PATH = /usr/lib64/libcryptoki.so
init = 0

Mutt

  • You need to disable GPGME in Mutt, so you can use the old school smime_*_commands
  • cert.pem is the extracted "todentamis- ja salausvarmenne" certificate
  • ca.pem is the "VRK Gov. CA for Citizen Certificates - G3" intermediate CA certificate
  • The key ID is hardcoded and determined in the "Determine your private key ID" part
  • smime_default_key and smime_sign_as are just dummy values so Mutt doesn't prompt you for the key ID

Add the following to your muttrc:

unset crypt_use_gpgme
set smime_default_key="12345678.0"
set nohonor_disposition
set smime_decrypt_command="OPENSSL_CONF=/path/to/openssl.conf openssl smime -decrypt -engine pkcs11 -keyform engine -inform DER -in %f -inkey 45"
set smime_sign_as="87654321.0"
set smime_sign_command="OPENSSL_CONF=/path/to/openssl.conf openssl smime -sign -engine pkcs11 -keyform engine -signer /path/to/cert.pem -certfile /path/to/ca.pem -inkey 45 -in %f -outform DER"

Usage

Mutt will ask you for a passphrase, but as this comes from mpollux you can enter anything to the prompt.

SSH

If you have #1608 patch, you can get your SSH public key with pkcs15-tool --read-ssh-key ID where ID is the ID of your "todentamis- ja salausvarmenne" certificate. You also need to have the following in /etc/opensc.conf or otherwise pkcs15-tool will not work:

app default {
	card_atr 3b:7f:96:00:00:80:31:b8:65:b0:85:04:02:1b:12:00:f6:82:90:00 {
		driver = fineid;
	}
}

Do note that the patch does not seem to work properly with FINEID v5, except for the pkcs15-tool part so leaving it to your opensc.conf is not recommended.

Other option to convert your certificate into SSH authorized_key is to run openssl x509 -in cert.pem -noout -pubkey | ssh-keygen -i -m PKCS8 -f /dev/stdin.

You need to have the following in your SSH client configuration (ssh_config):

Host ...
	PKCS11Provider /usr/lib64/libcryptoki.so

Then just add the public key into your authorized_key and you can authenticate with your FINEID card.

mPollux notes

mPollux stores "DigiSign WebSigner ROOT Certificate" into your ~/.pki/nssdb database. It uses a password, which you can find from ~/.digisign/Seed.txt.

See DigiSignApplication.bin:

nth   paddr      vaddr      len  size section type    string
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
2856  0x0043b520 0x0043b520 19   20   .rodata ascii   /.digisign/Seed.txt
52823 0x006d4041 0x006d4041 2288 2289 .rodata ascii   #!/bin/bash\n\npwdfile=$1\nnsdCreate=$2\n\nif [ -f /usr/lib/libcryptoki.so ]; then\n   cryptokiPath=/usr/lib/libcryptoki.so;\nelse\n   cryptokiPath=/usr/lib64/libcryptoki.so;\nfi\n\nif [ "$nsdCreate" = "true" ]; then\n   certutil -d sql:$HOME/.pki/nssdb -N -f $pwdfile\nfi\n\nif [ $? -gt 0 ]; then\n   echo nssdb creation failed\n   exit 1\nfi\n\nhostname=$(hostname)\nif [ -z "$hostname" ]; then\n   echo hostname not defined\n   exit 1\nfi\n\nrootname="DigiSign WebSigner ROOT Certificate for $hostname"\n\n#Wrong password exit status: \n#   local nssd: 2\n#   firefox and thunderbird: 3\n\n#add root certificate to nssd db sent by parameters\naddRootCertificate()\n{\n   dbPath=$1\n   isPwdFile=$2\n   exitStatus=$3\n\n   certCheck=$(certutil -d $dbPath -L | grep "$rootname")\n   if [ -n "$certCheck" ]; then\n      #remove old certificate, ignore if it doesn't exist\n      certutil -d $dbPath -D -n "$rootname"\n   fi\n\n   if [ "$isPwdFile" = "true" ]; then\n      certutil -d  $dbPath -A -t "TC,Tw,Tw" -n "$rootname" -i /etc/xdg/Fujitsu/SSLCA.cer -f $pwdfile\n   else\n      certutil -d $dbPath -A -t "TC,Tw,Tw" -n "$rootname" -i /etc/xdg/Fujitsu/SSLCA.cer\n   fi\n   if [ $? -gt 0 ]; then\n       #if certificate inset failed for some reason, certutil added incorrect data to db. Clean incorrect data from db.\n       certutil -d $dbPath -D -n "$rootname"\n       exit $exitStatus\n   fi\n   #update pkcs11.txt\n   isInFile=$(cat $dbPath/pkcs11.txt | grep -c "DigiSign PKCS#11 Module")\n\n   if [ $isInFile -eq 0 ]; then\n      echo "library=$cryptokiPath" >> $dbPath/pkcs11.txt\n\t  echo "name=DigiSign PKCS#11 Module" >> $dbPath/pkcs11.txt\n   fi\n}\n\naddRootCertificate $HOME/.pki/nssdb true 2\n\nif [ -d ~/snap/firefox ]; then\necho snap version\n   for certDB in $(find ~/snap/firefox/common/ -name "cert9.db")\n   do\n      certDir=$(dirname ${certDB});\n\t  addRootCertificate $certDir false 3\n   done\nelse\n   for certDB in $(find  ~/.mozilla* ~/.thunderbird -name "cert9.db")\n   do\n      certDir=$(dirname ${certDB});\n\t  addRootCertificate $certDir false 3\n   done\n   #we need also check cet8.db for old version of Firefox or thunderbird. More related to Ubuntu 32 version\n   for certDB in $(find  ~/.mozilla* ~/.thunderbird -name "cert8.db")\n   do\n      certDir=$(dirname ${certDB});\n\t  addRootCertificate $certDir false 3\n   done\nfi\n\nexit 0\n

Atostek ID notes

Atostek ID is the new software that is going to replace mPollux. All these notes are about the Debian package.

Version 4.4.1.0

https://files.fineid.fi/download/atostek/4.4.1.0/linux/AtostekID_DEB_4.4.1.0.deb

6fec3d89bf2ff95a2f12acd2d28563b9a4ce416d82de52e882401c4bce4ca647  AtostekID_DEB_4.4.1.0.deb

Mysterious private key

A mysterious private key is embedded within the binary:

nth   paddr      vaddr      len  size section type    string
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
2197  0x005735a1 0x005735a1 3500 3501 .rodata ascii   -----BEGIN ENCRYPTED PRIVATE KEY-----\r\n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\r\n-----END ENCRYPTED PRIVATE KEY-----\r\n

Couldn't find any code referencing it and apparently the installCA function does generate it's own keys.

installCA function

You can find a reference string:

nth   paddr      vaddr      len  size section type    string
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
471   0x0056521c 0x0056521c 27   28   .rodata ascii   Entering installCA function

From there you can find the function:

address            noret size  nbbs edges    cc cost          min bound range max bound          calls locals args xref frame name
================== ===== ===== ===== ===== ===== ==== ================== ===== ================== ===== ====== ==== ==== ===== ====
0x000000000013c180     0 7025   256   418   164 2394 0x000000000013c180  7279 0x000000000013ddef   288   31      1    1   520 fcn.0013c180

PKCS #12 password

I dislike the idea of having random private keys stored on my computer and not knowing how to unlock them.

When you run the binary with the -installCA parameter, it creates this AID-ERASMARTCARD-EHOITO-FI-CA CA certificate for you into ~/.local/share/Atostek Oy/Atostek ID/ directory. The password for the PKCS #12 file is DyCpu4fB4y4WdsnCG6k3. The password is the same between different machines (e.g. not using any instance specific attributes to generate a unique one). You can breakpoint PKCS12_create to catch it.

You can see the password loaded into the r12 register before calling the function at 0x0013e430:

            0x0013ca7a      4c8b6590       mov r12, qword [rbp - 0x70]

The -installSCSCA parameter installs another cert (AID_SCSCA) and the password for that is Le3svzKWE2OD4cgWzXan.

References

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