身份验证概述
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。