1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
Using with SmartCard-HSM (Nitrokey HSM)
=======================================
Support for the SmartCard-HSM and Nitrokey HSM is provided through the
`OpenSC <https://github.com/OpenSC/OpenSC/wiki/PKCS11-Module>`_ project.
The device is not a cryptographic accelerator. Only key generation and the
private key operations (sign and decrypt) are supported. Public key operations
should be done by extracting the public key and working on the computer.
The following mechanisms are available:
+------------------+-----------------------+-----------------------------------+
| Cipher | Capabilities | Variants |
+==================+=======================+===================================+
| RSA (v1.5/X.509) | Decrypt, Verify, Sign | MD5, SHA1, SHA256, SHA384, SHA512 |
+------------------+-----------------------+-----------------------------------+
| ECDSA | Sign | SHA1 |
+------------------+-----------------------+-----------------------------------+
| ECDH | Derive | Cofactor Derive |
+------------------+-----------------------+-----------------------------------+
Session lifetime objects are not supported and the value of
:attr:`pkcs11.constants.Attribute.TOKEN` and the `store` keyword argument
are ignored. All objects will be stored to the device.
The following named curves are supported:
* secp192r1 (aka prime192v1)
* secp256r1 (aka prime256v1)
* brainpoolP192r1
* brainpoolP224r1
* brainpoolP256r1
* brainpoolP320r1
* secp192k1
* secp256k1 (the Bitcoin curve)
More information is available `in the Nitrokey FAQ
<https://www.nitrokey.com/documentation/frequently-asked-questions#which-algorithms-and-maximum-key-length-are-supported>`_.
Getting Started
---------------
Initialize the device with `sc-hsm-tool`, e.g.
::
sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 --label "Nitrokey"
See `the documentation
<https://github.com/OpenSC/OpenSC/wiki/SmartCardHSM#initialize-the-device>`_
for more information on the parameters.
The OpenSC PKCS #11 module is `opensc-pkcs11.so`.
Generating Keys
---------------
RSA
~~~
::
import pkcs11
with token.open(user_pin='1234', rw=True) as session:
pub, priv = session.generate_keypair(pkcs11.KeyType.RSA, 2048,
store=True,
label="My RSA Keypair")
EC
~~
::
with token.open(user_pin='1234', rw=True) as session:
ecparams = session.create_domain_parameters(
pkcs11.KeyType.EC, {
pkcs11.Attribute.EC_PARAMS: pkcs11.util.ec.encode_named_curve_parameters('secp256r1'),
}, local=True)
pub, priv = ecparams.generate_keypair(store=True,
label="My EC Keypair")
Exporting Public Keys for External Use
--------------------------------------
While we don't want our private keys to leave the boundary of our HSM,
we can extract the public keys for use with a cryptographic library of our
choosing. :ref:`importing-keys` has more information on functions for
exporting keys.
RSA
~~~
`PyCrypto` example:
::
from pkcs11 import KeyType, ObjectClass, Mechanism
from pkcs11.util.rsa import encode_rsa_public_key
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
# Extract public key
key = session.get_key(key_type=KeyType.RSA,
object_class=ObjectClass.PUBLIC_KEY)
key = RSA.importKey(encode_rsa_public_key(key))
# Encryption on the local machine
cipher = PKCS1_v1_5.new(key)
crypttext = cipher.encrypt(b'Data to encrypt')
# Decryption in the HSM
priv = self.session.get_key(key_type=KeyType.RSA,
object_class=ObjectClass.PRIVATE_KEY)
plaintext = priv.decrypt(crypttext, mechanism=Mechanism.RSA_PKCS)
ECDSA
~~~~~
`oscrypto` example:
::
from pkcs11 import KeyType, ObjectClass, Mechanism
from pkcs11.util.ec import encode_ec_public_key, encode_ecdsa_signature
from oscrypto.asymmetric import load_public_key, ecdsa_verify
# Sign data in the HSM
priv = self.session.get_key(key_type=KeyType.EC,
object_class=ObjectClass.PRIVATE_KEY)
signature = priv.sign(b'Data to sign', mechanism=Mechanism.ECDSA_SHA1)
# Encode as ASN.1 for interchange
signature = encode_ecdsa_signature(signature)
# Extract the public key
pub = self.session.get_key(key_type=KeyType.EC,
object_class=ObjectClass.PUBLIC_KEY)
# Verify the signature on the local machine
key = load_public_key(encode_ec_public_key(pub))
ecdsa_verify(key, signature, b'Data to sign', 'sha1')
ECDH
~~~~
Smartcard-HSM can generate a shared key via ECDH key exchange.
.. warning::
Where possible, e.g. over networks, you should use ephemeral keys,
to allow for perfect forward secrecy. Smartcard HSM's ECDH is only useful
when need to repeatedly retrieve the same shared secret, e.g. encrypting
files in a hybrid cryptosystem.
`cryptography` example:
::
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import \
Encoding, PublicFormat, load_der_public_key
# Retrieve our keypair, with our public key encoded for interchange
alice_priv = self.session.get_key(key_type=KeyType.EC,
object_class=ObjectClass.PRIVATE_KEY)
alice_pub = self.session.get_key(key_type=KeyType.EC,
object_class=ObjectClass.PUBLIC_KEY)
alice_pub = encode_ec_public_key(alice_pub)
# Bob generates a keypair, with their public key encoded for
# interchange
bob_priv = ec.generate_private_key(ec.SECP256R1,
default_backend())
bob_pub = bob_priv.public_key().public_bytes(
Encoding.DER,
PublicFormat.SubjectPublicKeyInfo,
)
# Bob converts Alice's key to internal format and generates their
# shared key
bob_shared_key = bob_priv.exchange(
ec.ECDH(),
load_der_public_key(alice_pub, default_backend()),
)
key = alice_priv.derive_key(
KeyType.GENERIC_SECRET, 256,
mechanism_param=(
KDF.NULL, None,
# SmartcardHSM doesn't accept DER-encoded EC_POINTs for derivation
decode_ec_public_key(bob_pub, encode_ec_point=False)
[Attribute.EC_POINT],
),
)
alice_shared_key = key[Attribute.VALUE]
When decoding the other user's `EC_POINT` for passing into the key derivation
the standard says to pass a raw octet string (set `encode_ec_point` to False),
however some PKCS #11 implementations require a DER-encoded octet string
(i.e. the format of the :attr:`pkcs11.constants.Attribute.EC_POINT` attribute).
Encrypting Files
----------------
The device only supports asymmetric mechanisms. To do file encryption, you
will need to generate AES keys locally, which you can encrypt with your RSA
public key (this is how the Nitrokey storage key works); or by using ECDH
to generate a shared secret from a locally generated public key.
Debugging
---------
The parameter `OPENSC_DEBUG` will enable debugging of the OpenSC driver.
A higher number indicates more verbosity.
Thanks
------
Thanks to Nitrokey for their support of open software and
sending a Nitrokey HSM to test with `python-pkcs11`.
|