# 10.其他
# 10.1 服务域名
域名用途 | 公网(外部研发联调、联运游戏上线使用) |
---|---|
中台域名 | https://open.kuaishou.com (opens new window) |
支付域名 | https://allin.kuaishoupay.com (opens new window) |
# 10.2 支付签名示例
# PHP
# 签名
<?php
$data = array(
"app_id" => "ks68xxxxxxxxxx756",
"channel_id" => "ks",
"role_id" => "10xxxxxxxx22",
"role_name" => "神仙",
"role_level" => 1,
"server_id" => "6",
"server_name" => "测试服务",
"product_id" => "11001",
"product_name" => "一零计划—60钻石包",
"product_desc" => "一零计划—60钻石包",
"money" => 100,
"currency_type" => "CNY",
"notify_url" => "https://kuaishou.com",
"extension" => "15434xxxxxxxx3813",
"user_ip" => "171.xx.xx.34",
"third_party_trade_no" => "123456789"
"game_id" => "1205920xxxxxxxxxxxxxxxx30acf74"
);
$private_key="-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgxxxxxxxxxxxxxxxxxxxxxxxavcBjFHRi3oAhhVdujfAygE
-----END RSA PRIVATE KEY-----";
$ret = sha_sign($data, $priKey);
echo $ret;
function sha_sign($data, $priKey) {
ksort($data);
$str = "";
foreach ($data as $k => $v)
{
if(!empty($v))
{
$str .= $k."=".$v."&";
}
}
$newstr = substr($str,0,strlen($str)-1);
echo $newstr;
echo $private_key;
$pkeyid=openssl_get_privatekey($private_key);
openssl_sign($params_str,$sign,($pkeyid),OPENSSL_ALGO_SHA512);//OPENSSL_ALGO_SHA512
openssl_free_key($pkeyid);
$sign=(base64_encode($sign));//exit($sign);
return $sign;
}
# 验签
<?php
$public_key="-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9xxxxxxxxxxxxxxxxxxxxxxxxAgEAqwyPnm1hhYZyNmvuHl6O
-----END PUBLIC KEY-----";
$receiveParams=array (
"allin_trade_no" => "AI719xxxxxxxxxxxxxxx4313",
"app_id" => "ks709xxxxxxxxxxxx615022",
"data" => "{"environment":"PRODUCTION"}",
"extension" => "KUAISHOU1544xxxxxx6519_a66f1xxx7f8ba_2810001",
"money" => "100",
"product_id" => "1",
"role_id" => "100xxxx1",
"server_id" => "281xxx1",
"sign" => "c06C3X989YhP0YMSaYaviv/uEQM7ZaH4Ua/a91Pv0R5Qx7dLQnSNQBsfCxQbH0W/p90hx5k6tZ56wqOiq1hl+Efs8f6175H9Q8xxxxxxxxjCvu3Kjd9jMvxDivQKtYEnJkr+lM1GcBlUQLUINUwYRH74o4txCMAxxxxxxxxxy6kSPIhnycZzeLlSDfJU6f4h5n9iYdJX8RfVBsPh7/H2FCPUwyHGyFQ2ap9Q4UTHYrq7L2uTY9LHY="
);
$signstr =$receiveParams["sign"];
$sign_arr=array();
foreach ($receiveParams as $key => $value) {
if($key!="sign"){
$sign_arr[$key]=$value;
}
}
ksort($sign_arr);
$params_str="";
foreach ($sign_arr as $key => $value) {
$params_str.=$key."=".$value."&";
}
$params_str=substr($params_str,0,strlen($params_str)-1);
$public_key_res = openssl_get_publickey($public_key);
$ok = openssl_verify($params_str, base64_decode($signstr), $public_key_res, OPENSSL_ALGO_SHA512);
if(!$ok){
echo "fail";
}else{
echo "success";
}
?>
# nodeJs
const fs = require("fs");
const logger = require("log4js").getLogger("common");
const crypto = require("crypto");
const path = require("path");
/**
*
* @param {Object} opts
* @param {String} opts.appId 快手分配appId
* @param {String} opts.notifyUrl 快手服务器主动通知商户服务器里指定的页面http/https路径
* @param {String} opts.rsaPrivate app私钥pem文件路径
* @param {String} opts.rsaPublic 快手公钥pem文件路径
* @constructor
*/
class KwaiPay{
constructor(){
this.app = null;
this.rsaPrivate = null;
this.rsaPublic = null;
}
init(app) {
this.app = app;
let rsaPrivateKey = fs.readFileSync(path.resolve(__dirname + "/../../keys/rsa_private_key.pem"), "utf-8");
if(rsaPrivateKey.indexOf("-----BEGIN RSA PRIVATE KEY-----") === -1){
rsaPrivateKey = this.getPrivateKey(rsaPrivateKey);
}
let rsaPublicKey = fs.readFileSync(path.resolve(__dirname + "/../../keys/allinpay_public_key.pem"), "utf-8");
if(rsaPublicKey.indexOf("-----BEGIN PUBLIC KEY-----") === -1){
rsaPublicKey = this.getPublicKey(rsaPublicKey);
}
this.rsaPrivate = rsaPrivateKey;
this.rsaPublic = rsaPublicKey;
}
wordwrap(str, width) {
width = width || 64;
if (!str) {
return str;
}
let regex = "(.{1," + width + "})( +|$
?)|(.{1," + width + "})";
return str.match(RegExp(regex, "g")).join("
");
}
getPublicKey (publicKey) {
let content = "-----BEGIN PUBLIC KEY-----
";
content += this.wordwrap(publicKey) + "
";
content += "-----END PUBLIC KEY-----";
return content;
}
getPrivateKey (privateKey) {
let content = "-----BEGIN RSA PRIVATE KEY-----
";
content += this.wordwrap(privateKey) + "
";
content += "-----END RSA PRIVATE KEY-----";
return content;
}
//得到编码前和编码后的参数信息
getParams(opts){
return this._encodeParams(opts);
}
//对编码前的参数签名
getSign(opts) {
return this._signParams(opts, this.rsaPrivate);
}
/**
* 签名校验
*/
signVerify(sign, param){
let dsign = decodeURIComponent(sign);
let tmp = this._encodeParams(param);
logger.info(`[signVerify] unEncodeurl:${tmp.unencode}, encodeurl:${tmp.encode}, sign:${sign}`);
return this._signVerify(tmp.unencode, dsign, this.rsaPublic);
}
/**
* 对请求参数进行组装、编码
* @param {Object} params 请求参数
* @returns {Object}
*/
_encodeParams(params) {
let keys = [];
for(let k in params) {
let v = params[k];
if (params[k] !== undefined && params[k] !== "") keys.push(k);
}
keys.sort();
let unencodeStr = "";
let encodeStr = "";
let len = keys.length;
for(let i = 0; i < len; ++i) {
let k = keys[i];
if(i !== 0) {
unencodeStr += "&";
encodeStr += "&";
}
unencodeStr += k + "=" + params[k];
encodeStr += k + "=" + encodeURIComponent(params[k]);
}
return {unencode:unencodeStr, encode:encodeStr};
}
_signParams (params, privateKey) {
let ret = this._encodeParams(params);
let sign = this._sign(ret.unencode, privateKey);
logger.info(`[signParams] unEncodeurl:${ret.unencode}, encodeurl:${ret.encode}, sign:${sign}`);
return sign;//encodeURIComponent(sign);
}
/**
* 对字符串进行签名
* @param {String} str 要签名的字符串
* @param {String} privateKey 商户应用私钥
* @returns {String}
*/
_sign(str, privateKey) {
let sha = crypto.createSign("RSA-SHA512");
sha.update(str, "utf8");
return sha.sign(privateKey, "base64");
}
/**
* 对字符串进行签名验证
* @param {String} str 要验证的参数字符串
* @param {String} sign 要验证的签名
* @param {String} publicKey 支付宝公钥
* @returns {Boolean}
*/
_signVerify(str, sign, publicKey) {
let verify = crypto.createVerify("RSA-SHA512");
verify.update(str, "utf8");
let result = verify.verify(publicKey, sign, "base64");
return result;
}
}
module.exports = new KwaiPay();
# java
// 签名算法
public static final String KEY_ALGORITHM = "RSA";
/**
* 签名算法
*/
public static final String SIGN_ALGORITHMS = "SHA512WithRSA";
/**
* 参与下单签名的字段
*/
public static final String[] keys = new String[]{
"app_id", "channel_id", "game_id",
"role_id", "role_name", "role_level",
"server_id", "server_name",
"product_id", "product_name", "product_desc",
"money", "currency_type",
"notify_url", "user_ip", "third_party_trade_no","extension"
};
/**
* 获取签名串
*
*/
public static String getSignStringFromMap(Map<String, String> map) {
List<String> keysList = Arrays.asList(keys);
List<String> list = new ArrayList<String>();
for (Map.Entry<String, String> entry : map.entrySet()) {
if (TextUtils.isEmpty(entry.getValue())) continue;
if (keysList.contains(entry.getKey())) {
list.add(entry.getKey() + "=" + entry.getValue());
} else {
System.out.println("all_" + entry.getKey() + " = " + entry.getValue());
}
}
Collections.sort(list);
return TextUtils.join("&", list);
}
/**
* RSA签名
*
* @param content 待签名数据
* @param privateKey 商户私钥
* @param encode 字符集编码 utf-8
* @return 签名值
*/
public static String sign(String src, String privateKey) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
signature.initSign(privateKey2);
signature.update(src.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature.sign());
}
/**
* RSA验签名检查
*
* @param content 待签名数据
* @param sign 签名值
* @param publicKey 分配给开发商公钥
* @param encode 字符集编码
* @return 布尔值
*/
public static boolean check(String content, String sign, String publicKey, String encode) {
try {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
byte[] encodedKey = Base64.decode(publicKey, Base64.DEFAULT);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
signature.initVerify(pubKey);
signature.update(content.getBytes(encode));
return signature.verify(Base64.decode(sign, Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
# C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace RsaHelper{
public class RasHelper{
/// <summary>
/// Signature 签名
/// </summary>
/// <param name="content">Content to be signed</param>
/// <param name="privateKey">Private key</param>
/// <returns></returns>
public static string RasSha512Sign(string content, string privateKey){
byte[] Data = Encoding.UTF8.GetBytes(content);
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
SHA512 sh = new SHA512CryptoServiceProvider();
byte[] signData = rsa.SignData(Data, sh);
return Convert.ToBase64String(signData);
}
/// <summary>
/// Verify signature 验证签名
/// </summary>
/// <param name="content">Content to be verified</param>
/// <param name="signedString">Signature result</param>
/// <param name="publicKey">Public key</param>
/// <returns></returns>
public static bool RsaSha512Verify(string content, string signedString, string publicKey){
signedString = signedString.Replace(" ", "+").Replace("u003d", "=");
byte[] Data = Encoding.UTF8.GetBytes(content);
byte[] data = Convert.FromBase64String(signedString);
// 不同部分
Byte[] byPkey = Convert.FromBase64String(publicKey);
RSACryptoServiceProvider rsaPub = DecodeX509PublicKey(byPkey);
SHA512 sh = new SHA512CryptoServiceProvider();
return rsaPub.VerifyData(Data, sh, data);
}
/// <summary>
/// Parse the private key of the PEM file generated by JAVA 解析由Java生成的PEM文件的私钥
/// </summary>
/// <param name="pemstr"></param>
/// <returns></returns>
private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr){
byte[] pkcs8privatekey;
pkcs8privatekey = Convert.FromBase64String(pemstr);
if (pkcs8privatekey != null) {
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);
return rsa;
}
else
return null;
}
/// <summary>
/// 解码私有密钥信息
/// </summary>
/// <param name="pkcs8"></param>
/// <returns></returns>
private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8){
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
MemoryStream mem = new MemoryStream(pkcs8);
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if (bt != 0x02)
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0001)
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
bt = binr.ReadByte();
if (bt != 0x04) //expect an Octet string
return null;
bt = binr.ReadByte(); //read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count
if (bt == 0x81)
binr.ReadByte();
else
if (bt == 0x82)
binr.ReadUInt16();
//------ at this stage, the remaining sequence should be the RSA private key
byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
return rsacsp;
}
catch (Exception){return null;}
finally { binr.Close();}
}
/// <summary>
/// 对比
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
private static bool CompareBytearrays(byte[] a, byte[] b){
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a){
if (c != b[i])
return false;
i++;
}
return true;
}
/// <summary>
/// RSA密钥解码
/// </summary>
/// <param name="privkey"></param>
/// <returns></returns>
private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey){
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception){return null;}
finally { binr.Close(); }
}
/// <summary>
/// 获得整数
/// </summary>
/// <param name="binr"></param>
/// <returns></returns>
private static int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn"t a removed zero, so back up a byte
return count;
}
#region Parse the PEM file generated by .net 解析.NET生成的PEM文件
#region 公钥解析
private static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
{
byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509key);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30)
ReadASNLength(reader); //skip the size
else
return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30)
identifierSize = ReadASNLength(reader);
else
return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
return null;
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03){ //is the next element a bit string?
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30){
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02){//is it an integer?
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00){ //strip off the first byte if it"s 0
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
if (reader.ReadByte() == 0x02){ //is it an integer?
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
}
}
}
return null;
}
private static int ReadASNLength(BinaryReader reader){
//Note: this method only reads lengths up to 4 bytes long as
//this is satisfactory for the majority of situations.
int length = reader.ReadByte();
if ((length & 0x00000080) == 0x00000080){ //is the length greater than 1 byte
int count = length & 0x0000000f;
byte[] lengthBytes = new byte[4];
reader.Read(lengthBytes, 4 - count, count);
Array.Reverse(lengthBytes); //
length = BitConverter.ToInt32(lengthBytes, 0);
}
return length;
}
#endregion
#endregion
}
}