3.6 KiB
Signer
The :pyaputils.Signer
object stores an RSA key (public or
private) and is used as a representation of an actor key.
Using an existing key
= Signer(Path('/home/test/testkey'), 'https://social.example.com/actor#main-key') signer
key
can be a string (usually the output of :pyCrypto.PublicKey.RSA.RsaKey.export_key
), an :pyCrypto.PublicKey.RSA.RsaKey
, or a :pypathlib.Path
pointing to a
file containing a text versino of a key.
keyid
must be a url pointing to an ActivityPub actor
containing a public key
Generating a new Signer
= Signer.new('https://social.example.com/actor#main-key', key_size=256)
signer = Signer.new('https://social.example.com/actor#main-key', key_size=4096) signer
If you don't already have a signing key, a new one can be generated
for you. Just input the url to where the public key will be displayed
and (optionally) the size of the key. The example above generates two
equally-sized keys since values under 1024 are assumed to be bytes
instead of bits. Be sure to use :pyaputils.Signer.export
and store the resulting
str
somewhere.
Importing a key from an actor
ActivityPub Actor
objects should always contain a public
key in actor['publicKey']['publicKeyPem']
. Just fetch the
actor and pass the whole dict
object to aputils.Signer.new_from_actor
= Signer.new_from_actor(actor) signer
Signing headers
Most ActivityPub resources can only be accessed with a valid
Signature
header. Fortunately ApUtils makes this extremely
simple.
= {'Accept': 'application/activity+json'}
headers = signer.sign_headers('GET', 'https://social.example2.com/user/8_sergals_in_a_trenchcoat', headers=headers) new_headers
The above example creates Host
, Date
, and
Signature
and returns them with with original
headers
as new_headers
. Just pass
new_headers
to whatever HTTP client you use. Example:
= urllib.request.Request(
request = 'https://social.example2.com/user/8_sergals_in_a_trenchcoat',
url = 'GET',
method = new_headers
headers
)
with urllib.request.urlopen(request) as response:
print(json.loads(response.read(), indent=4))
Alternatively a whole request can be passed to
aputils.Signer.sign_request
to simplify signing even
further:
= signer.sign_request(urllib.request.Request(
request = 'https://social.example2.com/user/8_sergals_in_a_trenchcoat',
url = 'GET',
method = {'Accept': 'application/activity+json'}
headers
))
with urllib.request.urlopen(request) as response:
print(json.loads(response.read(), indent=4))
Validating headers
Making sure the signature matches on the other end is slightly more
complicated, but still easy. First make sure you acquire the actor and
pass it to aputils.Signer.new_from_actor
. Then call aputils.Signer.validate_signature
. If validation
passes, True
is returned. Otherwise a aputils.SignatureFailureError
will be raised. If
you're running aiohttp, you can use the aputils.Signer.validate_aiohttp_request
method
instead of validate_signature
to simplify verification
= Signer.new_from_actor(actor)
signer
try:
signer.validate_signature(request.method, request.path, request.headers, request.body)
except SignatureFailureError as e:
raise HttpError(body=str(e), status=401)