Skip to content

Ký request ​

Cách Cake Mobile SDK ký các request của nó đến backend eKYC và FaceAuthen: HMAC-SHA256 trên một timestamp và request body, dùng JWT token riêng cho từng giao dịch làm secret key. Đây là một scheme khác với header ATV <ACCESS_ID>:<SIGNATURE> của backend. Trên các tenant FaceAuthen đã bật kiểm tra signature của thiết bị, SDK còn ký thêm bằng một khóa thiết bị đã đăng ký — xem Signature của thiết bị.

Mục đích ​

Thông tinMô tả
Xác thựcXác minh request thực sự đến từ một client được ủy quyền.
Toàn vẹnĐảm bảo request body không bị can thiệp trong quá trình truyền.
Chống replayTimestamp giới hạn thời gian một request bị chặn lại còn có thể dùng được.

Input để ký ​

Tham sốKiểuBắt buộcMặc địnhMô tả
timestampstringCó—Timestamp theo RFC3339, ví dụ 2019-04-21T18:00:15+07:00. Được gửi trong header X-TV-Timestamp.
requestBodystringCó—Request body thô. Hoạt động với cả JSON và multipart form data.
jwtTokenstringCó—Token từ Tạo giao dịch, dùng làm HMAC secret key.

WARNING

JWT chính là khóa ký, nên nó là một credential, không chỉ là một định danh. Không bao giờ ghi log, không bao giờ lưu giữ nó quá phạm vi giao dịch, và không bao giờ gửi nó cho bên thứ ba.

Thuật toán ​

text
StringToSign = timestamp + "\n" + hex_encode(SHA256(requestBody))
Signature    = Base64(HmacSha256(JWTToken, Utf8EncodingOf(StringToSign)))
  1. Băm (hash) request body thô bằng SHA256 và mã hóa hex.
  2. Nối timestamp và hash đó bằng một dấu xuống dòng.
  3. Mã hóa UTF-8 kết quả.
  4. Áp dụng HMAC-SHA256 dùng JWT token làm khóa.
  5. Mã hóa Base64 digest thu được.

JavaScript ​

javascript
function signRequest(requestBody, jwtToken) {
  // Generate RFC3339 timestamp
  const date = new Date();
  const timestamp = date.toISOString();

  // Generate hashed body
  const hashedBody = CryptoJS.SHA256(requestBody).toString(CryptoJS.enc.Hex);

  // Create string to sign
  const stringToSign = `${timestamp}\n${hashedBody}`;

  // Generate HMAC-SHA256 signature
  const hmac = CryptoJS.HmacSHA256(stringToSign, jwtToken);

  // Convert to hex and then Base64 encode
  const hexHash = hmac.toString(CryptoJS.enc.Hex);
  const signature = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(hexHash));

  return {
    timestamp: timestamp,
    signature: signature
  };
}

Go ​

go
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/hex"
    "time"
)

func signRequest(requestBody, jwtToken string) (string, string, error) {
    // Generate RFC3339 timestamp
    timestamp := time.Now().Format(time.RFC3339)

    // Generate hashed body
    hashFunc := sha256.New()
    if _, err := hashFunc.Write([]byte(requestBody)); err != nil {
        return "", "", err
    }
    hashedBody := hex.EncodeToString(hashFunc.Sum(nil))

    // Create string to sign
    stringToSign := timestamp + "\n" + hashedBody

    // Generate HMAC-SHA256 signature
    h := hmac.New(sha256.New, []byte(jwtToken))
    h.Write([]byte(stringToSign))
    signature := base64.StdEncoding.EncodeToString(h.Sum(nil))

    return timestamp, signature, nil
}

Header của request ​

HeaderBắt buộcMô tả
X-TV-TimestampCóTimestamp RFC3339 dùng trong StringToSign.
X-SignatureCóSignature đã mã hóa Base64.
AuthorizationCóBearer {jwt_token}.
http
POST /api/face-auth HTTP/1.1
Host: <HOSTNAME>
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
X-TV-Timestamp: <TIMESTAMP>
X-Signature: <SIGNATURE>

{
    "cus_user_id": "user_00000001",
    "ref_id": "ref_00000001"
}

Xác minh phía server ​

Backend xác minh mỗi request bằng cách:

  1. Trích xuất X-TV-Timestamp và X-Signature từ request.
  2. Tạo lại signature dự kiến bằng cùng thuật toán.
  3. So sánh bằng một phép so sánh constant-time, để tránh làm lộ thông tin qua thời gian xử lý.
  4. Chỉ tiếp tục nếu các signature khớp nhau.

Signature của thiết bị ​

Trên tenant đã bật kiểm tra signature của thiết bị (device signature), Mobile SDK còn ký mỗi request mà nó gửi bằng token giao dịch, dùng khóa thiết bị đã đăng ký qua Khởi tạo khóa ký và Xác nhận khóa ký. SDK thêm ba header:

HeaderBắt buộcMô tả
Device-IdĐi kèm các header signaturedevice_id mà khóa ký (signing credential) đã được đăng ký cho. Phải khớp chính xác với device_id đã lưu của khóa ký (phân biệt hoa thường).
X-Device-Key-IdXem bên dướiID của khóa ký — key_id từ Init và Confirm. Khóa ký phải ở trạng thái ACTIVE.
X-Device-SignatureXem bên dướiChuỗi JWS dạng compact (typ: cake-request-proof+jwt, alg: ES256) do SDK tạo trên audience của môi trường, method, URL path đúng như tài liệu (ví dụ /v1/face_auth, không có tiền tố gateway), Content-Type, hash của body, hash của token, thời điểm phát hành và một jti duy nhất.
http
POST /v1/face_auth HTTP/1.1
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
Device-Id: <DEVICE_ID>
X-Device-Key-Id: <DEVICE_KEY_ID>
X-Device-Signature: <DEVICE_SIGNATURE>

Các header này là bắt buộc khi Device-Id được gửi có một khóa ký ACTIVE, hoặc khi phiên bản SDK bằng hoặc cao hơn phiên bản bắt đầu áp dụng việc ký. Chúng áp dụng cho mọi request của SDK dùng token giao dịch: Xác minh Face Authen (POST /v1/face_auth), Tải ảnh lên (POST /v1/images, ký trên body multipart thô) và POST /v1/get_config.

  • Request bị từ chối với access_denied_exception (401100) khi signature bị thiếu, không hợp lệ, quá hạn hoặc bị dùng lại (replay), hoặc khi khóa ký mà X-Device-Key-Id chỉ tới không ở trạng thái ACTIVE hay được đăng ký cho một Device-Id khác.
  • Khi phiên bản SDK tối thiểu đã được thiết lập, request từ một SDK cũ hơn, hoặc không có header Sdk-Version, bị từ chối với sdk_version_unsupported (2009005).
  • Body vượt giới hạn kích thước bị từ chối với invalid_parameter_exception (4001000) khi signature là bắt buộc.

WARNING

Signature của thiết bị bảo vệ request trên đường truyền; nó không làm cho kết quả mà ứng dụng hiển thị trở nên đáng tin cậy. Hãy xác nhận mọi kết quả xác thực từ backend của bạn bằng Xác minh khóa xác thực giao dịch.

Bước tiếp theo ​

Developers