1. 获取RSA秘钥对
签名验签过程需要两对RSA秘钥:
小7私钥 + 小7公钥:小7公钥在小7手游开放平台游戏管理>游戏详情页面获取。
游戏私钥 + 游戏公钥:游戏密钥对需要自行生成(可使用支付宝开放平台开发助手生成),公钥提交给小7对接人员进行配置,私钥妥善保管。
1. 发送请求给小7时(游戏Server => 小7Server):使用游戏私钥给请求参数签名,对小7接口返回的响应数据使用小7公钥进行验签。
2. 接收小7请求时(小7Server => 游戏Server):使用小7公钥对接收到的请求参数进行验签,响应请求时使用游戏私钥签名。
2. 签名验签规则及流程
① 请求与响应生成的签名的规则是不同的
对于请求方向为游戏Server => 小7Server的接口,发送请求给小7时,需要使用Request对应的参数与游戏私钥生成签名,接收到小7响应时,需要使用小7响应内容Response中的字段与小7公钥进行验签;
对于请求方向为小7Server => 游戏Server的接口,接收到小7请求时,需要使用小7请求体Request中对应的参数与小7公钥进行验签,对请求进行响应时,需要使用Response中的字段与游戏私钥生成签名;
② payload生成
发送请求(Request)生成签名所需的payload拼接生成方式如下:
POST
+
空格
+
$apiMethod
+
@
+
$appkey
+
#
+
$gameType
+
.
+
$reqTime
+
\n\n
+
$bizParams
响应请求(Response)生成签名所需的payload拼接生成方式如下:
POST
+
空格
+
$apiMethod
+
@
+
$appkey
+
#
+
$gameType
+
.
+
$respTime
+
\n\n
+
$bizResp
生成示例:
POST x7mall.mallEntry@0b9ce7b64b02fb17cc948c0b9a6eb462#client.2025-04-21T00:25:20+0800{"roleId": "123", "guids": ["1234", "5678"], "guid": "", "serverId": ""}
③ 具体流程
a. 根据各接口定义获取业务参数bizParams或bizResp的json字符串,只需json_encode一次即可
b. 根据上述payload生成规则获取payload,注意请求和响应的payload是不一样的
c. 对请求签名时,将payload与游戏私钥使用openssl sha256算法进行运算生成原始签名,再将原始签名进行base64编码,得到最终signature的值
d. 验签时,将接收到signature进行base64解码,再将解码后的原始签名与payload一起使用小7公钥进行openssl sha256签名验证
④ 调试流程
签名验证不通过时,可参考以下流程排查问题:
a. 使用签名助手按照说明进行调试,确认可以调试通过;若不通过,请检查payload是否拼接正确,需要与签名助手结果窗口返回的payload一致。
b. 若是在响应小7请求时提示签名验证不通过,确认代码中使用的游戏私钥与在调试页面上填写的游戏公钥是否为一对秘钥;如果在调试页面未填写公钥进行调试,那么实际使用的游戏公钥为已提供给小7对接人员配置好的游戏公钥,可以与小7对接人员确认。
c. 若是对小7请求数据或响应数据进行签名验证不通过,确认代码中使用的小7公钥是否为开放平台对应游戏管理内页中提供的小7公钥。
3. Java代码参考
import org.apache.commons.codec.binary.Base64;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA cipher util
*
*/
public class RSA {
/**
* 加密算法RSA
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 签名算法
*/
public static final String SIGNATURE_ALGORITHM_SHA256 = "SHA256withRSA";
/**
*
* 用私钥对信息生成数字签名
*
*
* @param data 已加密数据
* @param privateKey 私钥(BASE64编码)
*
* @return
* @throws Exception
*/
public static String sign(byte[] data, String privateKey) throws Exception {
byte[] keyBytes = Base64.decodeBase64(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA256);
signature.initSign(privateK);
signature.update(data);
return Base64.encodeBase64String(signature.sign());
}
/**
*
* 校验数字签名
*
*
* @param data 已加密数据
* @param publicKey 公钥(BASE64编码)
* @param sign 数字签名
*
* @return
* @throws Exception
*
*/
public static boolean verify(byte[] data, String publicKey, String sign)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicK = keyFactory.generatePublic(keySpec);
//SIGNATURE_ALGORITHM
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM_SHA256);
signature.initVerify(publicK);
signature.update(data);
return signature.verify(Base64.decodeBase64(sign));
}
//=== Testing ===
public static void main(String[] args) {
try {
String payload = "POST x7mall.mallEntry@0b9ce7b64b02fb17cc948c0b9a6eb462#client.2021-01-13T14:54:52+0800" +
"\n\n" +
"{\"role\":{\"roleId\":\"\",\"guid\":\"\",\"roleName\":\"\",\"serverId\":\"\",\"serverName\":\"\",\"roleLevel\":\"\",\"roleCE\":\"\",\"roleStage\":\"\",\"roleRechargeAmount\":\"\"}}";
//私钥和公钥字符串,可从签名助手页面下载测试秘钥
String publicKey = "";
String privateKey = "";
//签名
String sign = sign(payload.getBytes(), privateKey);
//验签
Boolean passed = verify(payload.getBytes(), publicKey, sign);
System.out.println("payload=" + payload);
System.out.println("---------------------");
System.out.println("signature=" + sign);
System.out.println("---------------------");
System.out.println("passed ? " + (passed.equals(true)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. PHP代码参考
<?php
//------------------------------------------------
// 签名加签函数PHP代码参考
//------------------------------------------------
class Signature
{
/**
* 签名
*
* @param string $payload
* @param string $rsaPrivateKey
* @param int $algo
* @return string
*/
public static function sign($payload, $rsaPrivateKey, $algo = OPENSSL_ALGO_SHA256)
{
$formatPrivateKey = self::formatRsaPrivateKey($rsaPrivateKey);
openssl_sign($payload, $signature, $formatPrivateKey, $algo);
return base64_encode($signature);
}
/**
* 验签
*
* @param string $payload
* @param string $signature
* @param string $rsaPublicKey
* @param int $algo
* @return int
*/
public static function verify($payload, $signature, $rsaPublicKey, $algo = OPENSSL_ALGO_SHA256)
{
$rawSignature = base64_decode($signature);
$formatPublicKey = self::formatRsaPublicKey($rsaPublicKey);
return openssl_verify($payload, $rawSignature, $formatPublicKey, $algo);
}
/**
* 生成payload
*
* @param string $apiMethod
* @param string $appkey
* @param string $datetime
* @param string $body
* @param string $gameType
* @param string $method
* @return string
*/
public static function genPayload($apiMethod, $appkey, $datetime, $body, $gameType, $method = "POST")
{
$payload = $method . " " . $apiMethod . "@" . $appkey . "#" . $gameType . "."
. $datetime . "\n\n" . $body;
return $payload;
}
/**
* 格式化公钥
*
* @param string $publicKey
* @return string
*/
public static function formatRsaPublicKey($publicKey)
{
return "-----BEGIN PUBLIC KEY-----\r\n" . wordwrap($publicKey, 64, "\r\n", TRUE) . "\r\n-----END PUBLIC KEY-----";
}
/**
* 格式化私钥
*
* @param string $privateKey
* @return string
*/
public static function formatRsaPrivateKey($privateKey)
{
return "-----BEGIN RSA PRIVATE KEY-----\r\n" . wordwrap($privateKey, 64, "\r\n", TRUE) . "\r\n-----END RSA PRIVATE KEY-----";
}
}
$httpMethod = "POST";
$apiMethod = "x7mall.mallEntry";
$appkey = "0b9ce7b64b02fb17cc948c0b9a6eb462";
$reqTime = "2021-01-13T14:54:52+0800";
$gameType = "client";
$bizParams = '{"role":{"roleId":"","guid":"","roleName":"","serverId":"","serverName":"","roleLevel":"","roleCE":"","roleStage":"","roleRechargeAmount":""}}';
//RSA密钥对,可以从签名助手页面获取测试秘钥对
$rsaPrivateKey = "";
$rsaPublicKey = "";
//生成payload
$payload = Signature::genPayload($apiMethod, $appkey, $reqTime, $bizParams, $gameType);
//生成签名
$signature = Signature::sign($payload, $rsaPrivateKey);
//签名校验
$passed = Signature::verify($payload, $signature, $rsaPublicKey);
echo "--------------------------------------------------------------", PHP_EOL;
echo "payload=", $payload, PHP_EOL;
echo "--------------------------------------------------------------", PHP_EOL;
echo "signature=", $signature, PHP_EOL;
echo "--------------------------------------------------------------", PHP_EOL;
echo "passed=", $passed ? "true" : "false", PHP_EOL;