签名算法:
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; } ?>