The SmartCard-HSM has always had support for Elliptic Curve Cryptography (ECC), however initial support in OpenSC was somewhat limited. With the latest 0.14 release of the popular open source crypto middleware, support for ECC is on-par with RSA support.

All cryptographic operations in the SmartCard-HSM are build using primitives from the certified crypto library embedded in the smart card’s microcontroller. On the JCOP platform the crypto library supports EC keys for regular curves on GF(p) with 192,224,256 or 320 bits for the prime p.

Domain parameter for the elliptic curve can be chosen arbitrarily during key generation. Domain parameter can be specified at the APDU level in the GENERATE ASYMMETRIC KEY PAIR command.

For typical applications we recommend to select one of the named curves defined by NIST, SECG or Brainpool.

The SmartCard-HSM driver in OpenSC supports the following named curves:

  • secp192r1 (aka prime192v1)
  • secp256r1 (aka prime256v1)
  • brainpoolP192r1
  • brainpoolP224r1
  • brainpoolP256r1
  • brainpoolP320r1
  • secp192k1
  • secp256k1 (the Bitcoin curve)

Only the first two curves are also supported by OpenSSL up to 1.0.2. Later versions include support for Brainpool curves.

Unfortunately, the popular Curve25519 is not yet supported by the cryptographic library, and thus can not be provided by the SmartCard-HSM.

Generating EC Key Pairs

An EC key can be generated using

asc@calzone:~/tmp/ecctest$ pkcs11-tool --module /usr/local/lib/opensc-pkcs11.so --login --pin 648219 --keypairgen --key-type EC:prime256v1 --id 12 --label defaultkey
Using slot 0 with a present token (0x0)
Key pair generated:
Private Key Object; EC
  label:      defaultkey
  ID:         12
  Usage:      decrypt, sign, unwrap
Public Key Object; EC  EC_POINT 256 bits
  EC_POINT:   0441044e68fd16a81555c2bf448d25b767572c398ac9706298c748d9bd3c88557bb161bef155491140fdc2541c5f032d73abcedd4b6540021b615c2467bbf9bf04c5b8
  EC_PARAMS:  06082a8648ce3d030107
  label:      defaultkey
  ID:         12
  Usage:      encrypt, verify, wrap

–module defines the PKCS#11 module to use in the pkcs11-tool command. The location of the library depends on your system. If unsure try find /usr -name "opensc-pkcs11.so".

–login request pkcs11-tool to perform C_Login before generating the keypair.

–pin provides the user PIN. If you leave out this option, then pkcs11-tool will prompt for the PIN. If you are using a card reader with PIN PAD, you will need to enter the PIN on the PIN PAD.

–keypairgen request the generation of a key pair, while parameters are defined with –key-type EC:prime256v1.

–id 12 defines the unique key identifier ‘12’. This identifier must match a certificate stored later for the same key.

–label defaultkey assigns the label “defaultkey” to the newly generated key.

You can read the public key with –read-object in the same command, however the public key is then saved in raw PKCS#11 format.

To save the public key in a format understood by OpenSSL use:

asc@calzone:~/tmp/ecctest$ pkcs15-tool --read-public-key 12
Using reader with a card: SCM SCR 3310 [CCID Interface] (21120843305113) 00 00
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETmj9FqgVVcK/RI0lt2dXLDmKyXBi
mMdI2b08iFV7sWG+8VVJEUD9wlQcXwMtc6vO3UtlQAIbYVwkZ7v5vwTFuA==
-----END PUBLIC KEY-----

or

asc@calzone:~/tmp/ecctest$ pkcs15-tool --read-public-key 12 | openssl ec -pubin -text -noout
read EC key
Using reader with a card: SCM SCR 3310 [CCID Interface] (21120843305113) 00 00
Private-Key: (256 bit)
pub: 
    04:4e:68:fd:16:a8:15:55:c2:bf:44:8d:25:b7:67:
    57:2c:39:8a:c9:70:62:98:c7:48:d9:bd:3c:88:55:
    7b:b1:61:be:f1:55:49:11:40:fd:c2:54:1c:5f:03:
    2d:73:ab:ce:dd:4b:65:40:02:1b:61:5c:24:67:bb:
    f9:bf:04:c5:b8
ASN1 OID: prime256v1

Signing with the EC Private Key

The newly generated key can be used to sign some data:

asc@calzone:~/tmp/ecctest$ echo "Hello World" >text.txt
asc@calzone:~/tmp/ecctest$ pkcs11-tool --module /usr/local/lib/opensc-pkcs11.so --login --pin 648219 --sign --id 12 --input text.txt --output text.txt.signature
Using slot 0 with a present token (0x0)
Using signature algorithm ECDSA
Writing OpenSSL ECDSA_SIG
asc@calzone:~/tmp/ecctest$ hexdump -C text.txt.signature 
00000000  30 43 02 1f 6a 01 b8 28  dc 8f 45 2a c1 89 45 4f  |0C..j..(..E*..EO|
00000010  dd a0 f7 e8 45 e3 db 2e  72 6f dd f1 c2 91 3e ca  |....E...ro....>.|
00000020  90 ce 93 02 20 68 9d 42  81 25 ce 7e c5 98 37 d7  |.... h.B.%.~..7.|
00000030  60 24 ea 9d 7d f9 f7 59  4c 52 28 85 df 9f 27 40  |`$..}..YLR(...'@|
00000040  58 44 0d a9 3c                                    |XD..<|
00000045

The signature is created using ECDSA with SHA-1. The signature is stored as an ASN.1 SEQUENCE containing 2 INTEGER r and s.

Encryption / Decryption with EC Key Pairs

Message encryption and decryption works by using Diffie-Hellman (ECDH) based key agreement. The sender uses his own private key and the recipients public key to derive a shared secret. The shared secret is used as symmetric key to encrypt the message. The recipient uses his private key and the public key of the sender to perform the same operation, deriving the shared secret to decrypt the message.

Unfortunately, OpenSC 0.14 has a bug that prevents using an EC key generated with pkcs11-tool for key derivation.

Key derivation works for keys generated with other tools, e.g. the issuecert.js script from the OpenSCDP Script Collection.

The –derive operation in pkcs11-tool requires the public key of the recipient in OpenSSL DER format. This must first be converted from the raw public key. In a real world scenario, the public key would be extracted from the certificate.

The public key is extracted, converted and the shared secret derived using

sc@calzone:~/tmp/ecctest$ pkcs15-tool --read-public-key 02 --out recipient-public-key.pem
Using reader with a card: SCM SCR 3310 [CCID Interface] (21120843305113) 00 00
asc@calzone:~/tmp/ecctest$ cat recipient-public-key
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESatwpqKpNUVV5yru/YYda8aLblJL
kZGxv46x8bbheVNGedmTmBSZVi1G0cuMyFetu/RYosxukTx02kaeO+i0CQ==
-----END PUBLIC KEY-----
asc@calzone:~/tmp/ecctest$ openssl ec -in recipient-public-key.pem -out recipient-public-key.der -outform DER -pubin -pubout
read EC key
writing EC key
asc@calzone:~/tmp/ecctest$ pkcs11-tool --module /usr/local/lib/opensc-pkcs11.so --login --pin 648219 --id 02 --derive --input-file recipient-public-key.der -m ECDH1-DERIVE --output-file shared-secret
Using slot 0 with a present token (0x0)
Using derive algorithm 0x00001050 ECDH1-DERIVE
asc@calzone:~/tmp/ecctest$ hexdump -C shared-secret 
00000000  57 29 af 70 15 6f ae 8e  77 10 df 81 5b 44 59 10  |W).p.o..w...[DY.|
00000010  b7 c0 84 7d c8 b4 77 50  d6 01 51 aa b8 d7 3f 83  |...}..wP..Q...?.|
00000020

A Note for Ubuntu Users

If you are using Ubuntu, then please make sure that you are compiling and installing OpenSC 0.14. Current Ubuntu versions (<= 14.04) include OpenSC 0.13 with some upstream patches. Versions of OpenSC before February 2014 do not persistently store EC public keys, so EC keys can only be used from a PKCS#11 aware application. See the bug report for details.