PHP支付寶接口RSA驗證

這兩天一直困擾的PHP RSA簽名驗證問題終於解決了, 因爲以前RSA接觸的很少,再加上官方至今還未有PHP的SDK可供參考,所以走了一些彎路,寫在這裏和你們分享。
    雖然支付寶官方還未提供相關SDK,PHP確實能夠實現RSA方式的簽名,這點其實很重要,因爲不熟悉,在遇到困難的時候,常常會不禁自主地想到是否PHP不支持RSA簽名,乾脆用MD5得了,這樣就沒有了前進的動力。 其實說穿了MD5和RSA簽名,不一樣的只是簽名方式的區別,其餘的都同樣,所以我這裏主要說一下如何用RSA進行簽名和驗籤。  
    
首先你須要準備下面的東西:
    
php的openssl擴展裏已經封裝好了驗籤的方法openssl_verify。
    若是在Windows下的php.ini須要開啓Openssl模塊:   extension=php_openssl.dll

    
商戶私鑰:

    即RSA私鑰,按照手冊,按如下方式生成:php

    openssl genrsa -out rsa_private_key.pem 1024  

    
商戶公鑰:

    即RSA私鑰,按照手冊,按如下方式生成:
    openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pemandroid

    生成以後,按照手冊的說明,須要在簽約平臺上傳公鑰,須要注意的是,上傳的時候須要把全部的註釋和換行都去掉。  

    
另外手冊中還有以下命令:

    openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocryptios

    該命令將RSA私鑰轉換成PKCS8格式,對於PHP來講,不須要。  

    
支付寶公鑰:

    根據手冊,在簽約平臺得到。
    若是你直接複製下來的話,會獲得一個字符串,須要進行下面的轉換;
    1)把空格變成換行
    2)添加註釋
    好比你複製下來的公鑰是:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRBMjkaBznjXk06ddsL751KyYt算法

ztPFg0D3tu7jLqCacgqL+lbshIaItDGEXAMZmKa3DV6Wxy+l48YMo0RyS+dWze4M
UmuxHU/v6tiT0ZTXJN3EwrjCtCyyttdv/ROB3CkheXnTKB76reTkQqg57OWW+m9j

TCoccYMDXEIWYTs3CwIDAQAB,那轉換以後爲:
    -----BEGIN PUBLIC KEY-----函數

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRBMjkaBznjXk06ddsL751KyYt
ztPFg0D3tu7jLqCacgqL+lbshIaItDGEXAMZmKa3DV6Wxy+l48YMo0RyS+dWze4M
UmuxHU/v6tiT0ZTXJN3EwrjCtCyyttdv/ROB3CkheXnTKB76reTkQqg57OWW+m9j
TCoccYMDXEIWYTs3CwIDAQAB
-----END PUBLIC KEY-----
    把公鑰保存在文件裏。  
 

注意這個是2048位的公鑰應該是9行或者10行,不能爲1行,否則PHP的openssl_pkey_get_public沒法讀取,pub_key_id的結果爲false,若是沒有-----BEGIN PUBLIC KEY-----  和 -----END PUBLIC KEY-----  能夠本身加上,最後保存到一個rsa_public_key.pem文件中。

好了,如今已經有了全部的東西,先看簽名函數:   
 1 <?php
 2 /**
 3  * 簽名字符串
 4  * @param $prestr 須要簽名的字符串
 5  * return 簽名結果
 6  */
 7 function rsaSign($prestr) {
 8     $public_key= file_get_contents('rsa_private_key.pem');
 9     $pkeyid = openssl_get_privatekey($public_key);
10     openssl_sign($prestr, $sign, $pkeyid);
11     openssl_free_key($pkeyid);
12     $sign = base64_encode($sign);
13     return $sign;
14 }
15 ?>
注意點:

1.$prestr的內容和MD5同樣(參見手冊,但不包含最後的MD5密碼)
2.簽名用商戶私鑰
3.最後的簽名,須要用base64編碼
4.這個函數返回的值,就是此次請求的RSA簽名。

驗籤函數:學習

 1 <?php
 2 /**
 3  * 驗證簽名
 4  * @param $prestr 須要簽名的字符串
 5  * @param $sign 簽名結果
 6  * return 簽名結果
 7  */
 8 function rsaVerify($prestr, $sign) {
 9     $sign = base64_decode($sign);
10     $public_key= file_get_contents('rsa_public_key.pem');
11     $pkeyid = openssl_get_publickey($public_key);
12     if ($pkeyid) {
13         $verify = openssl_verify($prestr, $sign, $pkeyid);
14         openssl_free_key($pkeyid);
15     }
16     if($verify == 1){
17         return true;
18     }else{
19         return false;
20     }
21 }
22 ?>
注意點:
1.$ prestr 的內容和MD5同樣(參見手冊)
2.$sign是支付寶接口返回的sign參數用base64_decode解碼以後的二進制
3.驗籤用支付寶公鑰
4.這個函數返回一個布爾值,直接告訴你,驗籤是否經過    

支付寶官方提供的PHP版SDK demo中只對MD5加密方式進行了處理,但android 端和ios端 請求支付寶加密方式只能用RSA加密算法,這時服務端PHP就沒法驗證簽名了,因此須要對demo進行一些修改。

一、修改
alipay_notify.class.php文件 
verifyNotify 函數第46行 
$isSign = $this->getSignVeryfy($_POST, $_POST["sign"]);  
改爲
$isSign = $this->getSignVeryfy($_POST, $_POST["sign"], $_POST["sign_type"]);  

verifyReturn 函數第83行
$isSign = $this->getSignVeryfy($_GET, $_GET["sign"]);  
改爲  
$isSign = $this->getSignVeryfy($_GET, $_GET["sign"],  $ _GET ["sign_type"] );    

getSignVeryfy 函數 116行
function getSignVeryfy($para_temp, $sign) {
改爲
function getSignVeryfy($para_temp, $sign,  $sign_type) {  
 
getSignVeryfy 函數 127行
switch (strtoupper(trim($this->alipay_config['sign_type']))) {
    case "MD5" :
        $isSgin = md5Verify($prestr, $sign, $this->alipay_config['key']);
break;
    default :
        $isSgin = false;
}  
改爲
switch (strtoupper(trim($sign_type))) {
    case "MD5" :
        $isSgin = md5Verify($prestr, $sign, $this->alipay_config['key']);
break;
    case "RSA" :
        $isSgin = rsaVerify($prestr, $sign);
        break;  
    default :
        $isSgin = false;
}    
二、新建一個alipay_rsa.function.php文件
 1 <?php
 2 /* *
 3  * RSA
 4  * 詳細:RSA加密
 5  * 版本:3.3
 6  * 日期:2014-02-20
 7  * 說明:
 8  * 如下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶能夠根據本身網站的須要,按照技術文檔編寫,並不是必定要使用該代碼。
 9  * 該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
10  */
11 /**
12  * 簽名字符串
13  * @param $prestr 須要簽名的字符串
14  * return 簽名結果
15  */
16 function rsaSign($prestr) {
17     $public_key= file_get_contents('rsa_private_key.pem');
18     $pkeyid = openssl_get_privatekey($public_key);
19     openssl_sign($prestr, $sign, $pkeyid);
20     openssl_free_key($pkeyid);
21     $sign = base64_encode($sign);
22     return $sign;
23 }
24 /**
25  * 驗證簽名
26  * @param $prestr 須要簽名的字符串
27  * @param $sign 簽名結果
28  * return 簽名結果
29  */
30 function rsaVerify($prestr, $sign) {
31     $sign = base64_decode($sign);
32     $public_key= file_get_contents('rsa_public_key.pem');
33     $pkeyid = openssl_get_publickey($public_key);
34     if ($pkeyid) {
35         $verify = openssl_verify($prestr, $sign, $pkeyid);
36         openssl_free_key($pkeyid);
37     }
38     if($verify == 1){
39         return true;
40     }else{
41         return false;
42     }
43 }
44 ?>

最後要說的是官方提供的手冊上說的基本上都是正確的,只是有些地方沒有說的很詳細,開發的時候必定要多參考,大體就是這樣,祝你們好運。測試

相關文章
相關標籤/搜索