身份驗證概述
VertexPlay API 使用多層身份驗證機制來確保 API 請求的安全性。
驗證流程
請求標頭
所有 API 請求都必須包含以下標頭:
驗證請求 (/v2/auth)
| 標頭 | 類型 | 必填 | 說明 |
|---|---|---|---|
Content-Type | string | 是 | 必須為 application/json |
x-agentid | string | 是 | 營運商代理 ID |
x-timestamp | string | 是 | Unix 時間戳(毫秒) |
x-nonce | string | 是 | 32 字元隨機字符串(用於防重放攻擊) |
x-signature | string | 是 | SHA256 簽章 |
其他 API 請求
| 標頭 | 類型 | 必填 | 說明 |
|---|---|---|---|
Authorization | string | 是 | Bearer token,格式:Bearer {accessToken} |
Content-Type | string | 是 | 必須為 application/json |
x-agentid | string | 是 | 營運商代理 ID |
x-timestamp | string | 是 | Unix 時間戳(毫秒) |
x-nonce | string | 是 | 32 字元隨機字符串 |
x-signature | string | 是 | SHA256 簽章 |
簽章生成
簽章使用 SHA256 算法生成,確保請求的完整性和真實性。
簽章步驟
-
準備簽章字符串:將以下參數按順序連接
agentId + timestamp + nonce + requestBody -
生成簽章:使用 SHA256 算法
signature = SHA256(signatureString) -
添加到請求標頭:將生成的簽章放入
x-signature標頭
Node.js 範例
const crypto = require('crypto');
function generateSignature(agentId, timestamp, nonce, requestBody) {
// 將請求體轉為字符串
const bodyString = JSON.stringify(requestBody);
// 組合簽章字符串
const signatureString = agentId + timestamp + nonce + bodyString;
// 生成 SHA256 簽章
const signature = crypto
.createHash('sha256')
.update(signatureString)
.digest('hex');
return signature;
}
// 使用範例
const agentId = 'integratorNBTest04';
const timestamp = Date.now().toString();
const nonce = crypto.randomBytes(16).toString('hex');
const requestBody = {
cipherText: 'G0ZMDELeJwx+7JcIfIFO...'
};
const signature = generateSignature(agentId, timestamp, nonce, requestBody);
Python 範例
import hashlib
import json
import time
import secrets
def generate_signature(agent_id, timestamp, nonce, request_body):
# 將請求體轉為字符串
body_string = json.dumps(request_body, separators=(',', ':'))
# 組合簽章字符串
signature_string = agent_id + timestamp + nonce + body_string
# 生成 SHA256 簽章
signature = hashlib.sha256(
signature_string.encode('utf-8')
).hexdigest()
return signature
# 使用範例
agent_id = 'integratorNBTest04'
timestamp = str(int(time.time() * 1000))
nonce = secrets.token_hex(16)
request_body = {
'cipherText': 'G0ZMDELeJwx+7JcIfIFO...'
}
signature = generate_signature(agent_id, timestamp, nonce, request_body)
數據加密
所有請求的敏感數據都使用 AES-256-GCM 加密,響應為明文 JSON 格式。
加密格式
{
"cipherText": "ivBase64(16字元) + authTagBase64(24字元) + encryptedDataBase64"
}
加密參數
- 算法:AES-256-GCM
- 密鑰長度:256 位(32 字節)
- IV 長度:96 位(12 字節)
- 認證標籤長度:128 位(16 字節)
cipherText 結構說明
cipherText 由三個部分組成,全部使用 base64 編碼:
-
IV (Initialization Vector)
- 長度:12 bytes
- Base64 編碼後:16 字元
- 位置:
cipherText[0:16]
-
Auth Tag (認證標籤)
- 長度:16 bytes
- Base64 編碼後:24 字元
- 位置:
cipherText[16:40]
-
Encrypted Data (加密數據)
- 長度:變長,取決於原始數據
- Base64 編碼
- 位置:
cipherText[40:]
解密範例 (Node.js)
const crypto = require('crypto');
const AES_ALGORITHM = 'aes-256-gcm';
/**
* 解密 cipherText
* @param {string} key - AES 密鑰 (hex 格式)
* @param {string} cipherText - 加密文本
* @returns {Object} - 解密後的 JSON 物件
*/
function symmetricDecrypt(key, cipherText) {
try {
const aesKey = Buffer.from(key, 'hex');
// 從密文分離 iv, authTag 和加密資料
const ivLength = 16; // base64 編碼的 12 bytes
const authTagLength = 24; // base64 編碼的 16 bytes
const ivBase64 = cipherText.substring(0, ivLength);
const authTagBase64 = cipherText.substring(ivLength, ivLength + authTagLength);
const encryptedData = cipherText.substring(ivLength + authTagLength);
const aesIv = Buffer.from(ivBase64, 'base64');
const authTag = Buffer.from(authTagBase64, 'base64');
// 建立解密器
const decipher = crypto.createDecipheriv(AES_ALGORITHM, aesKey, aesIv);
decipher.setAuthTag(authTag); // 設置認證標籤,GCM 會自動驗證完整性
// 解密
let decrypted = decipher.update(encryptedData, 'base64', 'utf8');
decrypted += decipher.final('utf8'); // 如果 authTag 驗證失敗,這裡會拋出錯誤
return JSON.parse(decrypted);
} catch (e) {
throw new Error(`Symmetric Decryption Failed: ${e.message}`);
}
}
// 使用範例
const key = 'your-32-byte-hex-key'; // 64 個 hex 字元 = 32 bytes
const cipherText = 'G0ZMDELeJwx+7JcI...'; // 完整的 cipherText
try {
const decryptedData = symmetricDecrypt(key, cipherText);
console.log('Decrypted data:', decryptedData);
} catch (error) {
console.error('Decryption error:', error.message);
}
加密範例 (Node.js)
/**
* 加密數據
* @param {string} key - AES 密鑰 (hex 格式)
* @param {Object} data - 要加密的 JSON 物件
* @returns {string} - 加密後的 cipherText
*/
function symmetricEncrypt(key, data) {
try {
const aesKey = Buffer.from(key, 'hex');
// 生成隨機 IV (12 bytes)
const aesIv = crypto.randomBytes(12);
// 建立加密器
const cipher = crypto.createCipheriv(AES_ALGORITHM, aesKey, aesIv);
// 加密數據
const jsonString = JSON.stringify(data);
let encrypted = cipher.update(jsonString, 'utf8', 'base64');
encrypted += cipher.final('base64');
// 獲取認證標籤 (16 bytes)
const authTag = cipher.getAuthTag();
// 組合 cipherText: ivBase64 + authTagBase64 + encryptedData
const cipherText = aesIv.toString('base64') +
authTag.toString('base64') +
encrypted;
return cipherText;
} catch (e) {
throw new Error(`Symmetric Encryption Failed: ${e.message}`);
}
}
// 使用範例
const data = {
username: 'player001',
amount: 100
};
const cipherText = symmetricEncrypt(key, data);
console.log('Encrypted cipherText:', cipherText);
錯誤處理
驗證錯誤碼
| 狀態碼 | 說明 |
|---|---|
| 84 | 解密失敗 - 可能是密鑰錯誤或數據損壞 |
| 83 | 認證失敗 - 簽章驗證失敗或參數錯誤 |
錯誤響應格式
{
"code": 83,
"message": "Signature verification failed",
"logUUID": "6589bf8d-fe74-48bd-841a-71bf8f848f86"
}
安全建議
- 保護 Secret Key:絕不在客戶端代碼或公開位置存儲 Secret Key
- 使用 HTTPS:所有 API 請求必須通過 HTTPS 傳輸
- 時間戳驗證:確保時間戳在合理範圍內(建議 ±1 分鐘)
- Nonce 唯一性:每個請求使用唯一的 nonce 防止重放攻擊
- Token 管理:定期刷新 accessToken,不要長期使用同一個 token
- 錯誤處理:妥善處理認證錯誤,避免洩露敏感信息
下一步
了解如何獲取 Access Token 來開始使用 API。