1. Purpose & Scope
Platform issues server-to-server requests, over HTTPS, to endpoints operated by the Partner. Because these requests may create accounts, move funds, or change the state of financial operations, the receiving endpoint must be able to prove two properties for every request before acting on it:- Authenticity — the request was generated by Platform and not by a third party.
- Integrity — the body is exactly what Platform sent, with no alteration by any intermediary along the way.
X-Signature header, accompanied by an X-Timestamp header that forms part of the signed message (see 3.2).
Scope. This section covers message-level authentication only, and it does not replace the protection applied to the channel itself — see Communication Security. The two complement each other as part of a defense-in-depth posture: TLS protects the data in transit, the signature proves who produced the payload and that it arrived unaltered.
2. Security Rationale & Best Practices
The scheme is not proprietary; it is a direct application of well-established, standards-based cryptographic guidance. This section documents the reasoning so that the Partner’s security teams can validate the approach against their own compliance frameworks.2.1 Why a keyed MAC (HMAC) in addition to TLS
TLS protects data in transit between two network peers, but it does not, by itself, prove to the application which party produced a given payload — especially in environments with TLS-terminating proxies, load balancers, WAFs, or API gateways where the original peer identity is lost before the request reaches the application. A keyed MAC binds the request body to a secret shared only by Platform and the Partner, producing an application-level proof of authenticity and integrity that survives intermediaries.This is not an argument against transport-level controls. TLS is mandatory on every connection, and a Partner may additionally request one of the network controls described in Communication Security. Those controls operate below the application and remain necessary. The signature addresses what no transport-level control can: it travels with the payload, so the proof of authenticity and integrity is still verifiable at the application, after any intermediary has terminated the TLS connection.
2.2 Why HMAC-SHA256 specifically
- Standardized construction. HMAC is defined in RFC 2104 and standardized as FIPS PUB 198-1 (The Keyed-Hash Message Authentication Code). Its security is formally analyzed and does not depend on the secrecy of the algorithm — only on the secrecy of the key (Kerckhoffs’s principle).
- Approved hash. SHA-256 is a NIST-approved hash function (FIPS 180-4) and is recommended for MAC use by NIST SP 800-107 Rev. 1. It offers a 256-bit security margin with no known practical attacks against HMAC-SHA256.
- Symmetric & low-overhead. Verification is a single hash computation — fast enough to run on every request without meaningful latency, unlike asymmetric signatures.
- Ubiquitous. Native, audited implementations exist in every mainstream platform (.NET
HMACSHA256, JavaMac, Nodecrypto, Pythonhmac), removing the need for custom cryptographic code.
2.3 Alignment with industry guidance
Conclusion. HMAC-SHA256 request signing is a recognized best-practice pattern for authenticating server-to-server requests. Implementing it as specified here brings the Partner integration in line with OWASP and NIST guidance.
3. Signature Scheme Specification
3.1 Algorithm parameters
3.2 Canonicalization rule (critical)
The signed message is built by concatenating three byte sequences, in order: theX-Timestamp header value, a literal . separator, and the request body — each as a byte sequence, not a logical object:
X-Timestamp header string as received.
3.3 Required idempotency field
Every request body includes a mandatoryTransactionId field: an idempotency key that Platform derives deterministically from the underlying operation. Retrying a call always reproduces the same TransactionId, which is what allows the Partner to detect a delivery it has already executed and safely ignore it instead of executing the operation twice.
GetCreditStatus is the exception: each poll carries its own distinct TransactionId, since every poll is a new query rather than a redelivery of a previous one.
3.4 HTTP header
4. Verification Procedure (Mandatory)
Every inbound request must be verified with the following steps before any business logic executes:1
Read the raw body
Capture the request body as a byte buffer without deserializing it. Keep this exact buffer for both verification and later parsing.
2
Read the headers
Extract
X-Timestamp and X-Signature. If either is absent or empty, reject the request with 401 Unauthorized.3
Build the signed payload
Concatenate the raw
X-Timestamp header bytes, a literal . byte, and the raw body bytes (see 3.2).4
Recompute
Compute the HMAC-SHA256 signature over the signed payload, encoded in Base64, using the shared secret assigned to this Partner.
5
Compare in constant time
Compare the recomputed value with the received
X-Signature header using a fixed-time comparison (never == / ordinary string equality).6
Decide
If they match, continue processing. If they do not, reject with
401 Unauthorized and log the event (without logging the secret).5. Reference Implementation (C# / .NET)
The following ASP.NET Core middleware verifies the signature over the raw body and rejects invalid requests. It buffers the body so that downstream model binding still works.CryptographicOperations.FixedTimeEquals (available in .NET Core 2.1+) performs the required constant-time comparison. Do not substitute string.Equals or ==.6. Operational & Security Requirements
The shared secret is generated by Platform and delivered to the Partner through a secure channel. Each Partner receives a distinct secret so that a compromise is isolated to a single integration. On the Platform side it is held in a secrets manager and is never stored in source code or in repositories; the Partner is expected to protect its copy to the same standard.6.1 Response semantics
7. Appendix A — Verifiable Test Vector
Use the following vector to confirm your implementation before go-live. Your computed signature must equal the expected value exactly.
Reproduce from a shell:
8. Appendix B — Frequently Asked Questions
My signature almost works but fails intermittently. Why?
My signature almost works but fails intermittently. Why?
Almost always a canonicalization issue (see 3.2). Confirm you are hashing the raw body buffer and not a re-serialized object. Watch for a framework that trims whitespace, reorders keys, or rewrites numbers/dates.
Should the secret be treated as text or bytes?
Should the secret be treated as text or bytes?
As the HMAC key, the secret is used as its ASCII byte representation. Provision secrets using ASCII-safe characters to avoid any ambiguity.
Is Base64 URL-safe or standard?
Is Base64 URL-safe or standard?
Standard Base64 (with
+, / and = padding), as produced by Convert.ToBase64String.