數字簽名(又稱公鑰數字簽名、電子簽章)是一種類似寫在紙上的普通的物理簽名,但是使用了公鑰加密領域的技術實現,用於鑒別數字信息的方法。一套數字簽名通常定義兩種互補的運算,一個用於簽名,另一個用於驗證。
MD5是沒辦法反向破解的
在講解這個之前一定要了解一個東西:MD5是沒辦法反向破解的,如果你不了解這個那這原理理解起來有點難,當然一些簡單的組合通過一些網站的暴力破解,但我們的數據都是32甚至更多的MD5加密,所以這是幾乎不能破解的。了解了這一點,你才能不在這個原理上走彎路。
雙方約定:
服務方一般會給請求方一個appid和appkey, appid是請求方請求的唯一標識,這個是可以在網絡上傳輸的,而appkey是不在網絡上傳輸的。
請求方:
請求方如果需要請求,他會把請求的數據(這個請求數據中一定包含appid)按一定的規則(這個規則是雙方規定好的一個算法)進行過濾排序,然后把過濾排序后的參數加上appkey進行MD5加密碼(這也就是我們說的簽名),然后再把請求的參數加上簽名一起傳給服務方。
服務方:
服務方收到請求,會首先把請求參數的不需要或空的參數值過濾掉,然后按照同樣的算法進行過濾排序,然后拿出請求中的appid,在服務器數據庫搜索中appid對應的appkey,然后把參數加上appkey進行MD5加密同樣生成簽名,這時拿這個簽名去與請求方傳的參數中的簽名對比,如果相等,簽名成功。
注意點:
在整個過程中其實就是參數加appkey進行MD5加密后的一個對比,但是appkey是不在網絡上傳輸的,是請求方和服務方分別在本地保存的一個同樣的標識,這樣可以起到防止網絡數據劫持的危險。
核心程序:
里面只寫了md5的程序,rsa的略。
<?php /** * 資料來源:支付寶 * 簽名:所有交互的數據都需要簽名,除了 sign(簽名),和 sign_type(簽名方式:md5,rsa) * md5簽名 * 1.私鑰:只有你和服務器知道,用於加密數據,由英文字母和數據組成,共32位 * 2.請求時簽名:$sign=md5((待簽名字符串.md5私鑰)); * 3.返回時驗證:$_POST['sign']==md5((待驗證字符串,md5私鑰)) * rsa簽名 * 1.私鑰與公鑰:openssl生成 * 2.請求時簽名:$sign = rsaSign(待簽名字符串,私鑰); * 3.返回時驗證:$_POST['sign'] == rsaVerify(待驗證字符串,公鑰); */ /** * 演示md5簽名過程 */ /** * 除去數組中的空值和簽名參數 * @param $para 簽名參數組 * return 去掉空值與簽名參數后的新簽名參數組 */ function paraFilter($para) { $para_filter = array(); while (list ($key, $val) = each ($para)) { if($key == "sign" || $key == "sign_type" || $val == "")continue; else $para_filter[$key] = $para[$key]; } return $para_filter; } /** * 對數組排序 * @param $para 排序前的數組 * return 排序后的數組 */ function argSort($para) { ksort($para); reset($para); return $para; } /** * 把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串 * @param $para 需要拼接的數組 * return 拼接完成以后的字符串 */ function createLinkstring($para) { $arg = ""; while (list ($key, $val) = each ($para)) { $arg.=$key."=".$val."&"; } //去掉最后一個&字符 $arg = substr($arg,0,count($arg)-2); //如果存在轉義字符,那么去掉轉義 if(get_magic_quotes_gpc()){ $arg = stripslashes($arg); } return $arg; } /** * 生成md5簽名字符串 * @param $prestr 需要簽名的字符串 * @param $key 私鑰 * return 簽名結果 */ function md5Sign($prestr, $key) { $prestr = $prestr . $key; return md5($prestr); } class Submit{ public $_sign_type =''; public $_md5_key = ''; public $_private_key_path = ''; public function __construct($sign_type){ $this->_sign_type = strtoupper($sign_type); } /** * 對參數進行過濾,然后排序 * @param array $para_temp */ function filterPara($para_temp){ $para_filter = paraFilter($para_temp);//除去待簽名參數數組中的空值和簽名參數 return argSort($para_filter);//對待簽名參數數組排序 } /** * 生成簽名結果 * @param $para_sort 已排序要簽名的數組 * @return string 簽名結果字符串 */ function buildRequestMysign($para_sort) { //把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串 $prestr = createLinkstring($para_sort); $mysign = ""; switch ($this->_sign_type) { case "MD5":$mysign = md5Sign($prestr, $this->_md5_key);break; case "RSA" :$mysign = rsaSign($prestr, $this->_private_key_path);break; default :$mysign = ""; } return $mysign; } /** * 生成要發送給支付寶的參數數組 * @param $para_temp 請求前的參數數組 * @return 要請求的參數數組 */ public function buildRequestPara($para_temp) { $para_sort = $this->filterPara($para_temp);//對待簽名參數進行過濾 $para_sort['sign'] = $this->buildRequestMysign($para_sort);//生成簽名結果,並與簽名方式加入請求提交參數組中 $para_sort['sign_type'] = strtoupper(trim($this->_sign_type)); return $para_sort; } }
測試數據:
//1.准備要提交的字符串 $para_token = array( 'title'=> 'app', 'money'=>'5.60', ); $md5 = new Submit('md5'); // 2.准備md5私鑰 $md5->_md5_key = 'd41d8cd98f00b204e9800998ecf8427e'; // 3.生成要提交的數據 $a=$md5->buildRequestPara($para_token); echo "<pre>"; print_r($a); //打印結果:Array ( [money] => 5.6 [title] => app [sign] => 9ae9554fac509eea96e6e8efba6846d5 [sign_type] => MD5 ) //4.假設現在服務器返回以下數據,如何驗證此數據是由服務發送的 $return = array( 'title'=> 'app', 'money'=>'5.60', 'sign'=>'9ae9554fac509eea96e6e8efba6846d5', 'sign_type'=>'MD5' ); //5.按照加密流程:過濾數數組->把數組轉為字符串->生成簽名,檢查是否生成同樣的簽名 $md5_vertiry = new Submit('md5'); $md5_vertiry->_md5_key = 'd41d8cd98f00b204e9800998ecf8427e'; $return = $md5_vertiry->filterPara($return); echo $md5_vertiry->buildRequestMysign($return)."<br/>"; //如果生成的簽名相等,即驗證成功 if($a['sign']==$md5_vertiry->buildRequestMysign($return)){ echo "驗證成功"; }else{ echo "驗證失敗"; }