注意
签名中,不支持特殊字符“\0”,“\t”,“\n”,“\x0B”,“\r”, “ “(空格)进行加密处理
加密方法选择MD5还是SHA256,需要根据应用管理-安全设置中的签名算法决定;默认为SHA256。如需使用MD5,请管理员进行调整。

签名作用
问题
- 如何分辨出是否贵企业的请求?
- 如何分辨出请求消息的内容是否被篡改?
解决方法
通过数字签名就可以解决上述的问题。具体为:约定sign_key作为密钥,该sign_key仅贵企业和滴滴知道,在传输中不可见,用于参与签名计算。 企业在发送请求前,将消息内容与sign_key按照滴滴提供的签名算法计算出签名。滴滴在收到请求时,也按相同算法计算出签名。 如果为同一签名,则可信任来源为贵企业,并且内容是完整的。
如果非贵企业来源,由于攻击者没有正确的sign_key,无法算出正确的签名;
如果消息内容被篡改,由于滴滴会将接收的消息内容与sign_key重算一次签名,该值与参数的签名不一致,则会拒绝该请求。
签名算法步骤
- 生成签名的时候,将颁发的sign_key加入到传递的参数中,参与加密
- 传递的参数(包含sign_key)按照参数名升序排序,然后,以&形式连接(类似格式为a=xxx&b=xxx&c=xxx...),生成小写的md5串或者sha256串
- 生成sign后,sign和其他参数一起传递,对于中文在传递的过程中,需要进行urlencode,加密的时候不进行urlencode(如是以POST方法json格式传参不需要urlencode)
- sign_key不要参与参数传递,只参与生成sign
- 对于所有API的调用中用到的签名都可以用此工具测试。签名验证工具
示例
以授权认证接口为例,接口传参如下:
client_id = "client_id1"
client_secret = "client_secret1"
grant_type = "client_credentials"
phone = "11000001234"
timestamp = 1566477389
---------------------------------------------------------------------------------------------------------------------------
假设sign_key = "sign_key1",则授权认证接口参与签名的参数有:client_id、client_secret、grant_type、phone、timestamp、sign_key
---------------------------------------------------------------------------------------------------------------------------
首先,将所有参与签名的参数进行排序,排序后的参数顺序为:client_id、client_secret、grant_type、phone、sign_key、timestamp;
接着,将排序好的参数用&连接后的字符串str为:client_id=client_id1&client_secret=client_secret1&grant_type=client_credentials&phone=11000001234&sign_key=sign_key1×tamp=1566477389
最后,使用md5算法或者sha256算法加密str得到sign
例如:
md5("client_id=client_id1&client_secret=client_secret1&grant_type=client_credentials&phone=11000001234&sign_key=sign_key1×tamp=1566477389")
sign = "c52b8bac5e980da9ac557db412c20580"
hash("sha256","client_id=client_id1&client_secret=client_secret1&grant_type=client_credentials&phone=11000001234&sign_key=sign_key1×tamp=1566477389")
sign = "6f296c236346659f6351d548e3ec4260ac9847b117a1cc9428fd709a5976a8c5"
算法实现
php版本
function genSign(array $params, $signKey, $signMethod)
{
$params['sign_key'] = $signKey;
ksort($params); //对数组(map)根据键名升序排序
$str = '';
foreach ($params as $k => $v)
{
if ('' == $str)
{
$str .= $k . '=' . trim($v);
}
else
{
$str .= '&' . $k . '=' . trim($v);
}
}
echo $str . "\n"; //待加密字符串
// 如果$signMethod == 1 : 采用sha256加密方式
if ($signMethod == 1) {
return hash("sha256", $str);
}
//采用md5加密:此处md5值为小写的32个字符
return md5($str);
}
$params = array(
"client_id" => "client_id_test",
"access_token" => "access_token_test",
"timestamp" => 1562325354,
);
$signMethod = 1;
$signKey = "sign_key_test";
$sign = genSign($params, $signKey, $signMethod);
echo $sign;变量说明
Java版本
import java.io.*;
import java.util.*;
import java.security.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
class test
{
//java md5算法
public static String md5(String plainText)
{
byte[] secretBytes = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(plainText.getBytes("utf-8"));
secretBytes = md.digest();
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException("no such algorithm!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);
int length = md5code.length();
for (int i = 0; i < 32 - length; i++) {
md5code = "0" + md5code;
}
return md5code;
}
//java sha256算法
public static String sha256Hex(String input) {
try {
// 创建一个MessageDigest实例,并指定使用SHA-256算法
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// 将输入字符串转换为字节数组,并更新摘要
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
// 将字节数组转换为十六进制字符串
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static String genSign(HashMap<String, String> params, String signKey, Integer signMethod)
{
params.put("sign_key", signKey);
String result = "";
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(params.entrySet());
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
// 构造签名键值对的格式
for (Map.Entry<String, String> item : infoIds) {
if (item.getKey() != null || item.getKey() != "") {
String key = item.getKey();
String val = item.getValue().trim();
if (result == "") {
result += key + "=" + val;
} else {
result += "&" + key + "=" + val;
}
}
}
//System.out.println(result);
} catch(Exception e) {
throw new RuntimeException("error");
}
if(signMethod == 1) {
return sha256Hex(result);
}
return md5(result);
}
public static void main (String[] args) throws java.lang.Exception
{
HashMap<String, String> map = new HashMap<String, String>();
map.put("client_id", "client_id_test");
map.put("access_token", "access_token_test");
map.put("timestamp", "1562325354");
String signKey = "sign_key_test";
Integer signMethod = 1;
String sign = genSign(map, signKey, signMethod);
System.out.println(sign);
}
}
| 变量名 | 类型 | 说明 |
| sign_key | string | 申请应用时分配的密钥(保密存储,不要作为参数传递) |
| params | array | 调用api时要传递的所有参数组成的数组(参数名为键,参数值为元素值) |
签名示例:
签名失败:
{
"errno": 19999,
"errmsg": "签名失败",
"data": {
"params_sign_str": "client_id=dc60c9de0a96c20fccf2ccaa40_test&client_secret=d23c6d90dff1993982dc151601&company_id=112591566167936&grant_type=client_credentials&phone=13100011245&sign_key=**×tamp=1709630326"
},
"request_id": "b7tL/EnaLhrcf2ekGRDn8fNhX+VqpYY4qg6jaxlmO7vAKpziv4aLLv7hhwwn6Rl+"
}
params_sign_str 为签名前的字符串,需要客户自行进行对比
签名示例
请求参数:
{
"client_id": "dc60c9de0a96c20fccf2ccaa40_test",
"client_secret": "d23c6d90dff1993982dc151601",
"company_id": "112591566167936",
"grant_type": "client_credentials",
"phone": "13100011245",
"timestamp": 1709630572
}
生成签名前的字符串:
signstr client_id=dc60c9de0a96c20fccf2ccaa40_test&client_secret=d23c6d90dff1993982dc151601&company_id=112591566167936&grant_type=client_credentials&phone=13100011245&sign_key=f86b6735313052FD7d0f×tamp=1709630572
请求参数
{
"access_token": "6f185cb4a60770ca8e615abd1084e7a9d605f27a",
"client_id": "dc60c9de0a96c20fccf2ccaa40_test",
"company_id": "112591566167936",
"data": "{\"member_type\":1,\"realname\":\"中文+ Con\", \"phone\":\"11100009915\", \"project_ids\":\"\"}",
"sign": "966e90d7ee6527ba8d1cdc0f96c2d844",
"timestamp": 1709630852
}
生成签名前的字符串:
access_token=6f185cb4a60770ca8e615abd1084e7a9d605f27a&client_id=dc60c9de0a96c20fccf2ccaa40_test&company_id=112591566167936&data={"member_type":1,"realname":"中文+ Con", "phone":"11100009915", "project_ids":""}&sign_key=f86b6735313052FD7d0f×tamp=1709630852

