URI参数签名算法【转载】

URI参数签名算法

简介

应用基于HTTP POST或HTTP GET请求发送Open API调用请求时,为了确保应用与百度REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为,百度REST服务器使用了参数签名机制。应用在调用百度Open API之前,需要为其所有请求参数计算一个MD5签名,并追加到请求参数中,参数名为“sign”。百度REST服务器在接收到请求时会重新计算签名,并判断其值是否与应用传递过来的sign参数值一致,以此判定当前Open API调用请求是否是被第三者伪造或篡改。

应用在调用Open API之前需要通过百度OAuth2.0服务获得用户或平台的授权,获取到授权后将会拿到以下3个重要参数:

  • access_token:基于https调用Open API时所需要的访问授权码;
  • session_key:基于http调用Open API时所需要的访问授权码;
  • session_secret:基于http调用Open API时计算参数签名用的签名密钥。

其中,session_secret这个参数就是做参数签名时所需要的签名密钥。这与Facebook、人人网等平台稍微有所区别,这两个平台在做参数签名时所用的签名密钥一般有2个:

  • 如果是通过应用服务端调用Open API,则注册应用时所拿到的应用密钥(即API Key)就是参数签名密钥;
  • 如果是通过JavaScript、ActionScript等客户端语言调用Open API,则应用获取到用户授权后所拿到的Session Secret就是参数签名密钥。当然,通过服务端调用Open API时也可以用Session Secret作为签名密钥。

签名算法

假设参与参数签名计算的请求参数分别是“k1”、“k2”、“k3”,它们的值分别是“v1”、“v2”、“v3”,则参数签名计算方法如下:

  • 将请求参数格式化为“key=value”格式,即“k1=v1”、“k2=v2”、“k3=v3”;
  • 将格式化好的参数键值对以字典序升序排列后,拼接在一起,即“k1=v1k2=v2k3=v3”;
  • 在拼接好的字符串末尾追加上应用通过百度OAuth2.0协议获取Access Token时所获取到的session_secret参数值;
  • 上述字符串的MD5值即为签名的值。

注意:计算签名时的请求参数中不要包含sign(签名)参数,因为sign参数的值此时还不知道,有待计算

另外,计算签名的时候不需要对参数进行urlencode处理(“application/x-www-form-urlencoded”编码),但是发送请求的时候需要进行urlencode处理,这是很多开发者最容易犯错的地方。

签名过程示例

假设某个应用需要获取某个uid为67411167的用户的基本资料,应用在之前的通过百度OAuth2.0服务获取Access Token的过程中所拿到的session_key和session_secret参数值分别为:

  • session_key: "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="
  • session_secret: "27e1be4fdcaa83d7f61c489994ff6ed6"

调用Open API时的系统时间(PHP中可以通过date(‘Y-m-d H:i:s‘)来获取当前系统时间)为"2011-06-21 17:18:09",希望REST服务器以JSON格式返回调用结果,即相当于参与参数签名计算的请求参数集合为:



[
    "session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=",
    "timestamp" => "2011-06-21 17:18:09",
    "format" => "json",
    "uid" => 67411167
]
 

则计算签名的具体过程如下:

  • 将请求参数格式化为“key=value”格式,格式化后的请求参数集合为:
 [
    "session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=",
    "timestamp=2011-06-21 17:18:09",
    "format=json",
    "uid=67411167"
 ]
 
  • 将格式化好的参数键值对以字典序升序排列,得到如下参数集:
 [
    "format=json",
    "session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=",
    "timestamp=2011-06-21 17:18:09",
    "uid=67411167"
 ]
 
  • 将前面排序好的参数集拼接在一起,得到如下字符串:
format=jsonsession_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=timestamp=2011-06-21 17:18:09uid=67411167
  • 在拼接好的字符串末尾追加上应用通过百度OAuth2.0协议获取Access Token时所获取到的session_secret参数值,得到如下字符串:
format=jsonsession_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=timestamp=2011-06-21 17:18:09uid=6741116727e1be4fdcaa83d7f61c489994ff6ed6
  • 对前面得到的字符串求MD5签名,得到的d24dd357a95a2579c410b3a92495f009就是调用API时所需要的sign参数值。

接下来便可以通过HTTP POST方法或HTTP GET方法请求百度Open API的REST服务器,进行接口调用了,如:

GET /rest/2.0/passport/users/getInfo?session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D&timestamp=2011-06-21+17%3A18%3A09&format=json&uid=67411167&sign=d24dd357a95a2579c410b3a92495f009 HTTP/1.1
Host: openapi.baidu.com
User-Agent: Client of Baidu Open Platform
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: utf-8
Connection: close

或
POST /rest/2.0/passport/users/getInfo HTTP/1.1
Host: openapi.baidu.com
User-Agent: Client of Baidu Open Platform
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: utf-8
Content-Length: 179
Connection: close

session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D&timestamp=2011-06-21+17%3A18%3A09&format=json&uid=67411167&sign=d24dd357a95a2579c410b3a92495f009

签名算法实现代码

PHP代码实现

获取签名的PHP代码实现方式如下所示:

/**
  * 签名生成算法
  * @param  array  $params API调用的请求参数集合的关联数组,不包含sign参数
  * @param  string $secret 签名的密钥即获取access token时返回的session secret
  * @return string 返回参数签名值
  */
 function getSignature($params, $secret)
 {
    $str = ‘‘;  //待签名字符串
    //先将参数以其参数名的字典序升序进行排序
    ksort($params);
    //遍历排序后的参数数组中的每一个key/value对
    foreach ($params as $k => $v) {
        //为key/value对生成一个key=value格式的字符串,并拼接到待签名字符串后面
        $str .= "$k=$v";
    }
    //将签名密钥拼接到签名字符串最后面
    $str .= $secret;
    //通过md5算法为签名字符串生成一个md5签名,该签名就是我们要追加的sign参数值
    return md5($str);
 }

调用示例:

$uid = 67411167;
$params = array(
    "session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=",
    "timestamp" => "2011-06-21 17:18:09",
    "format" => "json",
    "uid" => $uid,
);
$sign = getSignature($params, "27e1be4fdcaa83d7f61c489994ff6ed6");

 

Java代码实现

获取签名的java代码实现方式如下所示:


/**
 * 签名生成算法
 * @param HashMap<String,String> params 请求参数集,所有参数必须已转换为字符串类型
 * @param String secret 签名密钥
 * @return 签名
 * @throws IOException
 */
public static String getSignature(HashMap<String,String> params, String secret) throws IOException
{
    // 先将参数以其参数名的字典序升序进行排序
    Map<String, String> sortedParams = new TreeMap<String, String>(params);
    Set<Entry<String, String>> entrys = sortedParams.entrySet();

    // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
    StringBuilder basestring = new StringBuilder();
    for (Entry<String, String> param : entrys) {
        basestring.append(param.getKey()).append("=").append(param.getValue());
    }
    basestring.append(secret);

    // 使用MD5对待签名串求签
    byte[] bytes = null;
    try {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        bytes = md5.digest(basestring.toString().getBytes("UTF-8"));
    } catch (GeneralSecurityException ex) {
        throw new IOException(ex);
    }

    // 将MD5输出的二进制结果转换为小写的十六进制
    StringBuilder sign = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(bytes[i] & 0xFF);
        if (hex.length() == 1) {
            sign.append("0");
        }
        sign.append(hex);
    }
    return sign.toString();
}



注意:计算签名时所有参数的key和value都必须先转换为对应的字符串类型,因为在HTTP请求中传递的内容都是字符串类型的,很多开发者都因为没注意到这点,直接将非字符串类型的参数的二进制值传递了进去,结果导致签名与服务端计算的不一致而出错。

C#代码实现

获取签名的C#代码实现方式如下所示:


/// <summary>
/// 计算参数签名
/// </summary>
/// <param name="params">请求参数集,所有参数必须已转换为字符串类型</param>
/// <param name="secret">签名密钥</param>
/// <returns>签名</returns>
public static string getSignature(IDictionary<string, string> parameters, string secret)
{
    // 先将参数以其参数名的字典序升序进行排序
    IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
    IEnumerator<KeyValuePair<string, string>> iterator= sortedParams.GetEnumerator();

    // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
    StringBuilder basestring= new StringBuilder();
    while (iterator.MoveNext()) {
            string key = iterator.Current.Key;
            string value = iterator.Current.Value;
            if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)){
                basestring.Append(key).Append("=").Append(value);
            }
    }
    basestring.Append(secret);

    // 使用MD5对待签名串求签
    MD5 md5 = MD5.Create();
    byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(basestring.ToString()));

    // 将MD5输出的二进制结果转换为小写的十六进制
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < bytes.Length; i++) {
            string hex = bytes[i].ToString("x");
            if (hex.Length == 1) {
                result.Append("0");
            }
            result.Append(hex);
    }

    return result.ToString();
}
时间: 2024-11-07 00:30:23

URI参数签名算法【转载】的相关文章

PHP、Java、C#实现URI参数签名算法,确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为

简介 应用基于HTTP POST或HTTP GET请求发送Open API调用请求时,为了确保应用与REST服务器之间的安全通信,防止Secret Key盗用.数据篡改等恶意攻击行为,REST服务器使用了参数签名机制.应用在调用Open API之前,需要为其所有请求参数计算一个MD5签名,并追加到请求参数中,参数名为“sign”.REST服务器在接收到请求时会重新计算签名,并判断其值是否与应用传递过来的sign参数值一致,以此判定当前Open API调用请求是否是被第三者伪造或篡改. 应用在调用

redirect uri 参数错误 怎么办

这种情况,多数是因为请求地址不合法所致. 去公众号中添加合法的地址. 这种地址需要满足一些条件. 设置地址 满足的条件 保证可以访问到安全文件 如果访问不到的话,将无法保存 这里是文件存放位置 经过这些处理,就不会出现redirect uri 参数错误 原文地址:https://www.cnblogs.com/jiqing9006/p/9223882.html

指向指针的指针做函数的参数(转载)

一. 回顾指针概念:  今天我们又要学习一个叫做指向另一指针地址的指针.让我们先回顾一下指针的概念吧!  当我们程序如下申明变量:  short int i; char a; short int * pi; 程序会在内存某地址空间上为各变量开辟空间,如下图所示.  内存地址→6     7      8     9     10     11    12    13     14    15  ---------------------------------------------------

open(/dev/ietctl, O_RDWR) 参数含义(转载)

这是文件I/O的常用函数,open函数,open函数用来打开一个设备,他返回的是一个整型变量,如果这个值等于-1,说明打开文件出现错误,如果为大于0的值,那么这个值代表的就是文件描述符.一般的写法是if((fd=open("/dev/ttys0",O_RDWR | O_NOCTTY | O_NDELAY)<0){perror("open");}这个事常用的一种用法fd是设备描述符,linux在操作硬件设备时,屏蔽了硬件的基本细节,只把硬件当做文件来进行操作,而

Delphi7 调用 C#的Webservice 不能传入参数(转载)

Delphi7 调用 C#的Webservice 不能传入参数 解决办法: 在Delphi导入WSDL后生成的单元的最后一行,即initialization里的初始化端口的代码中加入代码 InvRegistry.RegisterInvokeOptions(TypeInfo(接口名), ioDocument); 即可 要加在 InvRegistry.RegisterInterface(TypeInfo(接口名),'http://XXX.com', 'UTF-8'); InvRegistry.Reg

nginx 高并发配置参数(转载)

一.一般来说nginx 配置文件中对优化比较有作用的为以下几项: 1.  worker_processes 8; nginx 进程数,建议按照cpu 数目来指定,一般为它的倍数 (如,2个四核的cpu计为8). 2.  worker_cpu_affinity 00000001 0000001000000100 00001000 00010000 00100000 01000000 10000000; 为每个进程分配cpu,上例中将8 个进程分配到8 个cpu,当然可以写多个,或者将一 个进程分配

SQL-android uri的使用(转载)

今天在操作android的时候,用到了数据库的访问,就在网上学习了一下关于数据库的知识.其中访问数据库就是通过uri进行的,所以这里总结下android uri的应用. 以下内容参考http://www.cnblogs.com/lingyun1120/archive/2012/04/18/2455212.html 通用资源标志符(Universal Resource Identifier, 简称"URI"). Uri代表要操作的数据,Android上可用的每种资源 - 图像.视频片段等

js 获取项主域名 和 获取uri参数 解析返回结果小工具

function getImgDomain() { return "http://imgupload.youboy.com/"; } /**获取根目录**/ function getRootPath(){ //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp var curWwwPath=window.document.location.href; //获取主机地址之后的目录,如: uimcardprj/share/me

System.Uri.cs

ylbtech-System.Uri.cs 1.返回顶部 1. #region 程序集 System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.dll #endregion using System.ComponentMo