Skip to main content

A Pythonic implementation of Json Web Signature, Keys, Algorithms, Tokens and Encryption (RFC7514 to 7519), on top of the `cryptography` module.

Project description

JwSkate

Release Status CI Status Documentation Status

A Pythonic implementation of Json Web Signature, Keys, Algorithms, Tokens and Encryption (RFC7514 to 7519), and their extensions ECDH Signatures (RFC8037), and JWK Thumbprints (RFC7638).

A quick usage example, generating an RSA private key, signing some data, then validating that signature:

from jwskate import Jwk

# generate a RSA Jwk and sign a plaintext with it
rsa_private_jwk = Jwk.generate_for_kty("RSA", key_size=2048, kid="my_key")

data = b"Signing is easy!"
alg = "RS256"
signature = rsa_private_jwk.sign(data, alg)

# extract the public key, and verify the signature with it
rsa_public_jwk = rsa_private_jwk.public_jwk()
assert rsa_public_jwk.verify(data, signature, alg)

# let's see what a Jwk looks like:
assert isinstance(rsa_private_jwk, dict)  # Jwk are dict

print(rsa_private_jwk)

The result of this print JWK will look like this:

{ 'kty': 'RSA',
  'n': '...',
  'e': 'AQAB',
  'd': '...',
  'p': '...',
  'q': '...',
  'dp': '...',
  'dq': '...',
  'qi': '...',
  'kid': 'my_key'
}

Now let's sign a JWT containing arbitrary claims:

from jwskate import Jwk, Jwt

private_jwk = Jwk.generate_for_kty("EC", kid="my_key")
claims = {"sub": "some_sub", "claim1": "value1"}
sign_alg = "ES256"

jwt = Jwt.sign(claims, private_jwk, sign_alg)
# that's it! we have a signed JWT
assert jwt.claims == claims  # claims can be accessed as a dict
assert jwt.sub == "some_sub"  # or individual claims can be accessed as attributes
assert jwt["claim1"] == "value1"  # or as dict items
assert jwt.alg == sign_alg  # alg and kid headers are also accessible as attributes
assert jwt.kid == private_jwk.kid
assert jwt.verify_signature(private_jwk.public_jwk(), sign_alg)

print(jwt)

This will output the full JWT compact representation. You can inspect it for example at https://jwt.io

eyJhbGciOiJFUzI1NiIsImtpZCI6Im15a2V5In0.eyJzdWIiOiJzb21lX3N1YiIsImNsYWltMSI6InZhbHVlMSJ9.C1KcDyDT8qXwUqcWzPKkQD7f6xai-gCgaRFMdKPe80Vk7XeYNa8ovuLwvdXgGW4ZZ_lL73QIyncY7tHGXUthag

Or let's sign a JWT with the standardised lifetime, subject, audience and ID claims:

from jwskate import Jwk, JwtSigner

private_jwk = Jwk.generate_for_kty("EC")
signer = JwtSigner(issuer="https://myissuer.com", jwk=private_jwk, alg="ES256")
jwt = signer.sign(
    subject="some_sub",
    audience="some_aud",
    extra_claims={"custom_claim1": "value1", "custom_claim2": "value2"},
)

print(jwt.claims)

The generated JWT claims will include the standardised claims:

{'custom_claim1': 'value1',
 'custom_claim2': 'value2',
 'iss': 'https://myissuer.com',
 'aud': 'some_aud',
 'sub': 'some_sub',
 'iat': 1648823184,
 'exp': 1648823244,
 'jti': '3b400e27-c111-4013-84e0-714acd76bf3a'
}

Features

  • Simple, Clean, Pythonic interface
  • Convenience wrappers around cryptography for all algorithms described in JWA
  • Json Web Keys (JWK) loading and generation
  • Arbitrary data signature and verification using Json Web Keys
  • Json Web Signatures (JWS) signing and verification
  • Json Web Encryption (JWE) encryption and decryption
  • Json Web Tokens (JWT) signing, verification and validation
  • 100% type annotated
  • nearly 100% code coverage
  • Relies on cryptography for all cryptographic operations
  • Relies on BinaPy for binary data manipulations

Why a new lib ?

There are already multiple implementations of JOSE and Json Web Crypto related specifications in Python. However, I have been dissatisfied by all of them so far, so I decided to come up with my own module.

  • PyJWT: lacks support for JWK, JWE, JWS, requires keys in PEM format.
  • JWCrypto: very inconsistent and complex API.
  • Python-JOSE: lacks easy support for JWT validation (checking the standard claims like iss, exp, etc.), lacks easy access to claims

Design

JWK are dicts

JWK are specified as JSON objects, which are parsed as dict in Python. The Jwk class in jwskate is actually a dict subclass, so you can use it exactly like you would use a dict: you can access its members, dump it back as JSON, etc. The same is true for Json Web tokens in JSON format.

JWA Wrappers

While you can directly use cryptography to do the cryptographic operations that are described in JWA, its usage is not straightforward and gives you plenty of options to carefully select, leaving room for errors. To work around this, jwskate comes with a set of wrappers that implement the exact JWA specification, with minimum risk of mistakes.

Safe Signature Verification

For every signature verification method in jwskate, you have to provide the expected signature(s) algorithm(s). That is to avoid a security flaw where your application accepts tokens with a weaker encryption scheme than what your security policy mandates; or even worse, where it accepts unsigned tokens, or tokens that are symmetrically signed with an improperly used public key, leaving your application exposed to exploitation by attackers.

Each signature verification accepts 2 args alg and algs. If you always expect to verify tokens signed with a single signature algorithm, pass that algorithm ID to alg. If you accept multiple algs (for example, any asymmetric alg that you consider strong enough), you can instead pass an iterable of allowed algorithms with algs. The signature will be validated as long as it is signed with one of the provided algs.

For verification methods that accept a Jwk key, you don't have to provide an alg or algs if that Jwk has the appropriate alg member that define which algorithm is supposed to be used with that key.

Credits

All cryptographic operations are handled by cryptography.

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

jwskate-0.1.0.tar.gz (76.6 kB view details)

Uploaded Source

Built Distribution

jwskate-0.1.0-py3-none-any.whl (63.3 kB view details)

Uploaded Python 3

File details

Details for the file jwskate-0.1.0.tar.gz.

File metadata

  • Download URL: jwskate-0.1.0.tar.gz
  • Upload date:
  • Size: 76.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.0 CPython/3.9.12

File hashes

Hashes for jwskate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a865c4e85d3505dec49f79b5d396cb19858d99b3fb367b43bfc70c30b2baef7f
MD5 fc89f79a2bd81d9965dfc5c826303d01
BLAKE2b-256 4e8d7b46dddd556fb4eccfedab2af662927878d77668ad65771b431b4c65da5f

See more details on using hashes here.

File details

Details for the file jwskate-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: jwskate-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 63.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.0 CPython/3.9.12

File hashes

Hashes for jwskate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f92042f2de911c1dfe143169adba00abe2eec898fc8dd8e103a9c6b663f32f70
MD5 0ec16c4b5ac9cbcaeddf3fe71ba5648b
BLAKE2b-256 2e7b137b8637dc9135ba6a94128380b08282cca01baaae7e2335ac91e9d40198

See more details on using hashes here.

Supported by

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