Skip to content

签名 ​

KeepSDK 与游戏之间只用这一套签名。

算法 ​

  • 算法:MD5
  • 输出:32 位小写 hex
  • time:10 位 Unix 秒,允许误差 ±300 秒

步骤:

  1. 取出本次请求里的全部参数,去掉 sign
  2. 按参数名 ASCII 升序拼成 k=v&k=v
  3. 值用解码后的原文,不要 urlencode 后再签
  4. 末尾直接拼密钥,中间没有 &key=
  5. 对整串做 MD5
text
sign = md5( k1=v1&k2=v2&... + 密钥 )

GET(登录 302、/pay)和 JSON(发货、查角色)规则相同,都签语义值。

中文、空格写进 URL 时要 urlencode;算签名仍用原文。首充8元 签原文,URL 写成 %E9%A6%96...。签 encode 之后的串会 1002。

JSON 里直接写中文即可。空值不要写进 JSON,写了就要进签。

各接口用哪把密钥 ​

创建游戏时下发 login_key、pay_key,只展示一次。接口与密钥对应如下。

接口方向密钥
登录 login_urlKeepSDK → 游戏login_key
查角色 role_urlKeepSDK → 游戏login_key
拉区服 server_urlKeepSDK → 游戏login_key
封禁 ban_urlKeepSDK → 游戏login_key
聊天 POST /game/chat游戏 → KeepSDKlogin_key
打点 POST /game/report游戏 → KeepSDKlogin_key
平台VIP信息 POST /game/vip/profile游戏 → KeepSDKlogin_key
平台VIP收益 POST /game/vip/benefit游戏 → KeepSDKlogin_key
下单 GET /pay游戏 → KeepSDKpay_key
发货 deliver_urlKeepSDK → 游戏pay_key
礼包 gift_urlKeepSDK → 游戏pay_key

代码示例 ​

去掉 sign 后按参数名 ASCII 升序拼 k=v&k=v,末尾直接拼密钥,没有 &key=、没有 secret=。值用原文(含中文)。拷贝后先跑文末自测样例。接口入口见 PHP 示例。

Go ​

go
package keepsdksign

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"sort"
	"strings"
)

func Sign(params map[string]string, key string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		if k == "sign" {
			continue
		}
		keys = append(keys, k)
	}
	sort.Strings(keys)
	parts := make([]string, 0, len(keys))
	for _, k := range keys {
		parts = append(parts, fmt.Sprintf("%s=%s", k, params[k]))
	}
	sum := md5.Sum([]byte(strings.Join(parts, "&") + key))
	return hex.EncodeToString(sum[:])
}

func SignSample() string {
	return Sign(map[string]string{
		"app_id": "lmyxweb",
		"time":   "1757250000",
		"uid":    "u1",
	}, "test_login_key")
}

JavaScript(Node.js) ​

js
import crypto from 'node:crypto'

export function keepsdkSign(params, key) {
  const keys = Object.keys(params)
    .filter((k) => k !== 'sign')
    .sort()
  const raw = keys.map((k) => `${k}=${params[k]}`).join('&') + key
  return crypto.createHash('md5').update(raw, 'utf8').digest('hex')
}

export function keepsdkSignSample() {
  return keepsdkSign(
    {
      app_id: 'lmyxweb',
      time: '1757250000',
      uid: 'u1',
    },
    'test_login_key',
  )
}

Java ​

java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class KeepSDKSign {
  public static String sign(Map<String, String> params, String key) throws Exception {
    List<String> keys = new ArrayList<String>();
    for (String k : params.keySet()) {
      if (!"sign".equals(k)) {
        keys.add(k);
      }
    }
    Collections.sort(keys);
    StringBuilder raw = new StringBuilder();
    for (int i = 0; i < keys.size(); i++) {
      if (i > 0) {
        raw.append('&');
      }
      String k = keys.get(i);
      raw.append(k).append('=').append(params.get(k));
    }
    raw.append(key);
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] bytes = md.digest(raw.toString().getBytes(StandardCharsets.UTF_8));
    StringBuilder out = new StringBuilder(bytes.length * 2);
    for (byte b : bytes) {
      out.append(String.format("%02x", b));
    }
    return out.toString();
  }

  public static String signSample() throws Exception {
    Map<String, String> params = new HashMap<String, String>();
    params.put("app_id", "lmyxweb");
    params.put("time", "1757250000");
    params.put("uid", "u1");
    return sign(params, "test_login_key");
  }
}

PHP ​

php
<?php
function keepsdk_sign($params, $key) {
    unset($params['sign']);
    ksort($params, SORT_STRING);
    $parts = array();
    foreach ($params as $k => $v) {
        $parts[] = $k . '=' . $v;
    }
    return md5(implode('&', $parts) . $key);
}

function keepsdk_sign_sample() {
    return keepsdk_sign(array(
        'app_id' => 'lmyxweb',
        'time'   => '1757250000',
        'uid'    => 'u1',
    ), 'test_login_key');
}

Python ​

python
import hashlib

def keepsdk_sign(params: dict, key: str) -> str:
    keys = sorted(k for k in params if k != "sign")
    raw = "&".join(f"{k}={params[k]}" for k in keys) + key
    return hashlib.md5(raw.encode("utf-8")).hexdigest()

def keepsdk_sign_sample() -> str:
    return keepsdk_sign(
        {
            "app_id": "lmyxweb",
            "time": "1757250000",
            "uid": "u1",
        },
        "test_login_key",
    )

C# ​

csharp
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

public static class KeepSDKSign
{
    public static string Sign(Dictionary<string, string> paramsMap, string key)
    {
        var keys = paramsMap.Keys
            .Where(k => k != "sign")
            .OrderBy(k => k, StringComparer.Ordinal)
            .ToList();
        var raw = new StringBuilder();
        for (var i = 0; i < keys.Count; i++)
        {
            if (i > 0) raw.Append('&');
            var k = keys[i];
            raw.Append(k).Append('=').Append(paramsMap[k]);
        }
        raw.Append(key);
        using var md5 = MD5.Create();
        var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(raw.ToString()));
        var sign = new StringBuilder(hash.Length * 2);
        foreach (var b in hash)
        {
            sign.Append(b.ToString("x2"));
        }
        return sign.ToString();
    }

    public static string SignSample()
    {
        var paramsMap = new Dictionary<string, string>
        {
            { "app_id", "lmyxweb" },
            { "time", "1757250000" },
            { "uid", "u1" },
        };
        return Sign(paramsMap, "test_login_key");
    }
}

自测样例 ​

写完后先用这一组对一下:先对 raw,再对 sign。密钥是直接拼在串尾,前面没有 &。

text
params:
app_id=lmyxweb
time=1757250000
uid=u1
key=test_login_key

raw:
app_id=lmyxweb&time=1757250000&uid=u1test_login_key

sign:
28983fd71d2d477de66244003981f475