Technical Documentation

HMAC Request Signing and Keypairs

97 views 0

The Zephr REST API is secured using keypair authentication. Once a keypair has been created against an Admin User, a request can be signed using an HMAC algorithm in order to allow the request to be executed with the role and identity of the Admin User who owns the keypair.

Creating a Keypair

Keypairs are managed under the user icon in the top right of the Zephr admin dashboard:

Click “Issue keypair” and take note of the secret key.

You will not be able to retrieve the secret key after it is initially display.

Keypairs created in the admin console allow an integration service to act on behalf of the user who generates them. For keypairs that are not linked to a user, contact

You can add notes to the keypair from the context menu in the list of keypairs.

You can also create keypairs via the REST API, if required:

POST /v3/admin/users/{user_id}/keypairs

Note that no body is required in this request.

The response will be:

  "access key...",   "secret_key": "secret key...",   
  "message": "Keypair created: you will not be able to recover the secret, so take note of it" 

The secret key can never be recovered so it is important to record the payload from this request and store it securely.

Signing a request

To execute a secure request you must provide an Authorization header with a request signature:

GET /v3/users 


  • ALGORITHM is “SHA256”
  • ACCESS_KEY is the access key from the keypair
  • TIMESTAMP is the current number of milliseconds since the Epoc
  • NONCE is a random string (usually a number) that is unique – if you make two requests you must change the nonce between them
  • HASH is a hash generated by digesting the following inputs into an SHA-256 message-digest algorithm and outputting the resulting hex value:
    • the secret key from the keypair
    • the request’s body
    • the request’s path (not including the host, e.g. “/v3/users”)
    • the request query (not including the “?”)
    • the request method, in capitals, i.e. “POST”, “PUT”, “GET”, “DELETE”
    • the timestamp which must exactly match the TIMESTAMP part of the signature
    • the nonce, which must exactly match the NONCE part of the signature

Older request signatures were of the format: BLAIZE-HMAC-{{ALGORITHM}} {{ACCESS_KEY}}:{{TIMESTAMP}}:{{NONCE}}:{{HASH}}

This format did not include the query as part of the hash. Usage of legacy signatures is discouraged.

Reference implementations


The Zephr HmacSigner is available as part of the Zephr API project. Please contact to arrange access or otherwise you may copy and distribute the following code without licence.


import io.blaize.api.exception.HmacException; 

import java.util.Objects;

public class HmacSigner { 

    private final String algorithm;

    public HmacSigner(String algorithm) { 
        if ("SHA256".equals(algorithm)) {
            this.algorithm = "SHA-256";
        } else {
            this.algorithm = algorithm;

    public String signRequest(String secretKey, String body, String path, String method, 
String timestamp, String nonce) throws HmacException {


        try {

            MessageDigest messageDigest = 


            byte[] digest = messageDigest.digest();

            StringBuffer hash = new StringBuffer();

 for (byte digestByte : digest) {

                Integer unsignedInteger = new 
             return hash.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new HmacException(e);



Then a signature can be used as follows:

The following code is non-normative and only intended as a reference.

String protocol = "http";
String host = "";
String path = "/v3/users";
String method = "POST";
String body = "{\"identifiers\": { \"email_adress\": \"\" }, \"validators\": { \"password\": \"sup3rsecret\" }}";

String accessKey = "x";
String secretKey = loadSecretKeySecurely(accessKey);

String timestamp = String.valueOf(new Date().getTime());
String nonce = UUID.randomUUID().toString();

String hash = new HmacSigner("SHA-256").
signRequest(secretKey, body, path, method, timestamp, nonce);

String authorizationHeaderValue = "BLAIZE-HMAC-SHA256 "
+ inputs.compute("accessKey", this::replaceVariables) + ":" + timestamp + ":" + nonce + ":" + hash;

// This is a standard library implementation for illustration only
HttpURLConnection connection = (HttpURLConnection) new URL(protocol + "://" + host + path).openConnection();
connection.addRequestProperty("Authorization", authorizationHeaderValue);

DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());

int status = connection.getResponseCode();
if (this.status >= 200 && this.status < 400) {
System.out.println(new BufferedReader(new InputStreamReader(connection.getInputStream())).lines().collect(Collectors.joining("\n")));
} else {