VandosPay Docs

报文加密

请求报文 AES 加密规则与 Java 参考实现

本系统对请求报文体进行全报文加密。业务参数先序列化为 JSON 字符串,再用本次请求随机生成的 trk 做 AES 加密,最后 Base64 编码得到 payload。

加密链

  1. 生成 128 位随机 AES 密钥,得到原始字节 trkBytes。
  2. 用平台下发的 RSA 公钥加密 trkBytes,再 Base64 编码,作为报文公共字段 trk 上送。
  3. 将业务参数序列化为 UTF-8 JSON 字符串 plainJson。
  4. 用 trkBytes 对 plainJson 做 AES 加密。
  5. 对 AES 密文做 Base64 编码,作为报文公共字段 payload 上送。
businessParams = { ... }
plainJson     = JSON.stringify(businessParams)
trkBytes      = randomBytes(16)
trk           = Base64(RSA_ECB_PKCS1Padding(platformPublicKey, trkBytes))
payload       = Base64(AES_CBC_PKCS5Padding(trkBytes, utf8(plainJson)))

trk 同时用于解密本次请求对应的响应 payload,因此客户端需要保留本次请求生成的 trkBytes。

AES 参数

参数值
算法/模式/补位AES/CBC/PKCS5Padding
密钥长度128 bit(16 字节)
IVtrkBytes 原始字节(16 字节)
密钥派生以 trkBytes 为种子,使用 SHA1PRNG + AES KeyGenerator(128) 生成实际 AES 密钥

上述密钥派生方式依赖 Java 的 SHA1PRNG 与 KeyGenerator 组合,属于 Java 特有实现。其他语言接入时请先复刻该逻辑,或直接使用官方 SDK。

传输密钥本身使用 RSA/ECB/PKCS1Padding 加密,公钥为平台下发的 RSA 公钥。

Java 参考实现

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.Base64;

public final class TransportCrypto {

    private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";

    public static String encryptPayload(byte[] trk, String plainJson) throws GeneralSecurityException {
        byte[] ciphertext = aes(trk, plainJson.getBytes(StandardCharsets.UTF_8), Cipher.ENCRYPT_MODE);
        return Base64.getEncoder().encodeToString(ciphertext);
    }

    public static String decryptPayload(byte[] trk, String base64Ciphertext) throws GeneralSecurityException {
        byte[] ciphertext = Base64.getDecoder().decode(base64Ciphertext);
        return new String(aes(trk, ciphertext, Cipher.DECRYPT_MODE), StandardCharsets.UTF_8);
    }

    public static String encryptTrk(PublicKey platformPublicKey, byte[] trk) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, platformPublicKey);
        return Base64.getEncoder().encodeToString(cipher.doFinal(trk));
    }

    private static byte[] aes(byte[] trk, byte[] input, int mode) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        cipher.init(mode, derivedAesKey(trk), new IvParameterSpec(trk));
        return cipher.doFinal(input);
    }

    private static SecretKeySpec derivedAesKey(byte[] trk) throws GeneralSecurityException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(trk);
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128, random);
        return new SecretKeySpec(keyGenerator.generateKey().getEncoded(), "AES");
    }
}

本页目录