HSMKey
HSM-backed cryptographic keys compatible with Python's cryptography and jwcrypto library.
Documentation: https://hsmkey.readthedocs.io
Overview
HSMKey provides key implementations that perform all cryptographic operations on a Hardware Security Module (HSM) via PKCS#11. Private keys never leave the HSM - signing, decryption, and other private key operations are executed on the hardware.
Features
- Drop-in replacement - Keys implement
cryptographylibrary interfaces - Secure by design - Private keys cannot be exported from HSM
- Full algorithm support:
- RSA (2048, 3072, 4096 bits) - PKCS#1 v1.5 and PSS signing, OAEP decryption
- ECDSA (P-256, P-384, P-521)
- EdDSA (Ed25519, Ed448)
- Thread-safe session management - Connection pooling with proper cleanup
Installation
python3 -m pip install hsmkey
Or with uv:
uv add hsmkey
Quick Start
from hsmkey import SessionPool, PKCS11RSAPrivateKey
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
# Create session pool
pool = SessionPool(
module_path="/usr/lib/softhsm/libsofthsm2.so",
token_label="my-token",
user_pin="123456",
)
# Use session to access keys
with pool.session() as session:
# Load RSA key by label
key = PKCS11RSAPrivateKey(session, key_label="my-rsa-key")
# Sign data (signing happens on HSM)
signature = key.sign(
b"data to sign",
padding.PKCS1v15(),
hashes.SHA256(),
)
# Get public key for verification
public_key = key.public_key()
public_key.verify(signature, b"data to sign", padding.PKCS1v15(), hashes.SHA256())
Supported Key Types
RSA Keys
from hsmkey import PKCS11RSAPrivateKey
from cryptography.hazmat.primitives.asymmetric import padding
with pool.session() as session:
key = PKCS11RSAPrivateKey(session, key_label="rsa-2048")
# PKCS#1 v1.5 signing (RS256, RS384, RS512)
sig = key.sign(data, padding.PKCS1v15(), hashes.SHA256())
# PSS signing (PS256, PS384, PS512)
pss = padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.AUTO)
sig = key.sign(data, pss, hashes.SHA256())
# OAEP decryption
oaep = padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
plaintext = key.decrypt(ciphertext, oaep)
ECDSA Keys
from hsmkey import PKCS11EllipticCurvePrivateKey
from cryptography.hazmat.primitives.asymmetric import ec
with pool.session() as session:
key = PKCS11EllipticCurvePrivateKey(session, key_label="ec-p256")
# Sign with ECDSA (ES256, ES384, ES512)
signature = key.sign(data, ec.ECDSA(hashes.SHA256()))
# Verify
key.public_key().verify(signature, data, ec.ECDSA(hashes.SHA256()))
EdDSA Keys
from hsmkey import PKCS11Ed25519PrivateKey, PKCS11Ed448PrivateKey
with pool.session() as session:
# Ed25519
key = PKCS11Ed25519PrivateKey(session, key_label="ed25519")
signature = key.sign(data) # 64-byte signature
# Ed448
key = PKCS11Ed448PrivateKey(session, key_label="ed448")
signature = key.sign(data) # 114-byte signature
Key Lookup
Keys can be found by ID or label:
# By label
key = PKCS11RSAPrivateKey(session, key_label="my-key")
# By ID
key = PKCS11RSAPrivateKey(session, key_id=bytes([0x01]))
# By both (must match)
key = PKCS11RSAPrivateKey(session, key_id=bytes([0x01]), key_label="my-key")
Security
Private key material is protected:
key = PKCS11RSAPrivateKey(session, key_label="my-key")
# These operations raise HSMUnsupportedError:
key.private_numbers() # Cannot extract private numbers
key.private_bytes(...) # Cannot export private key
Public keys can be extracted and serialized:
public_key = key.public_key()
pem = public_key.public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo,
)
Development
Prerequisites
- SoftHSM2 installed (for testing).
- kryoptic built in
~/kryoptic(for testing). - OpenSSL for key generation
- just command runner
Setup
# Clone and install
git clone https://github.com/kushaldas/hsmkey
cd hsmkey
uv sync --all-extras
# Generate test keys and set up HSM
just setup
Running Tests
# Run all tests
just test
# Run with coverage
just test-cov
Available Commands
just # List all commands
just recreate-keys # Generate test keys on disk
just init-hsm # Initialize SoftHSM2 token
just import-keys # Import keys to HSM
just setup # Full setup
just test # Run tests
just list-keys # List keys in HSM
just reset # Reset everything
Documentation
Full documentation is available at https://hsmkey.readthedocs.io, including:
- Installation Guide
- Quick Start
- Key Classes Reference
- JWCrypto Integration
- Session Management
- API Reference
License
BSD-2-Clause
Metadata
Release files for hsmkey 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| hsmkey-0.2.0.tar.gz | 130.9 kB | Without provenance | Details for hsmkey-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| hsmkey-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for hsmkey-0.2.0-py3-none-any.whl |
Total release size: 157.1 kB
Release files / hsmkey-0.2.0.tar.gz
| Download URL | hsmkey-0.2.0.tar.gz |
|---|---|
| Size | 130.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
30de3861727a189aaa80ae56541bc7fbd167d6d976a5f86ed40c890425fbc02c
|
|
BLAKE2b-256 checksum How to use checksums |
3722ecd87cda7594c27a6709b9fe74c91d107ee525c4d7e8dbdf59db171e4426
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|
Release files / hsmkey-0.2.0-py3-none-any.whl
| Download URL | hsmkey-0.2.0-py3-none-any.whl |
|---|---|
| Size | 26.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1b320b246e7619424005afb8a437f70fc9e4665ec854e2492e442f43ec883c62
|
|
BLAKE2b-256 checksum How to use checksums |
b5825f38c6b5071ff19dbb03e91c5bcdc26285f4c5066b021ff144387d9f22f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|
Security for hsmkey 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.