Api是服务中重要的组成,可以为大客户提供更好、更便捷、更加实时的服务。大型的服务提供商,例如云存储服务、视频服务,他们大多都提供了设计复杂,功能全面的RESTfull API [
http://www.ruanyifeng.com/blog/2011/09/restful.html
],这是当前较为理想化的API设计。但是对于小型服务网站,没有必要嵌入如此复杂的API。可以用一些初级的Web api设计即可。
通常认为Api的设计有一下几点需要考虑的,本文首先讨论一下第一个问题。
API 中的身份认证。
API中的安全事项。
身份认证。在密码学中,身份认证是指,确认给你发消息的人就是他声称的那个人。例如,如果你跟Alice通信,身份认证是指你需要确认,与你通信的人就是真正的Alice 。
身份认证主要通过签名算法来实现,签名算法可以分为基于对称加密算法的签名和基于非对称加密算法的签名。其核心都在于,被验证着(本文中假如是Alice )通过间接证明她拥有某种Alice 专有的信息来自证是Alice 本人。
- 非对称加密算法用于签名
在非对称加密算法中,Alice用自己的私钥加密Bob要求的字符串,然后发送,Bob使用Alice的公钥解密,如果得到预定的字符串,则证明了发送方拥有Alice的私钥,即对方就是Alice。
签名:
sign = enc(Alice.pri, msg)
验证
msg = dec(Alice.pub,sign)
常见的可用于身份验证的非对称加密算法主要有RSA
, DSA
等。
彩带:数字货币中使用了椭圆曲线算法进行签名,比特币使用了ecdsa加密算法用于签名。
- 对称加密算法用于签名
与非对称加密算法用于签名同理,在利用对称加密算法签名时,也需要证明Alice拥有某种可验证的专有信息。那么,可以有多种实现方式,通常的原理是,Alice预先与Bob达成一个共享秘钥(key),这个key只有Alice和Bob两人知道,那么Alice通过用key加密Bob要求的信息,Bob再收到信息后,解密验证即可。
签名
cipher = enc(key, msg)
验证
msg = dec(key, cipher)
- 实际运用
在通常的实际运用总,我们使用对称加密算法进行签名、验证身份。原因如下:
1. 非对称加密算法复杂度远大于对称加密算法,使用非对称加密算法,服务器压力过大。
2. 方便进行秘钥管理。
优化
通常也不进行真正的加密操作,而采用了将秘钥附加在发送信息之后,取哈希值得方法得到。
即:
sign = md5( msg + secret_key)
这样,服务器在接收得到信息之后,再进行一次计算sign
的操作,对比自己计算出来的sign
与收到的sign
是否一致,一致则验证正确,反之则不正确。
这样做得好处在于,最大程度减轻了服务器计算代价,并且保持了对身份的验证效果。
- 代码示例
通常而言,对于http api,将请求的各个参数构造成http请求字符串,然后再末尾加上key=$secret_key
, 然后对整个串进行MD5
计算,得到的值为sign
,作为一个请求变量一起发送给服务器。
其中,为了保证服务器能够正确严重,需要对请求字段按照key
的字典序进行排序,因为不同的顺序会导致签名不同。
下面给出了 推送号 api接口中的签名计算函数
-PHP sdk中的签名
/**
* 生成签名
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
public function MakeSign($param, $key='')
{
//签名步骤一:按字典序排序参数
ksort($param);
$string = $this->ToUrlParams($param);
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$key;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
/**
* 格式化参数格式化成url参数
*/
public function ToUrlParams($param)
{
$buff = "";
foreach ($param as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
-Java sdk中的签名
/**
* 生成签名信息
* @param secretKey 产品私钥
* @param params 接口请求参数名和参数值map,不包括signature参数名
* @return
* @throws UnsupportedEncodingException
*/
public static String genSignature(String secretKey, Map<String, String> params) throws UnsupportedEncodingException {
String paramBuffer = buildHttpRequest(params);
paramBuffer += "&key="+secretKey;
// MD5是128位长度的摘要算法,用16进制表示,一个十六进制的字符能表示4个位,所以签名后的字符串长度固定为32个十六进制字符。
// 转为全部大写字母
return DigestUtils.md5Hex(paramBuffer.getBytes("UTF-8")).toUpperCase();
}
/**
* 将参数拼接称为url参数类型
* @param params key val
* @return
*/
public static String buildHttpRequest(Map<String,String> params){
String res = "";
if (params == null) {
return res;
}else{
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
for(int i = 0;i<keys.length;) {
res += keys[i]+"="+params.get(keys[i]);
if(++i < keys.length){
res += "&";
}
}
return res;
}
}