Skip to main content

KSeF Authentication library

Project description

Python KSeF Authentication Library (for PKCS#11 and local private keys)

[!NOTE]
PL: Biblioteka do języka Python obsługująca logowanie do KSeF z użyciem dowolnego klucza prywatnego obsługującego interfejs PKCS#11 – kwalifikowane podpisy i pieczęci elektroniczne (na karcie, tokenie USB lub w formie HSM), a także certyfikaty wydane przez KSeF, do których klucze prywatne przechowywane są na HSMie (np. YubiHSM, YubiKey, Google Cloud KMS). Obsługuje również klasyczne uwierzytelnianie kluczem przechowywanym lokalnie na dysku twardym w pliku .key (format PEM).

Supported features:

  • Authentication using private keys available through PKCS#11 interface:
    • Qualified signature or qualified seal issued on a physical device,
    • KSeF Certificate hosted on a HSM (e.g. YubiHSM, YubiKey, Google Cloud KMS).
  • Authentication using certificate and private key stored as PEM files on local hard disk.

Installation

Library available on PyPi: pyksef

pip3 install pyksef

CLI Usage

List available PKCS#11 tokens

Command:

p11_list_tokens \
    --pkcs11-dll "C:\Program Files\Krajowa Izba Rozliczeniowa S.A\Szafir 2.0\bin\CCGraphiteP11p.x64.dll"

Example output:

TokenRecord(slot=<Slot (slotID=2 flags=7)>, label='PKI Token 1 (Primary)', serial='31333132303030313233343536373839', manufacturer_id='CryptoTech P.S.A.', model='CCGraphitePro', hardware_version=(0, 0), firmware_version=(0, 0), flags=<TokenFlag.LOGIN_REQUIRED|USER_PIN_INITIALIZED|TOKEN_INITIALIZED: 1036>)
TokenRecord(slot=<Slot (slotID=3 flags=7)>, label='PKI Token 2 (QSCD)', serial='31333132303030313233343536373839', manufacturer_id='CryptoTech P.S.A.', model='CCGraphitePro', hardware_version=(0, 0), firmware_version=(0, 0), flags=<TokenFlag.WRITE_PROTECTED|LOGIN_REQUIRED|USER_PIN_INITIALIZED|TOKEN_INITIALIZED: 1038>)

List available private keys/certificates for PKCS#11 token

Command:

p11_list_objects \
    --pkcs11-dll "C:\Program Files\Krajowa Izba Rozliczeniowa S.A\Szafir 2.0\bin\CCGraphiteP11p.x64.dll" \
    --token-label "PKI Token 2 (QSCD)" \
    --token-serial "31333132303030313233343536373839"

Example output:

CertificateRecord(x509_cert=<Certificate(subject=<Name(C=PL,2.5.4.5=PNOPL-12345678900,CN=Jan Kowalski,2.5.4.42=Jan,2.5.4.4=Kowalski)>, ...)>)
PrivateKeyRecord(label='No Friendly Name Available', id='6572df736d642974a2bab6ddba753aefb89afcce', key_type=<KeyType.RSA>)

Fetch certificates stored on a PKCS#11 token

Command:

p11_list_objects \
    --pkcs11-dll "C:\Program Files\Krajowa Izba Rozliczeniowa S.A\Szafir 2.0\bin\CCGraphiteP11p.x64.dll" \
    --token-label "PKI Token 2 (QSCD)" \
    --token-serial "31333132303030313233343536373839" \
    --output certificates

Example output:

-----BEGIN CERTIFICATE-----
MIIHe...
-----END CERTIFICATE-----

Perform KSeF authentication using private key available through PKCS#11

Command:

ksef_auth_pkcs11 \
    --pkcs11-dll "C:\Program Files\Krajowa Izba Rozliczeniowa S.A\Szafir 2.0\bin\CCGraphiteP11p.x64.dll" \
    --token-label "PKI Token 2 (QSCD)" \
    --key-id 6572df736d642974a2bab6ddba753aefb89afcce \
    --context-id-type nip \
    --context-id 5421234567

Example output:

{"referenceNumber": "XXXXXXXX-XX-XXXXXXXXXX-XXXXXXXXXX-XX", "authenticationToken": {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "validUntil": "2026-02-04T15:20:15.6254824+00:00"}}

Perform KSeF authentication using certificate/private key file pair stored on disk

Command:

ksef_auth_file \
    --cert-file ksef.crt \
    --key-file ksf.key \
    --context-id-type nip \
    --context-id 5421234567

Example output:

{"referenceNumber": "XXXXXXXX-XX-XXXXXXXXXX-XXXXXXXXXX-XX", "authenticationToken": {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "validUntil": "2026-02-04T15:20:15.6254824+00:00"}}

Usage via Python

PKCS#11 List tokens available with certain provider

from pyksef.p11 import PKCS11Lib

# load PKCS#11 library for CryptoCard Graphite (note that any qualified signature/seal issuer is supported)
PKCS11_DLL_PATH = "C:\\Program Files\\Krajowa Izba Rozliczeniowa S.A\\Szafir 2.0\\bin\\CCGraphiteP11p.x64.dll"

lib = PKCS11Lib(PKCS11_DLL_PATH)
for token in lib.get_tokens():
    print(token)

Example output:

TokenRecord(slot=<Slot (slotID=2 flags=7)>, label='PKI Token 1 (Primary)', serial='31333132303030313233343536373839', manufacturer_id='CryptoTech P.S.A.', model='CCGraphitePro', hardware_version=(0, 0), firmware_version=(0, 0), flags=<TokenFlag.LOGIN_REQUIRED|USER_PIN_INITIALIZED|TOKEN_INITIALIZED: 1036>)
TokenRecord(slot=<Slot (slotID=3 flags=7)>, label='PKI Token 2 (QSCD)', serial='31333132303030313233343536373839', manufacturer_id='CryptoTech P.S.A.', model='CCGraphitePro', hardware_version=(0, 0), firmware_version=(0, 0), flags=<TokenFlag.WRITE_PROTECTED|LOGIN_REQUIRED|USER_PIN_INITIALIZED|TOKEN_INITIALIZED: 1038>)

PKCS#11 List private keys/certificates available with certain token

import getpass

from pyksef.p11 import PKCS11Lib

PKCS11_DLL_PATH = "C:\\Program Files\\Krajowa Izba Rozliczeniowa S.A\\Szafir 2.0\\bin\\CCGraphiteP11p.x64.dll"
TOKEN_LABEL = "PKI Token 2 (QSCD)"
USER_PIN = getpass.getpass("User PIN: ")

lib = PKCS11Lib(PKCS11_DLL_PATH)
lib.set_token(token_label=TOKEN_LABEL, user_pin=USER_PIN)

for certificate in lib.get_certificates():
    print(certificate)

for private_key in lib.get_private_keys():
    print(private_key)

Example output:

CertificateRecord(x509_cert=<Certificate(subject=<Name(C=PL,2.5.4.5=PNOPL-12345678900,CN=Jan Kowalski,2.5.4.42=Jan,2.5.4.4=Kowalski)>, ...)>)
PrivateKeyRecord(label='No Friendly Name Available', id='6572df736d642974a2bab6ddba753aefb89afcce', key_type=<KeyType.RSA>)

PKCS#11 Authentication

import binascii
import getpass
import json

from pyksef import ksef_auth_xades
from pyksef.auth.identifier import ContextIdentifier, ContextIdentifierType, SubjectIdentifierType
from pyksef.auth.state import ksef_poll_auth_finalized
from pyksef.p11 import create_p11_private_key, PKCS11Lib, get_leaf_certificate
from pyksef.x509 import load_pem_x509_certificate


# PKCS#11 Token parameters
PKCS11_DLL_PATH = "C:\\Program Files\\Krajowa Izba Rozliczeniowa S.A\\Szafir 2.0\\bin\\CCGraphiteP11p.x64.dll"
TOKEN_LABEL = "PKI Token 2 (QSCD)"
USER_PIN = getpass.getpass("User PIN: ")
PRIVATE_KEY_ID = "6572df736d642974a2bab6ddba753aefb89afcce"

# Authentication parameters
CONTEXT_ID = ContextIdentifier(type=ContextIdentifierType.nip, value="5421234567")
SUBJECT_ID_TYPE = SubjectIdentifierType.certificateSubject

# API URL
PROD_API_BASE_URL = "https://api.ksef.mf.gov.pl/v2"

# ---

lib = PKCS11Lib(PKCS11_DLL_PATH)
lib.set_token(token_label=TOKEN_LABEL, user_pin=USER_PIN)
lib.set_private_key(key_id=binascii.unhexlify(PRIVATE_KEY_ID))

# download the signer's certificate from the signer device directly
cert = get_leaf_certificate(o.x509_cert for o in lib.get_certificates())

# alternatively, you may just read the signer's certificate from file
# ---
# with open("ksef.crt", "rb") as f:
#     cert = load_pem_x509_certificate(cert_pem_bytes)
# ---

# perform KSeF authentication using PKCS#11 private key
auth_res = ksef_auth_xades(
  api_base_url=PROD_API_BASE_URL,
  cert=cert,
  key=create_p11_private_key(lib, cert),
  context_id=CONTEXT_ID,
  subject_id_type=SUBJECT_ID_TYPE,
)

# poll authentication state and redeem the actual token
auth_state = ksef_poll_auth_finalized(
    api_base_url=PROD_API_BASE_URL,
    reference_number=auth_res["referenceNumber"],
    authentication_token=auth_res["authenticationToken"]["token"]
)

print(json.dumps({
    "ksefAuthPKCS11Result": auth_res,
    "ksefPollAuthFinalizedResult": auth_state,
}, indent=4))

Example output:

{
    "ksefAuthPKCS11Result": {
        "referenceNumber": "XXXXXXXX-XX-XXXXXXXXXX-XXXXXXXXXX-XX",
        "authenticationToken": {
            "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "validUntil": "2026-02-09T16:08:59.2602376+00:00"
        }
    },
    "ksefPollAuthFinalizedResult": {
        "redeemResult": {
            "accessToken": {
                "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "validUntil": "2026-02-09T15:38:58.1201962+00:00"
            },
            "refreshToken": {
                "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "validUntil": "2026-02-16T15:23:58.1201962+00:00"
            }
        },
        "authState": {
            "startDate": "2026-02-09T15:23:58.1401992+00:00",
            "authenticationMethod": "QualifiedSignature",
            "status": {
                "code": 200,
                "description": "Uwierzytelnianie zako\u0144czone sukcesem"
            },
            "isTokenRedeemed": false
        }
    }
}

Authentication with private key on local disk

import getpass
import json

from pyksef import ksef_auth_xades
from pyksef.auth.identifier import ContextIdentifier, ContextIdentifierType, SubjectIdentifierType
from pyksef.auth.local_key import PEMPrivateKey
from pyksef.auth.state import ksef_poll_auth_finalized
from pyksef.x509 import load_pem_x509_certificate


# Certificate/key file parameters
PEM_CERT_FILENAME = "_private/ksef.crt"
PEM_KEY_FILENAME = "_private/ksef.key"
PEM_PASSPHRASE = getpass.getpass("PEM Passphrase: ")

# Authentication parameters
CONTEXT_ID = ContextIdentifier(type=ContextIdentifierType.nip, value="5421234567")
SUBJECT_ID_TYPE = SubjectIdentifierType.certificateSubject

# API URL
PROD_API_BASE_URL = "https://api.ksef.mf.gov.pl/v2"

# ---

# load X.509 certificate from file
with open(PEM_CERT_FILENAME, 'rb') as f:
    cert = load_pem_x509_certificate(f.read())

# load X.509 key from file
with open(PEM_KEY_FILENAME, 'rb') as f:
    key_pem = f.read()

# construct PEMPrivateKey object with file contents and passphrase to decrypt the key
key = PEMPrivateKey(key_pem, PEM_PASSPHRASE.encode("utf-8"))

# perform KSeF authentication with PEM certificate/key file
auth_res = ksef_auth_xades(
    api_base_url=PROD_API_BASE_URL,
    cert=cert,
    key=key,
    context_id=CONTEXT_ID,
    subject_id_type=SUBJECT_ID_TYPE,
)

# poll authentication state and redeem the actual token
auth_state = ksef_poll_auth_finalized(
    api_base_url=PROD_API_BASE_URL,
    reference_number=auth_res["referenceNumber"],
    authentication_token=auth_res["authenticationToken"]["token"]
)

print(json.dumps({
    "ksefAuthFileResult": auth_res,
    "ksefPollAuthFinalizedResult": auth_state,
}, indent=4))

Example output:

{
    "ksefAuthPKCS11Result": {
        "referenceNumber": "XXXXXXXX-XX-XXXXXXXXXX-XXXXXXXXXX-XX",
        "authenticationToken": {
            "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "validUntil": "2026-02-09T16:08:59.2602376+00:00"
        }
    },
    "ksefPollAuthFinalizedResult": {
        "redeemResult": {
            "accessToken": {
                "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "validUntil": "2026-02-09T15:38:58.1201962+00:00"
            },
            "refreshToken": {
                "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "validUntil": "2026-02-16T15:23:58.1201962+00:00"
            }
        },
        "authState": {
            "startDate": "2026-02-09T15:23:58.1401992+00:00",
            "authenticationMethod": "QualifiedSignature",
            "status": {
                "code": 200,
                "description": "Uwierzytelnianie zako\u0144czone sukcesem"
            },
            "isTokenRedeemed": false
        }
    }
}

Troubleshooting

If you see the following exception even though the DLL physically exists at the path indicated:

pkcs11.exceptions.PKCS11Error: OS exception while loading <file path>.dll: The specified module could not be found.

please check if your PATH environment variable is set correctly. Your PKCS#11 DLL might depend on some auxiliary DLLs that are unavailable.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyksef-0.3.0.tar.gz (16.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyksef-0.3.0-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file pyksef-0.3.0.tar.gz.

File metadata

  • Download URL: pyksef-0.3.0.tar.gz
  • Upload date:
  • Size: 16.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyksef-0.3.0.tar.gz
Algorithm Hash digest
SHA256 d2835d7ee746a9c5108c6c4a225754bd133f1e3a56b91f7c02348d1093826bd9
MD5 29e6596fa8efb217a4e0fe5e75f40463
BLAKE2b-256 2f1088f1a2fbf809b5a6d93cc822585cba0fcf1c19e38a26df7e07d8fc5ce1e8

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyksef-0.3.0.tar.gz:

Publisher: publish.yml on icedevml/pyksef

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyksef-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: pyksef-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyksef-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 11ac0357c45bf75ee1c2ca472548b521e01816c5fdfd9fc4c7258b6574e8e676
MD5 d1a30cc27e2c6b5c16c1279b5312e989
BLAKE2b-256 086185bc5bbf81d5e4d615d042a73282af8d49e7bd792c48a458b3a47823d602

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyksef-0.3.0-py3-none-any.whl:

Publisher: publish.yml on icedevml/pyksef

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page