OpenSC / libp11

PKCS#11 wrapper library
GNU Lesser General Public License v2.1
311 stars 189 forks source link

PIN asked twice, and no way to pass it from outside #532

Open ale-rinaldi opened 6 months ago

ale-rinaldi commented 6 months ago

Hello,

I've read all the discussion in https://github.com/OpenSC/OpenSC/issues/2039 but this seems to be a different issue, since pin_cache_ignore_user_consent in opensc.conf has no effect here.

Background

In Italy, we use P7M files (PKCS#7 CAdES signed documents) a lot to communicate with the Public Administration. They're signed using certificates on smart cards provided from authorized manufacturers, who verify your identity before issuing one. I don't know if it's the same in the rest of the world, so I specified it.

For a project, I need to be able to sign documents with a bash script, without user interaction. Of course we're talking about an air-gapped and physically secure environment, where I feel confident to store my PIN on the system. I found OpenSSL to be able to generate P7M files natively since version 3.

My smart card

My smart card is from Aruba, one of the authorized providers. The card itself is from Oberthur or Incard, I'm not sure. However, Aruba provides a PKCS#11 module to interact with it, made by Bit4Id, that works correctly and shows the two certificates, public and private keys (with the OpenSC default module, I only see one of them, the one with id 434e5330, and it's exposed with id 01). The module is provided as a DEB file that sets up the library in /lib/bit4id/libbit4xpki.so.

This is the output from pkcs11-tool --module /lib/bit4id/libbit4xpki.so -O --login with some info redacted:

Using slot 0 with a present token (0x0)
Logging in to "CNS".
Please enter User PIN: 
Certificate Object; type = X.509 cert
  label:      CNS User Certificate
  subject:    DN: CN={redacted}, GN={redacted}, SN={redacted}, O=Progetto CNS Arubapec\/Unical, OU=Universita' della Calabria, C=IT
  ID:         434e5330
Data object 1002
  label:          'PDATA'
  application:    'PDATA'
  app_id:         2.0.68.65.84.65
  flags:           modifiable
Public Key Object; RSA 1024 bits
  label:      CNS User Public Key
  ID:         434e5330
  Usage:      encrypt, verify
  Access:     none
Private Key Object; RSA 
  label:      CNS User Private Key
  ID:         434e5330
  Usage:      decrypt, sign
  Access:     sensitive, always sensitive, never extractable
Certificate Object; type = X.509 cert
  label:      DS User Certificate3
  subject:    DN: C=IT, CN={redacted}, GN={redacted}, SN={redacted}
  ID:         445333
Private Key Object; RSA 
  label:      DS User Private Key 3
  ID:         445333
  Usage:      sign
  Access:     always authenticate, sensitive, always sensitive, never extractable, local
Public Key Object; RSA 2048 bits
  label:      DS User Public Key3
  ID:         445333
  Usage:      encrypt, verify
  Access:     none

I have 2 certificates, each one with its public and private keys. The first one, with ID 434e5330, is meant to be used for strong HTTPS client-side authentication to the Public Administration services. While it's technically valid also to digitally sign documents, its legal validity is limited. The second one, with ID 445333, is the digital signature certificate, fully legally valid. The digital signature certificate has the always authenticate attribute, and I think this is where the problem comes from. I don't think I have any control on those attributes, and even if I had, I'd rather not mess up with the smart card itself.

My setup

I compiled libp11 from the master branch, and I'm using it on OpenSSL 3.0.13 from Ubuntu 24.04 repositories, with the PKCS#11 module provided by the smart card manufacturer. This is my openssl.cnf file:

openssl_conf = openssl_conf_section

[openssl_conf_section]
engines = engines_section

[engines_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib/x86_64-linux-gnu/engines-3/libpkcs11.so
MODULE_PATH = /lib/bit4id/libbit4xpki.so

The problem

I extracted the certificate with ID 445333 using this command:

pkcs11-tool --module /lib/bit4id/libbit4xpki.so  -r -y cert -d 445333 -o ds.der --login

and I converted it into PEM with:

openssl x509 -inform der -in ds.der > ds.pem

When I try to digitally sign a file using openssl, I get asked for the PIN twice, one for the token and one for the key:

$ openssl cms -nosmimecap -md sha256 -nodetach -binary -cades -stream -outform DER -sign -signer ds.pem -inkey 'pkcs11:id=%44%53%33' -keyform engine -in test.pdf -out test.pdf.p7m -engine pkcs11
Engine "pkcs11" set.
Enter PKCS#11 token PIN for CNS: [I type my PIN]
Enter PKCS#11 key PIN for DS User Private Key 3: [I type my PIN again]

I found many ways to avoid the first PIN request:

However, none of these methods allowed me to skip the second PIN request:

$ openssl cms -nosmimecap -md sha256 -nodetach -binary -cades -stream -outform DER -sign -signer ds.pem -inkey 'pkcs11:id=%44%53%33;pin-value=12345' -keyform engine -in test.pdf -out test.pdf.p7m -engine pkcs11
Engine "pkcs11" set.
Enter PKCS#11 key PIN for DS User Private Key 3:

I don't have this issue if I sign documents with the other certificate, but that's not what I need to do.

I tried editing /etc/opensc/opensc.conf adding this:

framework pkcs15 {
    use_pin_caching = true;
    pin_cache_ignore_user_consent = true;
}

but nothing changed. I see this advice pretty much everywhere, but it's always related to OpenSC and never to libp11 so I think libp11 won't look at this setting, maybe?

I could probably use some hack with expect, but I feel that's really not the way to go.

How I solved it for now

I found this other engine: https://github.com/opensignature/pkcs11engine

It's archived and not mantained anymore, but using this I'm not having the issue: the PIN is always asked only once, and is not asked at all if passed as pin-value in the PKCS#11 URI. This is solving my issue as of now, but I'd really love to be able to do this with a supported, current software like libp11.

I fully understand the rationale behind 'if it's "Always authenticate", then you have to always authenticate' and it makes sense, but I think a tool like OpenSSL is meant to automate things, and having no way to do so seems like a strong limitation to me.