# 安全校验

# API调试参数

  • URL: 获取游戏入口Url,商务沟通后提供
  • merchant_id: 商务沟通后提供
  • secretkey: 商务沟通后提供

# 加密解密方式

2.1. 请求和响应数据格式

平台访问游戏方API的结构:
header:
x-game-merchant-id:"123"
原始数据:
{
    "merchant_id":"123"
    "account":"123456"
    "currency":"USD"
}
加解密使用的secretKey: D2434fgdgfdgfg12
则首先对这个json串进行加密,假设有以下加密结果:
加密后发送:
{
    "data": 加密后的数据(HykjzxldgwfSIYc6nyvlUxkqmCR8TEzeyYVxQmAXvk6ZzHtu38ReQml6F9oMI2cc)
}
游戏方返回给平台方的结构:
{
    "errno" string 请查阅文档的错误码
    "errmsg" string 错误描述
    "data": 加密后的数据(HykjzxldgwfSIYc6nyvlUxkqmCR8TEzeyYVxQmAXvk6ZzHtu38ReQml6F9oMI2cc)
}
响应data解密结果: {"account":"123456","merchant_id":"123", "currency":"USD"}

# 代码SDK(JAVA)

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Base64;

/**
 * CIA Java AES/CBC/PKCS5Padding 加密
 */
public class AESUtil {
    public static String Encrypt(String sSrc, String sKey) {
        String password = sKey.substring(0, 16);
        try {
            if (password == null) {
                System.out.print("Key为空null");
                return null;
            }
            // 判断Key是否为16位
            if (password.length() != 16) {
                System.out.print("Key长度不是16位");
                return null;
            }
            byte[] raw = password.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式"
            IvParameterSpec iv = new IvParameterSpec(password.getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(sSrc.getBytes());
            String encrypteds = Base64.getEncoder().encodeToString(encrypted);
            return URLDecoder.decode(URLEncoder.encode(encrypteds, "UTF-8"), "UTF-8");// 通过Base64转码返回
        } catch (Exception ex) {
            System.out.println(ex);
            return null;
        }
    }


    public static String Decrypt(String URLDecoders, String sKey) {
        String password = sKey.substring(0, 16);
        try {
            // 判断Key是否正确
            if (password == null) {
                System.out.print("Key为空null");
                return null;
            }
            // 判断Key是否为16位
            if (password.length() != 16) {
                System.out.print("Key长度不是16位");
                return null;
            }
            byte[] raw = password.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(password.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = Base64.getDecoder().decode(URLDecoders);//先用base64解密
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original);
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

    public static void main(String[] args) {

        String jsonString = "{\"game_name\":\"百家乐\",\"desk_name\":\"百家乐002\"}";
        String en = Encrypt(jsonString, "uZlAJM2YgUHBQqtUQuoCNCGGoeMdbd4hw6u1tmWL");
        System.out.println("加密:" + en);

        String decr = Decrypt(en, "uZlAJM2YgUHBQqtUQuoCNCGGoeMdbd4hw6u1tmWL");
        System.out.println("解密:" + decr);
    }
}

# 代码SDK(golang)

package secret

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
)

func AesEncrypt(sSrc string, sKey string) (string, error) {
	if len(sKey) < 16 {
		return "", fmt.Errorf("Key长度不是16字节")
	}

	password := sKey[:16]
	key := []byte(password)
	plaintext := []byte(sSrc)

	// 判断Key长度是否为16字节
	if len(key) != 16 {
		return "", fmt.Errorf("Key长度不是16字节")
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	// 判断输入字符串是否为空
	if len(plaintext) == 0 {
		return "", fmt.Errorf("输入字符串为空")
	}

	// PKCS5Padding 填充
	blockSize := block.BlockSize()
	padding := blockSize - len(plaintext)%blockSize
	padText := make([]byte, len(plaintext)+padding)
	copy(padText, plaintext)
	for i := len(plaintext); i < len(padText); i++ {
		padText[i] = byte(padding)
	}

	ciphertext := make([]byte, len(padText))

	iv := []byte(password)

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext, padText)

	encoded := base64.StdEncoding.EncodeToString(ciphertext)
	return encoded, nil
}

func AesDecrypt(decoders string, sKey string) (string, error) {
	if len(sKey) < 16 {
		return "", fmt.Errorf("Key长度不是16字节")
	}
	password := sKey[:16]
	key := []byte(password)
	enc, err := base64.StdEncoding.DecodeString(decoders)
	if err != nil {
		return "", err
	}

	// 判断Key长度是否为16字节
	if len(key) != 16 {
		return "", fmt.Errorf("Key长度不是16字节")
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	if len(enc) == 0 {
		return "", fmt.Errorf("输入字符串为空")
	}

	if len(enc)%aes.BlockSize != 0 {
		return "", fmt.Errorf("输入字符串长度不是块大小的倍数")
	}

	iv := []byte(password)
	mode := cipher.NewCBCDecrypter(block, iv)

	mode.CryptBlocks(enc, enc)

	// 去除PKCS5Padding填充
	padding := int(enc[len(enc)-1])
	if padding > 0 && padding <= aes.BlockSize {
		enc = enc[:len(enc)-padding]
	}

	return string(enc), nil
}