Lab - Smartcards and the PTeID

In this guide you will develop Python programs using the Python PKCS11 module1 that interacts with PKCS#11 tokens to produce and validate digital signatures.

For that, you need to have installed the following software:

  • pcscd: sudo apt install pcscd

  • Python3 PIP: sudo apt install python3-pip

  • Swig: sudo apt instlal swig

  • PyKCS11: pip3 install --user pykcs11

  • PTEID Software: available at https://www.autenticacao.gov.pt/cc-aplicacao and pre-installed in the Virtual Machine Image.

  • (Optional) Opensc: sudo apt install opensc-pkcs11

You can verify the correct operation of the software by executing eidguiV2 and pkcs11-tool -L.

PKCS#11 Standard

The PKCS#11 standard (Cryptographic Token Interface Standard) is produced by RSA Security and defines native programming interfaces to cryptographic tokens, such as hardware cryptographic accelerators and smartcards, as is the case of Portuguese Citizen Card.

Python PyKCS11 includes a provider that, in contrast to most other providers, does not implement cryptographic algorithms itself. Instead, it exposes functions that are executed in the hardware tokens, and allows to obtain the objects stored in a smart card. When using the Python Cryptography module, this is the role of the Backends (providing access to hardware tokens). Unfortunately, no public (and stable) backend supports PKCS#11, making it required to use a standalone module (PyKCS#11).

Loading the card interface module

Interating with the Portuguese Citizen Card requires the use of a module that translated PKCS#11 calls into some internal format adequate to the smart card hardware. This is independent of the programming language used, and it is why we need to install the Portuguese Citizen Card software: besides a visualization tool, it also contains libraries that allow accessing the Portuguese Citizen Card (libpteidpkcs11.so)

Using the PyKCS11 module, the following snippet will try to list all slots, and present information about the tokens contained:

import PyKCS11
import binascii
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend as db
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
from cryptography.hazmat.primitives.hashes import SHA1, Hash

lib = '/usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so'

pkcs11 = PyKCS11.PyKCS11Lib()
pkcs11.load(lib)
slots = pkcs11.getSlotList(tokenPresent=True)

Task: List all tokens and print the detais of each token (revision, manufacturer, model, serial, etc...).

Contents of the Portuguese Citizen Card

After the token is available, it is possible to list the objects it contains. Some objects have public visibility and are accessed through a public session, while others are private, and the user must explicitelly create a private session. The different between sessions lies in the existence of omission of a pin code when opening the session.

You can list the contents of the Portuguese Citizen Card (i.e., its PKCS#11 objects), with code based on the following snippet:

...

all_attr = list(PyKCS11.CKA.keys())

#Filter attributes
all_attributes = [e for e in all_attr if isinstance(e, int)]

session = pkcs11.openSession(slot)
# session.login('1111') # DANGER!!! USE YOUR PINCODE!!

#### Search for objects and extract reference to private key and certificate

for obj in session.findObjects():
    attr = session.getAttributeValue(obj, all_attributes)

    attrDict = dict(list(zip(all_attributes, attr)))
    print("Type:", PyKCS11.CKO[attrDict[PyKCS11.CKA_CLASS]], "\tLabel:", attrDict[PyKCS11.CKA_LABEL])

The information you get in the CKA_LABEL attribute is the identification of the object included in the Portuguese Citizen Card. Through these identifiers we can select which certificate, or private key, we intend to use for each cryptographic operation. The attribute CKA_CLASS identifies the class of the object and the attribute CKA_CERTIFICATE_TYPE can identify the type of a certificate.

You can individually inspect all attributes of the objects. If you require access to the certificate content, convert the tuple contained in the CKA_VALUE to bytes (bytes(attributes['CKA_VALUE'])) and load it as a DER certificate (x509.load_der_x509_certificate).

cert_obj = session.findObjects([
                  (PyKCS11.CKA_CLASS, PyKCS11.CKO_CERTIFICATE),
                  (PyKCS11.CKA_LABEL, 'CITIZEN AUTHENTICATION CERTIFICATE')
                  ])[0]

cert_der_data = bytes(cert_obj.to_dict()['CKA_VALUE'])

Task: Print the label of all objects, as well as the issuer and subject of all certificates. Check with and without session.login().

Hint: Once loaded as a X509 certificate, you can reuse the code from the last class to print the issuer and subject, or even extract the public key.

Digital signature

Task: Create a program capable of generating a digital signature of a document, using the Portuguese Citizen Card, and storing it on a given file. Furthermore, complement it for writing on a file the public key certificate corresponding to the private key used for signing the document.

For this purpose consider the CITIZEN AUTHENTICATION CERTIFICATE and the corresponding CITIZEN AUTHENTICATION KEY (the private key), as many citizens to not have the CITIZEN SIGNATURE CERTIFICATE activated.

Because the private key is not extractable, you cannot load this key as a RSA key. Instead, use the session object to sign the text:

mechanism = PyKCS11.Mechanism(PyKCS11.CKM_SHA1_RSA_PKCS, None)

text = b'text to sign'

signature = bytes(session.sign(private_key, text, mechanism))

print("signature: ", binascii.hexlify(signature))

Signature validation

To validate the signature we will not use the card. In reality this would be done by the message/document recipient, which doesn’t have access to the signers’ card. The process involves extracting the public key from the certificate, compute the text digest, and then validate the signature.

If the signatures do not match, the verify method will throw an exception.

cert = x509.load_der_x509_certificate(cert_data, backend=db())

md = Hash(SHA1(), backend=db())
md.update(text)
digest = md.finalize()

public_key = cert.public_key()

public_key.verify(
    signature,
    digest,
    PKCS1v15(),
    SHA1()
)

Task: Create a program capable of checking a digital signature on a document. The program should use as inputs a file with the digital signature, a file with the signer’s public key certificate and a file with the signed contents. You should also display the identity of the entity that produced the signature (the subject). This is important as the user should know the identity of the user that created the signature.

Virtual Smartcard

In alternative to the real PT eID Smart Card you can use a Virtual Smart Card with custom generated certificates. It also uses a custom generated PKI with intermediate Certification Authorities. Please adjust your code accordingly.

This was not througly tested!!! Here be dragons!!! It works on my PC, so YMMV :)

In order to use this, download the VirtualSmartcard files with git clone. Several packages must be present, which varies according to the distribution. For linux ubuntu, the instructions are:

$ sudo apt update
$ sudo apt install  autoconf automake build-essential libpcsclite-dev python3-crypto python3-pip libtool help2man
$ pip3 install argparse crpyptography.io 

The next step is to compile the software. Some libraries may be required. If something is missing, the script should provide some feedback

$ git clone https://github.com/jpbarraca/vsmartcard.git
$ cd vsmartcard
$ cd virtualsmartcard
$ autoreconf --verbose --install
$ ./configure --sysconfdir=/etc
$ make
$ sudo make install

The library should be available at /usr/lib/pcsc/drivers/serial/libifdvpcd.so. Please check it. Then we need to stop pcscd and run it on a terminal as root.

$ systemctl stop pcscd
$ sudo pcscd -f -d

There should be a line displayed as follows, indicating that the libifdvpcd driver was loaded. This is mandatory.

00000047 [XX] readerfactory.c:1074:RFInitializeReader() Attempting startup of Virtual PCD 00 01 using /usr/lib/pcsc/drivers/serial/libifdvpcd.so
00000130 [XX] readerfactory.c:863:RFLoadReader() Reusing already loaded driver for /usr/lib/pcsc/drivers/serial/libifdvpcd.so

Then go to the pteid folder and generate a new Virtual PTEID Card. You can customize the script to use other subjects. If everything works, a file names card.json will be generated.

$ cd vsmartcard
$ cd pteid
$ python3 generate_pteid_card.py

Open another terminal and execute the virtual card.

$ cd virtualsmartcard/src/vpicc
$ cp ../../../pteid/card.json .
$ ./vicc -t PTEID -v

Finally, open another terminal and check if the card is available:

$ pkcs11-tool -L
Available slots:
Slot 0 (0x0): Virtual PCD 00 00
  token label        : CARTAO DE CIDADAO (Auth PIN)
  token manufacturer : GEMALTO
  token model        : PKCS#15 emulated
  token flags        : login required, token initialized, PIN initialized, readonly
  hardware version   : 0.0
  firmware version   : 0.0
  serial num         : 0000050839395659
  pin min/max        : 4/8
Slot 1 (0x1): Virtual PCD 00 00
  token label        : CARTAO DE CIDADAO (Sign PIN)
  token manufacturer : GEMALTO
  token model        : PKCS#15 emulated
  token flags        : login required, token initialized, PIN initialized, readonly
  hardware version   : 0.0
  firmware version   : 0.0
  serial num         : 0000050839395659
  pin min/max        : 4/8
Slot 2 (0x2): Virtual PCD 00 00
  token label        : CARTAO DE CIDADAO (Address PIN)
  token manufacturer : GEMALTO
  token model        : PKCS#15 emulated
  token flags        : login required, token initialized, PIN initialized, readonly
  hardware version   : 0.0
  firmware version   : 0.0
  serial num         : 0000050839395659
  pin min/max        : 4/8
Slot 3 (0x4): Virtual PCD 00 01
  (empty)

Then you can use it as a standard PT eID card, by any application. You need to keep the first two terminals open. The first is the pcscd daemon, which mediates access to cards, and the second is the Virtual Smart Card.

Only a subset of the methods is implemented, and it will crash if additional operations are requested. This is work in progress.

To test the card, use pkcs11-tool, opensc-explorer or the code you just developed.

Bibliography

Previous
Next