Signing test vectors
A signature is right to the byte or it is refused, and a refusal never says which byte. These vectors let you find the byte yourself: each one is a real request, answer or enrolment proof with the exact text that was signed and the exact result expected.
Using the .NET SDK?
It already passes every vector. This page is for a client written by hand — or by an AI assistant.
Test keys only
The keys in these files are test keys — never Anis’s or any partner’s real keys — and partners.anis.ly is an
example address. Use them to test, never to enrol.
What is here
| Set | What it tests | Vectors |
|---|---|---|
| Requests | Your signature base, byte for byte, for every kind of signed call | 9 |
| Answers | Your answer checks: which answers to accept and which to throw away | 39 |
| Enrolment | Your proof-of-possession message | 2 |
/vectors/index.json lists every vector with its title, its file and — for answers — whether
it must be accepted or rejected. The same index names the signed parts of each kind of call.
Testing request signing
Each request vector carries the request (request), the signature’s times (signature.created,
signature.expires), the test key (key) and what must come out (expected).
- Build the signature base from
request.method,request.authority,request.path,request.canonicalQuery(the query without its?),request.anisDate,request.nonce,request.idempotencyKeyand the body inrequest.bodyBase64, withkey.keyIdas the key id. - Compare your
Content-Digestwithexpected.contentDigest, yourSignature-Inputwithexpected.signatureInput, and your base withexpected.signatureBaseUtf8— byte for byte. - Sign your base with the test private key (
key.privateKeyPkcs8Base64) and check thatkey.publicJwkverifies it. An ECDSA signature is different every time, so compare the base, never the signature bytes.
RQ-03 gives the host as a caller might type it (request.authorityAsGiven) and as it must be signed
(request.authority, lower-case). RQ-06 is a reveal: no body at all, so the digest of zero bytes.
Testing answer verification
Each answer vector carries the answer (response.status, response.headers, response.bodyBase64), the
Signature-Input of the request it answers (request.signatureInput), Anis’s published keys at that moment
(signingKeys) and the time to use as your clock (verifyAt, Unix seconds — use it, not the real clock).
Your verifier must reach expected.outcome: accept or reject. For a rejection, expected.reason says why;
your own error names may differ, so match the outcome and use the reason to understand a mismatch.
signatureBaseForDebugging is the base Anis signed.
Most answers come in families: one that must be accepted (RS-001-…) and changed copies of it (RS-001-…-M01
onward) that must each be rejected — one body byte changed, a rewritten digest, a DER signature, a signed part
missing, a time out of the window. Others test the published keys: an answer signed by the next or the retiring key
must be accepted, and a key document carrying a private member must be refused whole.
Testing the enrolment proof
Each enrolment vector gives what the key submission returned (keySubmissionResult) and the test key (key).
- Build the proof message from
keySubmissionResultand compare it withexpected.proofMessageUtf8— and the challenge’s hash withexpected.challengeHashHex. - Sign it with the test key;
exampleSignatureis one valid signature, andkey.publicJwkverifies both. expected.proofRequestis the body to send.
After the vectors pass
Call Check your signature with your real key. It runs every check Anis runs on a signed call, reports what Anis saw, and changes nothing.