1. 安全参数说明
1.1 核心参数列表
| 参数名 | 说明 |
|---|---|
| APP_ID | 交易所指定标识 |
| APP_URL | 交易所法币交易页对外服务URL,由交易所指定 |
| APP_KEY | 通讯验证密钥 |
| APP_SECRET | 核心加密密钥,仅服务端存储 |
| APP_PASSPHRASE | 密码短语,仅服务端存储 |
1.2 安全要求
- 所有安全参数需妥善保存,严禁对外泄露;
- APP_SECRET、APP_PASSPHRASE仅限服务器端存储,禁止传递至前端或公开;
- 测试环境与生产环境参数隔离,不可交叉使用。
1.3 示例安全参数
APP_ID = "Bitrue"
APP_URL = "https://testnet.bitrue.com/otc"
APP_KEY = "Ae4fHfWZwFFi9q2l"
APP_SECRET = "9z5zpp0itm1ygx"
APP_PASSPHRASE = "SDFxw0EDj33sdZedd"
2. 安全参数获取流程
2.1 测试环境
直接联系Legend技术支持团队获取测试环境专属参数。
2.2 生产环境
服务正式上线前,向Legend技术支持团队申请,申请时需提供以下信息(均为交易所侧信息):
- 技术/项目负责人的公司邮箱(用于接收参数);
- 交易所正式官网URL;
- API回调地址(含trade-notify、trade-execute、balance、balances等);
- API回调使用的API Key;
- Legend服务在交易所的落地页(landing page)URL。
3. 后端核心开发要求
3.1 电子签名生成(必实现)
- 基于Legend提供的
OTCHandler.java加密算法代码开发; - 每次调用JSSDK前,必须计算生成
signature(签名)和timestamp(时间戳,秒级时间戳),如浏览器刷新当前页面,就需要重新生成签名; - 签名生成逻辑:基于
timestamp + APP_URL通过HmacSHA256算法结合APP_SECRET计算,结果做Base64编码。
执行示例:
bash-3.2$ java OTCHandler
>>> Exporting these params to HTML:
APP_ID:Bitrue
signature:uTHvD4z3r+o1se/CVxqvp1E7TU80F/ers7y9wo6Kp1w=
APP_URL:https://www.bitrue.com/
APP_KEY:Aw4fHfWZwFFi9q2l
APP_UID:sdfSEEC293Bd1ZZ5VO98z8Gf/z39m6RjSxhdOV9nW+8+6j9j+ZV2OOfXWwyChSJ3+xzRZulDyi:OTg3NjVmZWRjYmE0MzIxMA==
timestamp:15874263373.2 用户UID处理(必实现)
UID为交易所侧用户唯一标识,需经过两次加密后作为APP_UID传递,全程禁止明文传输:
- 第一次加密:交易所侧自行实现,推荐
sha1(user_id + salt),结果长度不超过20字符; - 第二次加密:使用Legend提供的官方加密算法(TBClient.encryptUID)再次加密,作为最终APP_UID。
- 核心要求:APP_UID必须保证用户唯一、永不重复,作为OTC平台用户唯一标识。
3.3 时间戳timestamp(必实现)
与电子签名配套生成,为秒级Unix时间戳,需与signature生成时间保持一致,不可篡改。
3.4 用户Email
需由前端在页面中配置为APP_EMAIL变量,后端需保证传递至前端的Email为用户真实有效信息。
4. 前后端参数交互
后端完成上述计算后,需将以下参数传递给前端页面,用于JSSDK调用:
APP_ID、APP_KEY、APP_URL、APP_UID、signature、timestamp
5. 加密算法实现(Java)
5.1 UID加密完整流程
// 第一步:交易所侧自行加密(示例)
String user_id = "交易所原始用户ID";
String salt = "交易所自定义盐值";
String encrypted_user_id = sha1(user_id + salt); // 长度≤20字符
// 第二步:使用Legend官方算法二次加密
String APP_UID = TBClient.encryptUID(encrypted_user_id);输出示例:
Original UID: 42fa5da9de51f2caa39217c4b8bece42ff6029cf
Encrypted UID: Bd1ZZ5VO98z8Gf/z39m6RjSxhdOV9nW+8+6j9j+ZV2OOfXWwyChSJ3+xzRZulDyi:OTg3NjVmZWRjYmE0MzIxMA==
5.2 核心算法代码
5.2.1 TBClient.java(核心加密/签名工具类)
/**
* Legend Gateway Java 客户端:包含签名生成、UID加密核心算法
*/
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.xml.bind.DatatypeConverter;
import java.util.Base64;
import java.time.Instant;
import java.util.*;
public class TBClient {
// 需替换为实际获取的安全参数
protected static final String APP_ID = "Bitrue";
protected static final String APP_URL = "https://www.bitrue.com/";
protected static final String APP_KEY = "Aw4fHfWZwFFi9q2l";
protected static final String APP_SECRET = "5z5znf230itm1ygx";
protected static final String APP_PASSPHRASE = "sdfSEEC293";
private static final String CIPHER_NAME = "AES/CBC/PKCS5PADDING";
private static final String CIPHER_VI = "98765fedcba43210";
private static final int CIPHER_KEY_LEN = 16; //128 bits
/**
* 生成请求签名(HmacSHA256)
* @param unixTime 秒级时间戳
* @return Base64编码后的签名
*/
public static String sign(long unixTime){
try {
String message = Long.toString(unixTime) + TBClient.APP_URL;
Mac hasher = Mac.getInstance("HmacSHA256");
hasher.init(new SecretKeySpec(TBClient.APP_SECRET.getBytes(), "HmacSHA256"));
byte[] hash = hasher.doFinal(message.getBytes());
return DatatypeConverter.printBase64Binary(hash);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
ex.printStackTrace();
}
return null;
}
/**
* UID二次加密(AES+CBC+PKCS5PADDING)
* @param UID 交易所侧一次加密后的UID
* @return 最终APP_UID
*/
public static String encryptUID(String UID){
String encrytedUID = TBClient.encrypt(TBClient.APP_SECRET, TBClient.CIPHER_VI, UID);
return TBClient.APP_PASSPHRASE + encrytedUID;
}
/**
* AES加密底层方法
* @param key 加密密钥(自动补0/截断至16位)
* @param iv 初始化向量
* @param data 待加密数据
* @return 加密后Base64串 + : + IV的Base64串
*/
public static String encrypt(String key, String iv, String data) {
try {
if (key.length() < CIPHER_KEY_LEN) {
key = String.format("%-" + CIPHER_KEY_LEN + "s", key).replace(' ', '0');
} else if (key.length() > CIPHER_KEY_LEN) {
key = key.substring(0, CIPHER_KEY_LEN);
}
IvParameterSpec initVector = new IvParameterSpec(iv.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, initVector);
byte[] encryptedData = cipher.doFinal((data.getBytes()));
String base64_EncryptedData = Base64.getEncoder().encodeToString(encryptedData);
String base64_IV = Base64.getEncoder().encodeToString(iv.getBytes("UTF-8"));
return base64_EncryptedData + ":" + base64_IV;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
// 测试方法
public static void testUID () {
String uid = "d58e3582afa99040e27b92b13c8f2280";
System.out.println("Original UID: " + uid);
System.out.println("Encrypted UID: " + TBClient.encryptUID(uid));
}
public static void testSig () {
long unixTime = Instant.now().getEpochSecond();
System.out.println("Signature: " + TBClient.sign(unixTime));
}
public static void main (String[] args) {
TBClient.testUID();
TBClient.testSig();
}
}5.2.2 OTCHandler.java(参数组装工具类)
/**
* 前端参数组装:生成JSSDK所需所有参数
*/
import java.util.*;
import java.time.Instant;
public class OTCHandler extends TBClient{
/**
* 组装前端所需所有参数
* @return 参数字典
*/
private static Map<String, String> htmlParams(){
Map<String, String> html_params = new HashMap<>();
long unixTime = Instant.now().getEpochSecond();
html_params.put("timestamp", Long.toString(unixTime));
html_params.put("signature", sign(unixTime));
html_params.put("APP_ID", APP_ID);
html_params.put("APP_URL", APP_URL);
html_params.put("APP_KEY", APP_KEY);
html_params.put("APP_UID", encryptUID(getUID()));
return html_params;
}
/**
* 需交易所自行实现:获取一次加密后的用户UID
* @return 交易所侧加密后的UID(长度≤20)
*/
private static String getUID(){
// 此处为示例,需替换为交易所实际业务逻辑
return "d58e3582afa99040e27b92b13c8f2280";
}
// 测试参数输出
public static void main (String[] args) {
Map<String, String> htmlParams = OTCHandler.htmlParams();
System.out.println(">>> Exporting these params to HTML: ");
htmlParams.forEach((k, v) -> System.out.println(k + ":" + v));
}
}6. 配置参数说明
Legend SDK可配置以下参数,需联系Legend商务团队申请修改:
- 结算方式:默认Legend与合作伙伴结算,合作伙伴与客户直接结算;
- 最小交易额:需调用
/app/pairs接口查询获取; - 支持交易对:默认支持USDT、ETH、BTC及所有Legend支持的法币交易对。
7. 开发核心规范
- 环境隔离:测试/生产环境参数独立,禁止交叉使用;
- 密钥安全:APP_SECRET、APP_PASSPHRASE仅可在服务器端存储,禁止写入前端代码、配置文件公开化;
- 算法规范:必须使用Legend提供的官方加密/签名代码,禁止自行修改算法逻辑;
- 唯一性保障:APP_UID需与交易所用户一一对应,永不重复,避免用户标识冲突;
- 参数一致性:signature与timestamp必须实时生成,一一对应,禁止复用、篡改时间戳;
- 异常处理:对加密、签名过程中的异常(如算法异常、密钥错误)做捕获处理,避免服务异常。
8. 技术支持
集成过程中遇到问题,直接联系Legend技术支持团队。
