簽名算法:
Setp.1 確定待簽名參數
在請求參數列表中,除去sign參數外,其他需要使用到的參數皆是要簽名的參數。
在通知返回參數列表中,除去sign參數外,凡是通知返回回來的參數皆是要簽名的參數。
Setp.2 對參數進行排序
對於待簽名的所有參數,需要根據參數名首字符字典順序(ascii值大小)排序,若遇到相同首字符,則判斷第二個字符,以此類推。
Setp.3 生成待簽名字符串
將排序后的待簽名參數以“&“符號拼接, 形如:“參數名1=參數值1&參數名2=參數值2&….&參數名N=參數值N”。
Setp.4 生成簽名/驗證簽名
生成簽名:把待簽名字符串與商戶的私鑰一同放入RSA的簽名函數中進行簽名運算,從而得到簽名結果字符串(sign值)。
驗證簽名:把待簽名字符串、平台提供的公鑰、通知返回參數中的參數sign值三者一同放入RSA的簽名函數中進行簽名運算,來判斷簽名是否驗證通過。
1、公鑰、私鑰格式化(加上前后戳、每64位進行換行)
/**********************************私鑰格式化*************************************/
1 function formatPriKey($priKey) { 2 $fKey = "-----BEGIN PRIVATE KEY-----\n"; 3 $len = strlen($priKey); 4 for($i = 0; $i < $len; ) { 5 $fKey = $fKey . substr($priKey, $i, 64) . "\n"; 6 $i += 64; 7 } 8 $fKey .= "-----END PRIVATE KEY-----"; 9 return $fKey; 10 }
/**********************************公鑰格式化*************************************/
1 function formatPubKey($pubKey) { 2 $fKey = "-----BEGIN PUBLIC KEY-----\n"; 3 $len = strlen($pubKey); 4 for($i = 0; $i < $len; ) { 5 $fKey = $fKey . substr($pubKey, $i, 64) . "\n"; 6 $i += 64; 7 } 8 $fKey .= "-----END PUBLIC KEY-----"; 9 return $fKey; 10 }
2、私鑰簽名、公鑰驗簽
/**********************************私鑰簽名*************************************/
1 /** 2 * 生成簽名 3 * @param string $signString 待簽名字符串 4 * @param [type] $priKey 私鑰 5 * @return string base64結果值 6 */ 7 function getSign($signString,$priKey){ 8 $privKeyId = openssl_pkey_get_private($priKey); 9 $signature = ''; 10 openssl_sign($signString, $signature, $privKeyId); 11 openssl_free_key($privKeyId); 12 return base64_encode($signature); 13 }
/**********************************公鑰驗簽*************************************/
1 /** 2 * 校驗簽名 3 * @param string $pubKey 公鑰 4 * @param string $sign 簽名 5 * @param string $toSign 待簽名字符串 6 * @param string $signature_alg 簽名方式 比如 sha1WithRSAEncryption 或者sha512 7 * @return bool 8 */ 9 function checkSign($pubKey,$sign,$toSign,$signature_alg=OPENSSL_ALGO_SHA1){ 10 $publicKeyId = openssl_pkey_get_public($pubKey); 11 $result = openssl_verify($toSign, base64_decode($sign), $publicKeyId,$signature_alg); 12 openssl_free_key($publicKeyId); 13 return $result === 1 ? true : false; 14 }
3、公鑰加密、私鑰解密
/**********************************公鑰加密*************************************/
1 /** 2 *公鑰加密 3 *@param string $sign_str 待加密字符串 4 *@param string $public_key 公鑰 5 *@param string $signature_alg 加密方式 6 *@return string 7 */ 8 function get_public_sign($sign_str,$public_key,$signature_alg=OPENSSL_ALGO_SHA1){ 9 $public_key = openssl_pkey_get_public($public_key);//加載密鑰 10 openssl_sign($sign_str,$signature,$public_key,$signature_alg);//生成簽名 11 $signature = base64_encode($signature); 12 openssl_free_key($public); 13 return $signature; 14 }
/**********************************私鑰解密**************************************/
1 /** 2 *私鑰解密 3 *@param string $sign_str 待加密字符串 4 *@param string $sign sign 5 *@param string $private_key 私鑰 6 *@param string $signature_alg 加密方式 7 *@return bool 8 */ 9 10 function private_verify($sign_str,$sign,$private_key,$signature_alg=OPENSSL_ALGO_SHA1){ 11 $private_key = openssl_get_privatekey($private_key); 12 $verify = openssl_verify($sign_str, base64_decode($sign), $private_key, $signature_alg); 13 openssl_free_key($private_key); 14 return $verify==1;//false or true 15 }
示例代碼如下(私鑰簽名公鑰驗簽):
<?php $pubKey = '-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDr6H/ictALLsV9/63lPFSYDPQK gRwEM2FiewfR/BYaPGfpgdl8lelNYqFxnqBRKbGnbFOwOxOu7oiiPYaJxcSU94hI d3S0/UsSXyRfTaHT8ZZv+5luikQAG62hwkxqcSdL3aEMbqsHRfQ9RXiFAneiJJwZ 1D0nHPANfBA4UN+OXQIDAQAB -----END PUBLIC KEY-----'; $priKey = '-----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOvof+Jy0AsuxX3/ reU8VJgM9AqBHAQzYWJ7B9H8Fho8Z+mB2XyV6U1ioXGeoFEpsadsU7A7E67uiKI9 honFxJT3iEh3dLT9SxJfJF9NodPxlm/7mW6KRAAbraHCTGpxJ0vdoQxuqwdF9D1F eIUCd6IknBnUPScc8A18EDhQ345dAgMBAAECgYEAoNlVIQShn45TcBa97dhV4Zqr ZuIjRSX3V5uFeIKGW3smastzjAP3ICGI7Jx4uP5RuFMfOMD/Kb5QgTasHhIvdwe0 kuMdUqd8YCLgZaV1u02GWkp5I7bG2HRKAqfrpaExeOJt3Iqmggt208P3BNQLTOa2 NFtNqT+onI1dGwbC120CQQD2BbkrXPv+wGAKkcqIK77Bkrwpg7Iqj91uyVHBAleW AgfWDFA3rJb8jDCARHte2ehMImmhbsQmVBjdI1DNdYWLAkEA9XnEoVVIL5IA09s0 XtL/Na065loDTJsQZiumdi6VMn6zWafu6GFhS0w5DQdkjtA7qhwpftjVRaWtK0DX 4qpItwJAKxGrbfT0RI/HAHKvYxFNbrPSbu4YNa1D1Y422rQfQyqN1qIHNQfo0sN0 BjB27I73RMTNey5Z9l/IjoYNMjq9qwJABChZ0jm1jUi1xuDRlEGSnQAgHUKtB6Eg t/pJSXskf8RxmTUk8L6lfTb/SF81rs2MFSeA9GsLwbA6rJ7eiTJFJQJBAJnixcdp F6knRxyOUDhWoa8uYmnUdcyrfo4dnNyliJbNTTSw0LJAGZsCbo9EDqQIxDrqDa9X qj0yz6UT1JM37Tk= -----END PRIVATE KEY-----'; //$priKey = formatPriKey($priKey); //$pubKey = formatPubKey($pubKey); $params = [ "merchant_id"=>"1", "uid"=>"2122334455", "out_trade_id"=>"13423423423", "amount"=>"88", "subject"=>"活動紅包" ]; //獲取預處理字符串 $signString = getSignString($params); //預處理字符串為 //amount=88&merchant_id=1&out_trade_id=13423423423&subject=活動紅包&uid=2122334455 //獲取簽名 $sign = getSign($signString,$priKey); //生成的簽名為 //k8DMuhe+q9rDVDgzAk8lFQEE+tZAahXLiZWExmiYl83vJpZlnKTBghLd1DM8itNzw3JYGhxR8ueHCIkkGyVh0BiPuKYmXFyrCwLVif9sMWCu2DFoEDFARZClDRCfE5rV+IDmumCBfVyxFY/uW/DIMS7AO7GlrydW5aVZ6xYKtBw= //驗證簽名 $res = checkSign($pubKey,$sign,$signString); var_dump($res);//結果為 true /** * 生成簽名 * @param string $signString 待簽名字符串 * @param [type] $priKey 私鑰 * @return string base64結果值 */ function getSign($signString,$priKey){ $privKeyId = openssl_pkey_get_private($priKey); $signature = ''; openssl_sign($signString, $signature, $privKeyId); openssl_free_key($privKeyId); return base64_encode($signature); } /** * 校驗簽名 * @param string $pubKey 公鑰 * @param string $sign 簽名 * @param string $toSign 待簽名字符串 * @param string $signature_alg 簽名方式 比如 sha1WithRSAEncryption 或者sha512 * @return bool */ function checkSign($pubKey,$sign,$toSign,$signature_alg=OPENSSL_ALGO_SHA1){ $publicKeyId = openssl_pkey_get_public($pubKey); $result = openssl_verify($toSign, base64_decode($sign), $publicKeyId,$signature_alg); openssl_free_key($publicKeyId); return $result === 1 ? true : false; } /** * 獲取待簽名字符串 * @param array $params 參數數組 * @return string */ function getSignString($params){ unset($params['sign']); ksort($params); reset($params); $pairs = array(); foreach ($params as $k => $v) { if(!empty($v)){ $pairs[] = "$k=$v"; } } return implode('&', $pairs); } /** * 格式化私鑰 */ function formatPriKey($priKey) { $fKey = "-----BEGIN PRIVATE KEY-----\n"; $len = strlen($priKey); for($i = 0; $i < $len; ) { $fKey = $fKey . substr($priKey, $i, 64) . "\n"; $i += 64; } $fKey .= "-----END PRIVATE KEY-----"; return $fKey; } /** * 格式化公鑰 */ function formatPubKey($pubKey) { $fKey = "-----BEGIN PUBLIC KEY-----\n"; $len = strlen($pubKey); for($i = 0; $i < $len; ) { $fKey = $fKey . substr($pubKey, $i, 64) . "\n"; $i += 64; } $fKey .= "-----END PUBLIC KEY-----"; return $fKey; } ?>