Skip to content

接口安全通讯规则 ​

网络货运相关接口数据传输需经过数据加密及数据签名两个过程,以确保数据在传递时的安全性。

以下是加密后发送数据的请求格式:

POST 请求方式:

json
{
  "body": "加密数据",
  "sign": "签名",
  "timestamp": "时间戳"
}

GET 请求方式:

body=加密数据&sign=签名&timestamp=时间戳

数据加解密 ​

使用AES进行数据加密,密钥需在平台生成,一般在菜单「API安全」-> 「密钥管理」中获取(clientSecret)。

POST请求:Json格式数据可转为字符串,然后使用 clientSecret 作为密钥,进行加密。

GET请求:Url参数格式数据,如:b=1&a=2(无需排序),直接使用 clientSecret 作为密钥,进行加密。

返回的数据如果是密文,可使用 clientSecret 作为密钥,进行解密。

数据签名 ​

使用标准签名算法(SHA256WithRSA)进行数据签名,RSA密钥对由客户自行生成,然后填写RSA公钥到平台。一般在菜单「API安全」-> 「密钥管理」RSA公钥处填写。

签名算法:

1、获取加密后的body;

2、获取系统时间戳;

3、将步骤1、2得到的结果,用 # 连接;

4、将步骤3得到的结果,进行MD5摘要计算;

5、将步骤4得到的结果,用RSA私钥进行签名。

请求时效 ​

请求在 10 分钟内有效,超过 10 分钟后,请求将无法通过验证。

JAVA代码示例 ​

java

import com.alibaba.fastjson2.JSONObject;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SignUtils {

    public static void main(String[] args) throws Exception {
        //在菜单「API安全」-> 「密钥管理」中获取(clientSecret)。
        String clientSecret = "YqW641o8PsHejCDN...";
        //私钥字符串(Base64 编码格式)
        String privateKey = "MIIEugIBADANBgkqhkiG9w0BAQEFAASCBK........";

        //待发送的原始数据参数
        JSONObject data = new JSONObject();
        data.put("r1", "123");
        data.put("a1", "456");

        //数据加密
        String encrypted = aesEncrypt(data.toString(), clientSecret);

        long timestamp = System.currentTimeMillis();
        JSONObject request = new JSONObject();
        request.put("body", encrypted);
        request.put("sign", signBody(encrypted, timestamp, privateKey));
        request.put("timestamp", timestamp);

        // 以下是发送到服务器的数据POST格式
        System.out.println("Signed JSON: " + request.toJSONString());

        // 发送请求示例(假设使用 HttpClient)
        HttpUtil.createPost("https://www.xxyb.cn/v4/api/xxx")
                .header("token", token)
                .body(request.toString())
                .execute()
                .body();
    }

    /**
     * AES加密
     */
    public static String aesEncrypt(String content, String clientSecret) throws Exception {
        byte[] key = clientSecret.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] combined = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(combined);
    }

    /**
     * AES解密
     */
    public static String aesDecrypt(String encryptContent, String clientSecret) throws Exception {
        byte[] key = clientSecret.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptContent);
        byte[] original = cipher.doFinal(encryptedBytes);
        return new String(original, StandardCharsets.UTF_8);
    }

    /**
     * 数据签名
     *
     * @param encryptContent 加密后的请求数据
     * @param timestamp      时间戳
     * @param privateKeyStr  私钥字符串(Base64 编码)
     * @return 签名
     */
    public static String signBody(String encryptContent, long timestamp, String privateKeyStr) throws Exception {
        // 签名内容
        String message = encryptContent + "#" + timestamp;
        // MD5摘要计算
        byte[] hash = md5(message).getBytes(StandardCharsets.UTF_8);

        // 加载私钥
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        // 使用 SHA256withRSA 签名算法
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(hash);
        // 生成签名
        byte[] signBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signBytes);
    }

    /**
     * 计算MD5值
     *
     * @param data 数据
     * @return MD5摘要值
     */
    public static String md5(String data) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8));
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}