SReverseby Simpa Labs

Signature reproduction

How to reproduce an Android API signature in Python

Two apps can both sign requests with HMAC-SHA256 and still need different client code, because the string being signed is the part that varies. Reproducing a signature means recovering that string exactly.

What a signature actually is

A signature is the output of a hash, an HMAC or a public key operation over a byte string the app assembles from the request. The algorithm is rarely the hard part. The input is. A SHA-256 of the wrong string is as useless as the wrong hash of the right string, and the server cannot tell you which mistake you made.

The string the app signs

Several conventions appear in Android clients, often combined:

  • Method and path, joined in a fixed order, with the query string either appended raw or excluded.
  • Selected headers, listed in the order the code gave them, not the order your HTTP client sends them.
  • The request body, sometimes as raw bytes and sometimes as a re-serialised JSON string.
  • A timestamp and a nonce, appended before hashing so the output cannot be reused.
  • A trailing secret used as an HMAC key or concatenated onto the end of the message.

Every one of those choices changes the bytes. Concatenation order, the delimiter between parts, whether empty parameters are skipped or included as empty, and whether values are percent-encoded once or twice all matter. Some schemes build a sorted query string, some keep the original order, and some sort by key while leaving values alone.

Recovering the input from the device

You need input and output pairs produced by the app itself. Hook the signing routine and log its arguments and its return value, or hook the hash primitive and log the byte array passed into it. The second option is more work and much more reliable, because it shows the exact bytes rather than the objects the app passed around.

Log the outgoing request at the end of the interceptor chain as well. That gives you the headers as transmitted, which is the comparison point for your Python output. Confirm which of those values the app generated and which it received from the server.

Writing the Python client

Keep the implementation boring. Read the key as bytes, build the canonical string with an explicit order, and hash it. Choose hmac.new over hashing a concatenated key yourself when the scheme uses a key. Compare your first output against a captured pair with the same inputs, printing both as hex. If they differ, the input differs.

Narrow the difference with a binary search over the string. Remove the body from the concatenation and see whether the output still matches. Remove the timestamp. Swap the order of two adjacent parts. Each test either restores the match or eliminates a candidate, and the correct construction is found rather than guessed.

Where reproductions usually go wrong

  • Encoding. Base64 has standard and URL-safe alphabets, and padding is sometimes stripped. Hex is sometimes lowercase and sometimes uppercase.
  • JSON key order. The app may sign the bytes it must send, not a re-serialised version of the parsed object, and Python dictionaries do not preserve the same order by accident.
  • Number formatting. Floats, large integers and booleans serialise differently across languages, so a body that looks equivalent produces different bytes.
  • Unicode escaping. Escaping non-ASCII characters changes byte length and therefore the digest.
  • Whitespace. An extra trailing newline or a space after a colon is enough to break the match.
  • Double encoding. Signing the encoded form of a value and sending the decoded form, or the reverse.

After the signature matches

Reproducing one signature is a milestone rather than a finished client. Requests in a real flow depend on order and state: a token endpoint returns a credential that later calls expect, cookies carry session continuity, and some endpoints return a value that feeds the next signature. Timestamp and nonce handling needs a client clock that is close enough to the server's to stay inside the accepted window, and retry logic has to generate a new signature per attempt rather than resending the old one.

If the signing routine lives in a native library, the recovery work changes shape but the target does not. You still need the canonical string and the key, and you still verify by matching the device output exactly.

Reviewed 28 September 2026 · SReverse research desk

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?