Get started
apsig is collection of signature implemention used in ActivityPub.
This library implements the creation/verification of signatures for HTTP Signatures (draft-cavage-http-signatures-12), Linked Data Signatures 1.0, and Object Integrity Proofs (FEP-8b32).
RFC9421 implementation is progress.
Installation
# pip
pip install apsig
# uv
uv add apsig
# pdm
pdm add apsig
Example
First, prepare the keys for signing and verification. apsig uses the cryptography library.
from cryptography.hazmat.primitives.asymmetric import rsa, ed25519
from cryptography.hazmat.primitives import serialization
# For HTTP Signatures (RSA)
private_key_rsa = rsa.generate_private_key(public_exponent=65537, key_size=3092)
public_key_rsa_pem = private_key_rsa.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
# For Object Integrity Proofs (Ed25519)
private_key_ed = ed25519.Ed25519PrivateKey.generate()
public_key_ed = private_key_ed.public_key()
HTTP Signature (draft)
This is used for signing HTTP requests.
import email.utils
from apsig.draft import Signer, Verifier
# === Signing ===
method = "POST"
url = "https://example.com/api/resource"
headers = {
"Content-Type": "application/json",
"Date": email.utils.formatdate(usegmt=True),
}
body = '{"key": "value"}'
key_id = "https://example.com/users/johndoe#main-key"
signer = Signer(
headers=headers,
private_key=private_key_rsa,
method=method,
url=url,
key_id=key_id,
body=body.encode("utf-8"),
)
signed_headers = signer.sign()
print(signed_headers)
# === Verifying ===
verifier = Verifier(
public_pem=public_key_rsa_pem.decode("utf-8"),
method=method,
url=url,
headers=signed_headers,
body=body.encode("utf-8"),
)
verified_key_id = verifier.verify(raise_on_fail=True)
print(f"Verified with key: {verified_key_id}")
Object Integrity Proofs (proof)
This is used for signing JSON objects (like ActivityStreams objects).
from apsig import ProofSigner, ProofVerifier
# === Signing ===
json_object = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/data-integrity/v1",
],
"id": "https://server.example/objects/1",
"type": "Note",
"content": "Hello world",
}
proof_options = {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "https://example.com/keys/1",
"created": "2024-01-01T09:00:00Z",
}
signer = ProofSigner(private_key_ed)
signed_object = signer.sign(json_object, proof_options)
print(signed_object)
# === Verifying ===
verifier = ProofVerifier(public_key_ed)
verified_key_id = verifier.verify(signed_object, raise_on_fail=True)
print(f"Verified with key: {verified_key_id}")
Linked Data Signature (LD-Signature)
This is another method for signing JSON-LD objects, often used in older ActivityPub implementations.
from apsig import LDSignature
# === Signing ===
ld_signer = LDSignature()
json_ld_object = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
],
"type": "Note",
"content": "Hello, Linked Data!",
}
creator = "https://example.com/users/johndoe#main-key"
signed_ld_object = ld_signer.sign(
doc=json_ld_object,
creator=creator,
private_key=private_key_rsa
)
print(signed_ld_object)
# === Verifying ===
# The public key can be passed directly.
public_key_rsa = private_key_rsa.public_key()
verified_creator = ld_signer.verify(
doc=signed_ld_object,
public_key=public_key_rsa,
raise_on_fail=True
)
print(f"Verified with creator: {verified_creator}")
License
MIT License