[](https://github.com/Mari6814/py-jws-algorithms/actions/workflows/test.yml)
[](https://github.com/Mari6814/py-jws-algorithms/raw/main/badges/coverage.svg)
[](https://github.com/Mari6814/py-jws-algorithms/raw/main/badges/python-versions.svg)
# Introduction
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:
```bash
pip install jws-algorithms
```
# Basic Usage
Symmetric algorithms use shared _secrets_ that are simple random byte strings:
```python
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:
```python
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.
```python
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.
```python
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.
```python
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:
```python
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
```
# 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:
```bash
# 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:
```bash
# 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:
```bash
# 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:
```bash
# 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:
```bash
# 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:
```bash
# 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:
```bash
# 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
```
Raw data
{
"_id": null,
"home_page": null,
"name": "jws-algorithms",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "jose, jose algorithms, json web signature, json web token, jws, jwt, jwt algorithms, signature, signing, verify",
"author": null,
"author_email": "Marian Plivelic <marianplivelic@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/a8/aa/56feb5d48c5dedc7852ebef1a462a02dbeffcdc925cd569e7529007256e1/jws_algorithms-1.2.2.tar.gz",
"platform": null,
"description": "[](https://github.com/Mari6814/py-jws-algorithms/actions/workflows/test.yml)\n[](https://github.com/Mari6814/py-jws-algorithms/raw/main/badges/coverage.svg)\n[](https://github.com/Mari6814/py-jws-algorithms/raw/main/badges/python-versions.svg)\n\n# Introduction\n\nA 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.\n\nThis library provides two enums:\n\n- **`SymmetricAlgorithm`**: for symmetric algorithms like HMAC, where the same secret is used for signing and verifying.\n- **`AsymmetricAlgorithm`**: for asymmetric algorithms like RSA and ECDSA, where a private key is used for signing and a public key is used for verification.\n\nSelect 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.\n\n# Installation\n\nYou can install the package via pip:\n\n```bash\npip install jws-algorithms\n```\n\n# Basic Usage\n\nSymmetric algorithms use shared _secrets_ that are simple random byte strings:\n\n```python\nfrom jws_algorithms import SymmetricAlgorithm\n\n# Our message we want to sign and verify using HMAC-SHA256\nmessage = b\"Hello, World!\"\n\n# Generate a new secret random bytes\nkey = SymmetricAlgorithm.HS256.generate_secret()\n\n# Sign the message\nsignature = SymmetricAlgorithm.HS256.sign(key, message)\n\n# Verify the signature\nassert SymmetricAlgorithm.HS256.verify(key, message, signature)\n```\n\nFor more security, asymmetric algorithms use a _private key_ to sign messages and a _public key_ to verify signatures:\n\n```python\nfrom jws_algorithms import AsymmetricAlgorithm\n\n# Our message we want to sign and verify using RSA-SHA256\nmessage = b\"Hello, World!\"\n\n# We need a public and private key pair\npublic_key, private_key = AsymmetricAlgorithm.RS256.generate_keypair()\n\n# Sign the message with the private key\nsignature = AsymmetricAlgorithm.RS256.sign(private_key, message)\n\n# Verify the signature with the public key\nassert AsymmetricAlgorithm.RS256.verify(public_key, message, signature)\n```\n\n# Keys from files\n\nThe private keys can also be loaded from files by passing a `pathlib.Path` object to the loading functions.\n\n```python\nfrom pathlib import Path\nfrom jws_algorithms import AsymmetricAlgorithm\n\n# The message to sign using RSA-SHA256\nmessage = b'Hello, World!'\n\n# Sign with a private key loaded from a file\nsignature = AsymmetricAlgorithm.RS256.sign(\n Path('path/to/private_key.pem'),\n message\n)\n\n# Verify with a public key loaded from a file\nassert AsymmetricAlgorithm.RS256.verify(\n Path('path/to/public_key.pem'),\n message,\n signature\n)\n```\n\n# From raw text or environment\n\nKeys 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.\n\n```python\nimport os\nfrom jws_algorithms import AsymmetricAlgorithm\n\n# The message to sign using RSA-SHA256\nmessage = b'Hello, World!'\n\n# Sign with a private key loaded from an environment variable\nsignature = AsymmetricAlgorithm.RS256.sign(os.environ['PRIVATE_KEY'], message)\n\n# Verify with a public key loaded from an environment variable\nassert AsymmetricAlgorithm.RS256.verify(os.environ['PUBLIC_KEY'], message, signature)\n```\n\n# Encrypted private keys\n\nWhen loading private keys, you can provide an optional password if the private key is encrypted.\n**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.\n\n```python\nfrom pathlib import Path\nfrom jws_algorithms import AsymmetricAlgorithm\n\n# Sign with an encrypted private key loaded from a file\nsignature = AsymmetricAlgorithm.RS256.sign(\n Path('path/to/encrypted_private_key.pem'),\n b'Hello, World!',\n password='my_secret_password'\n)\n\n# Public keys are not encrypted, so no password is needed here\nassert AsymmetricAlgorithm.RS256.verify(\n Path('path/to/public_key.pem'),\n b'Hello, World!',\n signature\n)\n```\n\n# Using this enum in your own code\n\nYou 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:\n\n```python\nfrom jws_algorithms import SymmetricAlgorithm, AsymmetricAlgorithm\n\ndef index(request):\n alg_name = request.headers.get(\"X-Signature-Algorithm\")\n algorithm = SymmetricAlgorithm[alg_name] if alg_name in SymmetricAlgorithm else AsymmetricAlgorithm[alg_name]\n message = request.body\n signature = request.headers.get(\"X-Signature\")\n key = get_key_somehow(alg_name) # Load the key from a database\n if not algorithm.verify(key, message, signature):\n raise ValueError(\"Invalid signature\")\n # Process the request\n```\n\n# How to generate keys\n\nIn case you don't have keys yet, here are some examples of how to generate them.\n\n## HMAC\n\nSymmetric HMAC-SHA is just some random bytes:\n\n```bash\n# Using openssl\nopenssl rand -base64 32 > hmac_secret.key\n\n# Using python\npython -c \"import os; print(os.urandom(32).hex())\" > hmac_secret.key\n```\n\n## RSA\n\nUsing openssl:\n\n```bash\n# Generate a 2048-bit RSA private key in PEM format\nopenssl genpkey -algorithm RSA -out rsa_private.pem -pkeyopt rsa_keygen_bits:2048\n\n# Extract the public key from the private key\nopenssl rsa -pubout -in rsa_private.pem -out rsa_public.pem\n```\n\nUsing ssh-keygen:\n\n```bash\n# Generate a 2048-bit RSA private key in PEM format\nssh-keygen -t rsa -b 2048 -m PEM -f rsa_private.pem\n\n# Extract the public key from the private key\nssh-keygen -y -f rsa_private.pem > rsa_public.pem\n```\n\n## ECDSA\n\nUsing openssl:\n\n```bash\n# Generate a private key for the P-256 curve in PEM format\nopenssl ecparam -name prime256v1 -genkey -noout -out ecdsa_private.pem\n\n# Extract the public key from the private key\nopenssl ec -in ecdsa_private.pem -pubout -out ecdsa_public.pem\n```\n\nUsing ssh-keygen:\n\n```bash\n# Generate a private key for the P-256 curve in PEM format\nssh-keygen -t ecdsa -b 256 -m PEM -f ecdsa_private.pem\n\n# Extract the public key from the private key\nssh-keygen -y -f ecdsa_private.pem > ecdsa_public.pem\n```\n\n## EdDSA (Ed25519)\n\nUsing openssl:\n\n```bash\n# Generate an Ed25519 private key in PEM format\nopenssl genpkey -algorithm ED25519 -out ed25519_private.pem\n# Extract the public key from the private key\nopenssl pkey -in ed25519_private.pem -pubout -out ed25519_public.pem\n```\n\nUsing ssh-keygen:\n\n```bash\n# Generate an Ed25519 private key in PEM format\nssh-keygen -t ed25519 -m PEM -f ed25519_private.pem\n\n# Extract the public key from the private key\nssh-keygen -y -f ed25519_private.pem > ed25519_public.pem\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "Implements the most common JWT algorithms to generate keys or sign and verify payloads without the requirement to first read a lot of documentation just to do the signing and verifying part.",
"version": "1.2.2",
"project_urls": {
"Documentation": "https://github.com/Mari6814/py-jws-algorithms",
"Repository": "https://github.com/Mari6814/py-jws-algorithms"
},
"split_keywords": [
"jose",
" jose algorithms",
" json web signature",
" json web token",
" jws",
" jwt",
" jwt algorithms",
" signature",
" signing",
" verify"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "c6903b9d08f5f6a61a69a74e0f8fcd0540eeab889e5ce526628bc0aba9f45128",
"md5": "0817487a298b6158fe646f780ebf7923",
"sha256": "c90e86295b49aa001d2c61d3bee83a8f7f0859186eae027084be2c65b8dd1f45"
},
"downloads": -1,
"filename": "jws_algorithms-1.2.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0817487a298b6158fe646f780ebf7923",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 11681,
"upload_time": "2025-10-08T20:32:03",
"upload_time_iso_8601": "2025-10-08T20:32:03.623002Z",
"url": "https://files.pythonhosted.org/packages/c6/90/3b9d08f5f6a61a69a74e0f8fcd0540eeab889e5ce526628bc0aba9f45128/jws_algorithms-1.2.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a8aa56feb5d48c5dedc7852ebef1a462a02dbeffcdc925cd569e7529007256e1",
"md5": "55e845423afe6546696138dc0ab9f413",
"sha256": "3b1a3723ab473d87df93d55a18d8304b7d30a64007cf8d580fc38a53c50f2fe2"
},
"downloads": -1,
"filename": "jws_algorithms-1.2.2.tar.gz",
"has_sig": false,
"md5_digest": "55e845423afe6546696138dc0ab9f413",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 10967,
"upload_time": "2025-10-08T20:32:04",
"upload_time_iso_8601": "2025-10-08T20:32:04.976722Z",
"url": "https://files.pythonhosted.org/packages/a8/aa/56feb5d48c5dedc7852ebef1a462a02dbeffcdc925cd569e7529007256e1/jws_algorithms-1.2.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-08 20:32:04",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Mari6814",
"github_project": "py-jws-algorithms",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "jws-algorithms"
}