Authorization
How to sign requests
import binascii
import collections
import decimal
import time
from Crypto.Hash import SHA512
from Crypto.PublicKey import RSA
from Crypto.Signature.pkcs1_15 import PKCS115_SigScheme
# Example of private/public keys
PRIVATE_KEY_PEM=-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC8+WkIRyupart2WnDWSYr8Qz4LOXSft8IxlzfiLkSWL4c0Y9WI\nC4UbKDAH7Som+5gOTTRt9YckHVc3FO+HyGQdmPATKxcJoVk8Bj0nmLSiWN+x6DHL\ne/4E3xqbwGlmCuwVGM1Yg3HDMneJznNGLvfPXAt0ZnpLuRaBdI3cHjm1PwIDAQAB\nAoGAG59LSoH4sFlhEHI01yXF0ozTp1ldIjF+ibgkW4PO07MbzL0hC7M/YBJWPvpc\nNHmHJuQDG2WNvOorAuqk/pB8jZQUquH3exzLmohJy65Sl19EL24K8REx0gsg8+Q3\n+dPwVE3QXAKCUtmRmQPv+WGzWVvi1rDim9Il87u/t72MPY0CQQC/6dilUIirCt32\np6p9j8U9CP09E9nOOd6cehKbyqk8F/8pLb991IoKLdMLAevrdwas8OVxXv75RxcV\ni/kgu0nVAkEA/BRMtFhcGkwWT3+TeHuTtz/AYXTYwVgnCV8g+5NyDPGOM83+rrn0\nfl3SP3RdJSpbl2hqfKee4LaUNxHvP9KYwwJAb9gm7GE85QHWThN05uxM9L8yZtAU\n1X0gYW07PxQjAOSzThpdSUX+lOQT5IxgI8AgYdWawjGswzZNG7+RlyPPaQJBAK9R\nPve+7G+ts2afB+WnrNiqEha1uXre4Gd4LexKY+v0pD4LarICyrSIdA2F1LJgf0sS\nZ91ChxEqanLSml79DRkCQDyD74RrQJB3zFw7zJQI52rTXePXn8IZnf/O+dqCHjQs\no/MO7n1kavb6syDVP2AO0iqnDyB8UNHVlrl5LavjIH0=\n-----END RSA PRIVATE KEY-----
PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8+WkIRyupart2WnDWSYr8Qz4LOXSft8IxlzfiLkSWL4c0Y9WIC4UbKDAH7Som+5gOTTRt9YckHVc3FO+HyGQdmPATKxcJoVk8Bj0nmLSiWN+x6DHLe/4E3xqbwGlmCuwVGM1Yg3HDMneJznNGLvfPXAt0ZnpLuRaBdI3cHjm1PwIDAQAB
def stringify_value(value: str) -> str:
"""
Convert the value to a decimal string or a lower case string.
:param value: The value to be converted.
:return: The converted value as a string.
"""
try:
# Try to convert value to a decimal string.
decimal_value = Decimal(str(value))
except decimal.InvalidOperation:
# If not possible, convert the value to a lower case string.
return str(value).lower()
return str(decimal_value)
def request_body_to_str_line(body: dict, timestamp: str) -> str:
"""
Convert the body of the request to a string line.
:param body: The body of the request as a dictionary.
:param timestamp: The timestamp as a string.
:return: The string line.
"""
sorted_body = collections.OrderedDict(sorted(body.items())) if isinstance(body, dict) else {}
line = "".join(f"{key.lower()}{stringify_value(value)}" for key, value in sorted_body.items() if value)
line += f"timestamp{timestamp}"
return line.lower()
def request_sign(body: dict, timestamp: str, private_key_pem: bytes) -> str:
"""
Generate the request signature.
:param body: The body of the request as a dictionary.
:param timestamp: The timestamp as a string.
:param private_key_pem: The private key as a PEM string.
:return: The request signature as a string.
"""
line = request_body_to_str_line(body, timestamp)
new_hash = SHA512.new(line.encode())
signer = PKCS115_SigScheme(RSA.importKey(private_key_pem))
sign = signer.sign(new_hash)
return binascii.hexlify(sign).decode("utf-8")
def get_authorization_headers(private_key_pem: bytes, public_key: str, body: dict) -> dict:
"""
Get the authorization headers.
:param private_key_pem: The private key as a PEM string.
:param public_key: The public key as a string.
:param body: The body of the request as a dictionary.
:return: The authorization headers as a dictionary.
"""
timestamp = str(int(time.time()))
signature = request_sign(body, timestamp, private_key_pem)
return {
"public-key": public_key,
"signature": signature,
"timestamp": timestamp,
}
# Example of signed request
base_url = 'https://test.acquiring.swapple.io'
body = {'currency': 'CARDUAH', 'amount': '10', ...}
requests.post(
f'{base_url}/public/api/orders/',
json=body,
headers=get_authorization_headers(body)
)How to verify responses (Optional)
Last updated