Skip to main content

Test Coverage Versions

JWS Algorithms

A simple library for signing and verifying messages using common JWS (JSON Web Signature) algorithms without the overhead of a full JWT/JWS library or a lot of setup and reading documentation when you do not need JWT/JWS.

This library provides two enums:

  • SymmetricAlgorithm: for symmetric algorithms like HMAC, where the same secret is used for signing and verifying.
  • AsymmetricAlgorithm: for asymmetric algorithms like RSA and ECDSA, where a private key is used for signing and a public key is used for verification.

Select the algorithm you want to use from the enum, then call its sign and verify methods, or if you still need a key generate_secret or generate_keypair depending on the algorithm.

Installation

You can install the package via pip:

pip install jws-algorithms

Basic Usage

Symmetric algorithms use shared secrets that are simple random byte strings:

from jws_algorithms import SymmetricAlgorithm

# Our message we want to sign and verify using HMAC-SHA256
message = b"Hello, World!"

# Generate a new secret random bytes
key = SymmetricAlgorithm.HS256.generate_secret()

# Sign the message
signature = SymmetricAlgorithm.HS256.sign(key, message)

# Verify the signature
assert SymmetricAlgorithm.HS256.verify(key, message, signature)

For more security, asymmetric algorithms use a private key to sign messages and a public key to verify signatures:

from jws_algorithms import AsymmetricAlgorithm

# Our message we want to sign and verify using RSA-SHA256
message = b"Hello, World!"

# We need a public and private key pair
public_key, private_key = AsymmetricAlgorithm.RS256.generate_keypair()

# Sign the message with the private key
signature = AsymmetricAlgorithm.RS256.sign(private_key, message)

# Verify the signature with the public key
assert AsymmetricAlgorithm.RS256.verify(public_key, message, signature)

Keys from files

The private keys can also be loaded from files by passing a pathlib.Path object to the loading functions.

from pathlib import Path
from jws_algorithms import AsymmetricAlgorithm

# The message to sign using RSA-SHA256
message = b'Hello, World!'

# Sign with a private key loaded from a file
signature = AsymmetricAlgorithm.RS256.sign(
    Path('path/to/private_key.pem'),
    message
)

# Verify with a public key loaded from a file
assert AsymmetricAlgorithm.RS256.verify(
    Path('path/to/public_key.pem'),
    message,
    signature
)

From raw text or environment

Keys can also be passed as raw text (often from environment variables) by calling the functions with a str or bytes instead of a Path or compiled representation of the cryptography package.

import os
from jws_algorithms import AsymmetricAlgorithm

# The message to sign using RSA-SHA256
message = b'Hello, World!'

# Sign with a private key loaded from an environment variable
signature = AsymmetricAlgorithm.RS256.sign(os.environ['PRIVATE_KEY'], message)

# Verify with a public key loaded from an environment variable
assert AsymmetricAlgorithm.RS256.verify(os.environ['PUBLIC_KEY'], message, signature)

Encrypted private keys

When loading private keys, you can provide an optional password if the private key is encrypted. Important: You have to install with all optional dependencies or specifically the encryption extra to use this feature, as it depends on the bcrypt package.

from pathlib import Path
from jws_algorithms import AsymmetricAlgorithm

# Sign with an encrypted private key loaded from a file
signature = AsymmetricAlgorithm.RS256.sign(
    Path('path/to/encrypted_private_key.pem'),
    b'Hello, World!',
    password='my_secret_password'
)

# Public keys are not encrypted, so no password is needed here
assert AsymmetricAlgorithm.RS256.verify(
    Path('path/to/public_key.pem'),
    b'Hello, World!',
    signature
)

Using this enum in your own code

You can use the SymmetricAlgorithm and AsymmetricAlgorithm enums in your own code to select algorithms dynamically. For example, when your client has a signature, they can send the algorithm name along it and you can parse it using the enum:

from jws_algorithms import SymmetricAlgorithm, AsymmetricAlgorithm

def index(request):
    alg_name = request.headers.get("X-Signature-Algorithm")
    algorithm = SymmetricAlgorithm[alg_name] if alg_name in SymmetricAlgorithm else AsymmetricAlgorithm[alg_name]
    message = request.body
    signature = request.headers.get("X-Signature")
    key = get_key_somehow(alg_name)  # Load the key from a database
    if not algorithm.verify(key, message, signature):
        raise ValueError("Invalid signature")
    # Process the request

JWK (JSON Web Key) export

You can export the keys to JWK format according to the algorithm used when calling the to_jwk method on the key object. It will return a dictionary with the JWK representation of the key, that you then can publish or use in other libraries that support JWK.

from jws_algorithms import AsymmetricAlgorithm

# Generate a new RSA key pair
public_key, private_key = AsymmetricAlgorithm.RS256.generate_keypair()

# Export the public key to JWK format
public_jwk = AsymmetricAlgorithm.RS256.to_jwk(public_key)

As a safety precaution, passing the private key will also yield the public key's JWK representation.

public_jwk = AsymmetricAlgorithm.RS256.to_jwk(private_key)

If you know what you are doing, you can export the private key by explicitly enabling the include_private flag

private_jwk = AsymmetricAlgorithm.RS256.to_jwk(private_key, include_private=True)

How to generate keys

In case you don't have keys yet, here are some examples of how to generate them.

HMAC

Symmetric HMAC-SHA is just some random bytes:

# Using openssl
openssl rand -base64 32 > hmac_secret.key

# Using python
python -c "import os; print(os.urandom(32).hex())" > hmac_secret.key

RSA

Using openssl:

# Generate a 2048-bit RSA private key in PEM format
openssl genpkey -algorithm RSA -out rsa_private.pem -pkeyopt rsa_keygen_bits:2048

# Extract the public key from the private key
openssl rsa -pubout -in rsa_private.pem -out rsa_public.pem

Using ssh-keygen:

# Generate a 2048-bit RSA private key in PEM format
ssh-keygen -t rsa -b 2048 -m PEM -f rsa_private.pem

# Extract the public key from the private key
ssh-keygen -y -f rsa_private.pem > rsa_public.pem

ECDSA

Using openssl:

# Generate a private key for the P-256 curve in PEM format
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa_private.pem

# Extract the public key from the private key
openssl ec -in ecdsa_private.pem -pubout -out ecdsa_public.pem

Using ssh-keygen:

# Generate a private key for the P-256 curve in PEM format
ssh-keygen -t ecdsa -b 256 -m PEM -f ecdsa_private.pem

# Extract the public key from the private key
ssh-keygen -y -f ecdsa_private.pem > ecdsa_public.pem

EdDSA (Ed25519)

Using openssl:

# Generate an Ed25519 private key in PEM format
openssl genpkey -algorithm ED25519 -out ed25519_private.pem
# Extract the public key from the private key
openssl pkey -in ed25519_private.pem -pubout -out ed25519_public.pem

Using ssh-keygen:

# Generate an Ed25519 private key in PEM format
ssh-keygen -t ed25519 -m PEM -f ed25519_private.pem

# Extract the public key from the private key
ssh-keygen -y -f ed25519_private.pem > ed25519_public.pem

Metadata

Release files for jws-algorithms 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jws-algorithms 1.3.0
File Size Uploaded
jws_algorithms-1.3.0.tar.gz 13.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jws-algorithms 1.3.0
File Interpreter ABI Platform
jws_algorithms-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.7 kB

Release files / jws_algorithms-1.3.0.tar.gz

Download URL jws_algorithms-1.3.0.tar.gz
Size 13.2 kB
Tags Source
SHA-256 checksum
How to use checksums
a0b66a4780c377a657a796bbff9979009a09b19c2aa79a8f5173210d5c2f4c2a
BLAKE2b-256 checksum
How to use checksums
e8d03c9c5ccaf95998549d40d6ad30283bfdad6de443df8118bbace664d2f777
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 27, 2026.

Transparency log

Release files / jws_algorithms-1.3.0-py3-none-any.whl

Download URL jws_algorithms-1.3.0-py3-none-any.whl
Size 14.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5ac3a0fb8ff095021e32afee0f1173ba4a93c3a5a05ab30f41610da651c5e03e
BLAKE2b-256 checksum
How to use checksums
ab11860fe46a481ec6b88acd5451f37aa494f604665b99484eae85e1d17e83fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.3.0 This release

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.1.1

2 release files

1.0.7

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page