Docs MenuTap to expand ▼
HomeOpen PlatformDocsAuth & Signature

Auth & Signature

Awice Logistics Open Platform uses industry-standard HMAC-SHA256 signing + IP whitelist dual authentication to protect AppKeys at the protocol level. This page details the signing algorithm and common pitfalls.

Signature Formula

Signature = HMAC-SHA256(
    AppSecret,
    METHOD + "\n" + PATH + "\n" + Timestamp + "\n" + Nonce + "\n" + MD5(BODY)
)

Concatenate the 5 fields with a newline character (\n i.e. byte 0x0A) into a message string. Then use AppSecret as the key and run HMAC-SHA256 over the message string. The output is a 64-char lowercase hex string used as the signature.

Request Header Spec

Every OpenAPI request must carry the following 4 HTTP headers:

HeaderRequiredDescription
X-Awice-AppKeyYesThe AppKey created in the console
X-Awice-TimestampYesUnix timestamp in seconds, tolerance ±300s
X-Awice-NonceYes8-32 byte random string, unique within 10 minutes
X-Awice-SignatureYesHMAC-SHA256 signature (64-char lowercase hex)

Field Computation Details

  • METHOD:Uppercase HTTP method (POST / GET), without the HTTP version.
  • PATH:Request path, without the query string, without the host. For example /openapi/v1/freight/fcl/search. Note: the request URL is https://www.5688.cn/api + PATH, but only PATH is used in the signature.
  • Timestamp:Unix timestamp in seconds. The server tolerates ±300s of client clock drift. Fetch it fresh per request.
  • Nonce:A unique random string per request. The server caches all nonces seen in the last 10 minutes. Reuse returns 2004.
  • MD5(BODY):32-char lowercase hex MD5 of the request body.
    • POST/PUT: MD5 the raw JSON string (watch whitespace, ensure stable field order)
    • GET/DELETE: BODY is an empty string, md5("") = d41d8cd98f00b204e9800998ecf8427e

Common Causes of Signature Errors

If you receive 2002 signature failure, 95% of the time it's one of these:

  1. Clock not synced:Cloud server / local NTP is off, time drift > 5 minutes. You'd actually get 2003, not 2002. Check system time with date.
  2. Path includes query string:Only PATH may be used in the signature. Wrong: /openapi/v1/x?a=1 Right: /openapi/v1/x.
  3. JSON serialization is inconsistent:Client uses json.dumps() with default whitespace vs without — the MD5 differs entirely. Use json.dumps(..., separators=(',', ':')) for compact format. The server MD5s the raw body you send (it does not re-serialize).
  4. AppSecret contains stray whitespace:Pasted from the console with a trailing newline or space; trim() it in your code.
  5. Wrong METHOD case:Must be POST (uppercase), not post.

Multi-language Signature Helpers

PHP

<?php
function buildSignature(string $method, string $path, string $body, string $appSecret): array {
    $ts    = (string)time();
    $nonce = bin2hex(random_bytes(8));
    $msg   = strtoupper($method) . "\n" . $path . "\n" . $ts . "\n" . $nonce . "\n" . md5($body);
    $sign  = hash_hmac('sha256', $msg, $appSecret);
    return [
        'timestamp' => $ts,
        'nonce'     => $nonce,
        'signature' => $sign,
    ];
}

// Usage:
$body = json_encode(['pol' => 'CNSHA', 'pod' => 'USLSA']);
$hdrs = buildSignature('POST', '/openapi/v1/freight/fcl/search', $body, $appSecret);

Python

import hmac, hashlib, time, secrets

def build_signature(method: str, path: str, body: str, app_secret: str) -> dict:
    ts    = str(int(time.time()))
    nonce = secrets.token_hex(8)
    body_md5 = hashlib.md5(body.encode()).hexdigest()
    msg = f"{method.upper()}\n{path}\n{ts}\n{nonce}\n{body_md5}".encode()
    sig = hmac.new(app_secret.encode(), msg, hashlib.sha256).hexdigest()
    return {'timestamp': ts, 'nonce': nonce, 'signature': sig}

# Usage:
import json
body = json.dumps({'pol': 'CNSHA', 'pod': 'USLSA'})
hdrs = build_signature('POST', '/openapi/v1/freight/fcl/search', body, app_secret)

Node.js

const crypto = require('crypto');

function buildSignature(method, path, body, appSecret) {
  const ts    = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomBytes(8).toString('hex');
  const bodyMd5 = crypto.createHash('md5').update(body).digest('hex');
  const msg = `${method.toUpperCase()}\n${path}\n${ts}\n${nonce}\n${bodyMd5}`;
  const sig = crypto.createHmac('sha256', appSecret).update(msg).digest('hex');
  return { timestamp: ts, nonce, signature: sig };
}

// Usage:
const body = JSON.stringify({ pol: 'CNSHA', pod: 'USLSA' });
const hdrs = buildSignature('POST', '/openapi/v1/freight/fcl/search', body, appSecret);

Go

package awice

import (
    "crypto/hmac"
    "crypto/md5"
    "crypto/rand"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "strings"
    "time"
)

type SignHeaders struct {
    Timestamp string
    Nonce     string
    Signature string
}

func BuildSignature(method, path, body, appSecret string) (*SignHeaders, error) {
    ts := fmt.Sprintf("%d", time.Now().Unix())

    nonceBytes := make([]byte, 8)
    if _, err := rand.Read(nonceBytes); err != nil {
        return nil, err
    }
    nonce := hex.EncodeToString(nonceBytes)

    bodyMd5 := md5.Sum([]byte(body))
    bodyMd5Hex := hex.EncodeToString(bodyMd5[:])

    msg := strings.ToUpper(method) + "\n" + path + "\n" + ts + "\n" + nonce + "\n" + bodyMd5Hex
    h := hmac.New(sha256.New, []byte(appSecret))
    h.Write([]byte(msg))
    sig := hex.EncodeToString(h.Sum(nil))

    return &SignHeaders{Timestamp: ts, Nonce: nonce, Signature: sig}, nil
}

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.HexFormat;
import java.util.Map;

public class AwiceSigner {
    public static Map<String, String> buildSignature(
            String method, String path, String body, String appSecret) throws Exception {
        String ts = String.valueOf(System.currentTimeMillis() / 1000);

        byte[] nonceBytes = new byte[8];
        new SecureRandom().nextBytes(nonceBytes);
        String nonce = HexFormat.of().formatHex(nonceBytes);

        String bodyMd5 = HexFormat.of().formatHex(
            MessageDigest.getInstance("MD5").digest(body.getBytes(StandardCharsets.UTF_8)));

        String msg = method.toUpperCase() + "\n" + path + "\n" + ts + "\n" + nonce + "\n" + bodyMd5;

        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        String signature = HexFormat.of().formatHex(mac.doFinal(msg.getBytes(StandardCharsets.UTF_8)));

        return Map.of("timestamp", ts, "nonce", nonce, "signature", signature);
    }

    // Usage:
    // String body = "{ \"pol\":\"CNSHA\",\"pod\":\"USLSA\" }";
    // Map<String,String> hdrs = buildSignature("POST", "/openapi/v1/freight/fcl/search", body, appSecret);
}
Auth & Signature - Awice Logistics Open Platform Docs | 5688.cn