名詞解釋php
什麼是公鑰和私鑰算法
首先要明白公鑰和私鑰只是一個相對概念,就是說咱們不能單純的去稱呼一對密鑰中的一個爲公鑰,另外一個爲私鑰,它們的公私性老是相對於生成者來講的。一對密鑰生成後,保存在生成者手裏的就是生成者私鑰,生成者發佈出去的就是生成者公鑰,能夠看到咱們在稱呼它們的時候前面帶上了生成者,這樣能夠便於咱們理解,避免混淆概念。一對兒公私鑰,不能由其中的一個導出另外一個。express
能夠暫時這麼理解:
一對密鑰在剛生成的時候是沒有公私之分的,可是生成者會保留一個在本身手裏,發佈一個給別人用,正是這個「保留與發佈」的操做才使得這對密鑰有了公私之分,那麼對於生成者來講,保留在本身手裏的密鑰就被稱做生成者私鑰,發佈給別人用的那個密鑰就被稱做生成者公鑰,注意這裏的稱呼帶上了生成者,就是爲了代表一對密鑰的公私性老是相對於它們的生成者來講的。(實際中私鑰和公鑰在生成的時候已經具有了公私性,由於公鑰和私鑰是不一樣的生成機理,但這樣理解也是沒有錯的,有助於幫助咱們理清後面的關係)安全
好比:
咱們使用支付寶SDK的時候,咱們商戶端會生成一對密鑰A和B,支付寶也會生成一對密鑰C和D。微信
那麼若是咱們商戶端保存了A,而把B發佈給了支付寶,A就被稱做商戶端私鑰,B就被稱做商戶端公鑰。(注意稱呼公私鑰的時候帶上生成者的名字,這樣能夠便於咱們理解,避免混淆概念)app
固然咱們也能夠保存B,而把A發佈給支付寶,這樣B就被稱做商戶端私鑰,A就被稱做商戶端公鑰。(實際中不會這麼作,由於公私鑰已經提早肯定好了,它們的生成機理不一樣但這樣理解也是沒有錯的,有助於幫助咱們理清後面的關係)異步
一樣,假設支付寶保存了C,而把D發佈給了咱們,那麼C就被稱做支付寶私鑰,D就被稱做支付寶公鑰,反之同理。工具
什麼是加密和數字簽名,加密和數字簽名的聯繫與區別測試
(1)什麼是加密?微信支付
加密是指:咱們使用一對公私鑰中的一個密鑰來對數據進行加密,而使用另外一個密鑰來進行解密的技術,須要注意的是公鑰和私鑰均可以用來加密,也均可以用來解密,並非說規定死了只能私鑰來加密公鑰來解密,或者公鑰來加密私鑰來解密,但這個加解密必須是一對密鑰之間的互相加解密,不然不能成功。
加密的目的是:爲了確保數據傳輸過程當中的不可讀性,就是不想讓別人看到嘛。
接下來,咱們就着知道了加密這個概念,先看一下支付寶的加密過程,再引出數字簽名這個概念。
接着第1小節的好比,繼續:
當咱們商戶端和支付寶互相發佈了公鑰以後,咱們商戶端手裏就有商戶端私鑰和支付寶公鑰兩個密鑰,支付寶手裏也有商戶端公鑰和支付寶私鑰兩個密鑰。如今假設咱們商戶端要給支付寶傳遞訂單信息,那麼爲了保證傳遞訂單信息時數據的安全性(注意這個加密的目的,聽起來好像很合理,但其實咱們商戶端給支付寶傳遞訂單信息是明文傳輸的,只不過有數字簽名來保證數據沒被篡改而已,後面會說到,如今先按着這個錯誤的目的往下看,天然引出下面的反駁),針對咱們商戶端手裏擁有的密鑰,能夠有兩套加密方案,分別是:
方案一:
(商戶端)明文訂單信息+商戶端私鑰加密=加密訂單信息
(---->傳遞---->)
(支付寶)加密訂單信息+商戶端公鑰解密=明文訂單信息
方案二:
(商戶端)明文訂單信息+支付寶公鑰加密=加密訂單信息
(---->傳遞---->)
(支付寶)加密訂單信息+支付寶私鑰解密=明文訂單信息
貌似兩種加密方案都能達到對訂單信息加密傳輸的效果,那爲何咱們看到支付寶開發平臺讓咱們採用的是方案一而不是方案二呢?細想一下,採用方案二,咱們商戶端甚至只須要存儲支付寶公鑰這一個密鑰,都不用咱們去申請一對商戶端的公私鑰來維護,支付寶也不用保存咱們一堆商戶那麼多的商戶端公鑰了呀,這不是更簡單嗎,那真得爲何支付寶不讓咱們用方案二呢?下面來回答一下:
首先,支付寶開放平臺說明:當咱們採用RSA(1024位密鑰)來加密的時候,支付寶分配給全部商戶的支付寶公鑰都是同樣的,即支付寶針對那麼多的商戶只負責維護一對支付寶公私鑰;而當咱們採用RSA2(2048位密鑰)來加密的時候,支付寶會分配給每一個商戶單獨的一個支付寶公鑰,即支付寶爲每個的商戶單獨的維護一對獨立的支付寶公私鑰,固然一個商戶下的多個App的支付寶公鑰是同樣的。RSA是早就支持的,RSA2是最近才支持的。見此篇(https://openclub.alipay.com/read.php?tid=1495&fid=25&page=1)
好,知道了上面這段話,那如今假設咱們使用的是方案二(這樣咱們商戶端就省去商戶端申請商戶端公私鑰的這一過程,而只保留支付寶公鑰),而且採用RSA加密,業務邏輯將會是下面這樣:
方案二:訂單信息加密傳輸,而且是採用RSA加密
這就出問題了呀:notify_url很容易被竊取,一旦竊取後,壞蛋就能夠作和商戶同樣的操做來發起支付請求,由於採用RSA加密的話壞蛋一樣能夠獲取到支付寶公鑰,這樣就會一直給小明充錢。
所以,支付寶就須要確認支付請求確實是商戶發給他們的,而不是壞蛋發給他們的,這樣才能避免壞蛋惡意模擬商戶發起支付請求而給商戶形成損失。那如何才能達到這個驗證的效果呢?這就用到了數字簽名,那麼咱們會經過方案一的實現流程來引出數字簽名的具體概念。若是使用方案一的,咱們商戶手裏是須要存着商戶端私鑰和支付寶公鑰,而支付寶是須要存着商戶端公鑰和支付寶私鑰的,這樣方案一的業務流程以下:
方案一:對訂單信息添加數字簽名(簡稱加簽)來傳輸
這樣就能夠確保數據交易的安全性了,咱們也能夠看出使用支付寶SDK確保交易安全注重的其實不是訂單信息是否加密傳輸,而是如何確保商戶端和支付寶可以互相確認身份(造成數字簽名纔是加密的真正目的,而不是加密傳輸,這裏就反駁了上面的目的),咱們還能夠看出:
(2)數字簽名是什麼?
數字簽名其實就是一個:原文信息加密以後獲得的一個密文,而後把數字簽名拼接在要傳遞的數據後面,供接收方驗籤使用。例如上面的密文「你好,杭州」就能夠用做原文「充值2元,notify_url」的數字簽名,換句話說數字簽名的生成過程其實就是一個加密過程,而數字簽名的驗簽過程就是一個解密過程。
數字簽名的主要目的有兩個:1、用來互相驗證接收方和發送方的身份;2、在驗證身份的基礎上再驗證一下傳遞的數據是否被篡改過。所以使用數字簽名能夠用來達到數據的明文傳輸。
說到這裏,咱們再回想一下上面提到的RSA2加密,支付寶會分配給每一個商戶單獨的一個支付寶公鑰,即支付寶爲全部的商戶維護一對獨立的支付寶公私鑰,這樣的話,採用方案二也能完成數字簽名了呀,好像真得不須要咱們商戶去申請商戶端公鑰和私鑰了,只不過如今須要兼容以前的RSA加密,因此依舊採用了方案一來作數字簽名。可見數字簽名並不必定非得用RSA加密來生成,MD五、SHA-一、AES等加密算法均可以用來生成數字簽名,只不過實際開發要根據實際狀況來選擇適當的加密算法來生成數字簽名。一般咱們會選擇RSA加密來生成數字簽名(如支付寶支付就是這樣)或者MD5加密來生成數字簽名(如微信支付就是這樣)。
(3)加密和數字簽名的聯繫與區別
加密就是加密,而數字簽名的生成是經過加密獲得的一個密文,數字簽名的驗籤就是解密。
加密和數字簽名的目的不同(上面說過了)。
加密和數字簽名實際上是一對兄弟,能夠用來共同完成數據的安全傳輸,固然也能夠單獨使用。
支付寶支付流程解釋
由第一部分咱們能夠知道爲了確保商戶和支付寶交易的安全性,約定採用的是給訂單信息加數字簽名傳輸的方式。
那麼,支付寶也爲咱們提供了一鍵生成RSA密鑰的工具,能夠幫助咱們很快的生成一對商戶端公私鑰,一鍵生成RSA密鑰工具的下載地址
如下會對支付寶的支付流程作個大概的解釋,並點出實際開發中咱們使用支付寶SDK時應該注意的地方:
一些關鍵詞
商戶端私鑰:
由咱們本身生成的RSA私鑰(必須與商戶端公鑰是一對),生成後要保存在服務端,絕對不能保存在客戶端,也絕對不能從服務端下發
用來對訂單信息進行加簽,加簽過程必定要在服務端完成,絕對不能在客戶端作加簽工做,客戶端只負責用加簽後的訂單信息調起支付寶來支付
商戶端公鑰:
由咱們本身生成的RSA公鑰(必須與商戶端私鑰是一對),生成後須要填寫在支付寶開放平臺
用來給支付寶服務端驗籤通過咱們加簽後的訂單信息,以確保訂單信息確實是咱們商戶端發給支付寶的,而且確保訂單信息在傳輸過程當中未被篡改(下面會舉例子)
支付寶私鑰:
這個和咱們就不要緊了,支付寶私鑰是人家本身生成的,他們本身保存的
用來對支付結果進行加簽
支付寶公鑰:
支付寶公鑰和支付寶私鑰是一對,也是支付寶生成的,當咱們把商戶端公鑰填寫在支付寶開放平臺後,平臺就會給咱們生成一個支付寶公鑰,咱們能夠複製下來保存在服務端,一樣不要保存在客戶端,而且不要下發,避免被反編譯或截獲,而被篡改支付結果
用來讓服務端對支付寶服務端返給咱們的同步或異步支付結果進行驗籤,以確保支付結果確實是由支付寶服務端返給咱們服務端的,並且沒有被篡改,對支付結果的驗籤工做也必定要在服務端完成,絕對不能在客戶端驗籤,由於支付寶公鑰一旦存儲在客戶端用來驗籤,那就可能被反編譯,這樣就誰均可以驗籤支付結果並篡改了
支付寶支付流程
支付寶支付流程時序圖
第1步:用戶在咱們客戶端裏選擇好訂單信息後(好比要充值11塊錢),而後選擇支付寶支付;
第2步:咱們客戶端會走個接口告訴服務端用戶是選擇了哪一個訂單信息,服務端收到請求後,就會使用商戶端私鑰對這個訂單信息進行加密生成數字簽名,而後把這個數字簽名拼接在明文訂單信息後,造成一個加簽定單信息orderString;(下面會用客戶端的代碼來簡單演示一下這個加簽的過程,讓咱們看到訂單信息是如何經過加簽最終轉變爲調起支付寶那個orderString的)
第3步:服務端經過第2步那個接口把orderString返回給客戶端;
第4步、第5步:客戶端使用這個orderString調用一下支付寶SDK的提供的支付API發起支付就能夠調起支付寶客戶端來支付了,與此同時固然支付寶服務端也收到了這個支付請求;
第6步:支付寶服務端收到這個orderString後就會使用咱們填寫在支付寶開放平臺的那個商戶端公鑰對這個加簽定單信息進行驗籤,並處理交易來完成支付,而後就會獲得一個交易結果(交易成功啊、失敗啊、中途取消啊等等),支付寶服務端又會使用支付寶私鑰對這個交易結果進行加簽;(支付寶服務端的加簽、驗籤操做和咱們服務端作的加簽、驗籤操做是相似的)
第七、八、九、10步: (實際開發中會忽略掉九、10步,這裏只是說明它們在幹什麼)支付寶服務端會把這個加簽後的交易結果同步的返回給咱們客戶端,而後咱們客戶端須要把這個加簽交易結果返回給服務端去驗籤(注意千萬不能在客戶端驗籤,倒不是說不能在客戶端驗籤,主要的意思是說不要把支付寶公鑰存在客戶端),服務端驗簽結束後把真正的支付結果返回給咱們客戶端,客戶端根據這個支付結果做出相應的界面處理。可是,支付寶開放平臺說了:
一、強烈建議咱們開發者直接依賴支付寶服務端返回給咱們服務端的異步支付結果(即第12步),而忽略同步支付結果,由於這個同步支付結果有可能收不到啊,好比咱們App在調用支付寶支付的過程當中忽然閃退了,那這個同步支付結果咱們App是收不到的,天然也就沒法傳給服務端去驗籤,那不就完犢子了嘛,可是異步支付結果支付寶服務端是確定能確保返回給咱們的服務端的;
二、可是同步的支付結果和異步的支付結果均可以做爲支付完成的憑證,因此爲了簡化集成流程,咱們客戶端就能夠直接將同步支付結果做爲支付結束的一個憑證,在忽略掉第七、八、九、10步的基礎上,直接根據同步支付結果的狀態碼來做出相應的界面處理,甚至都不去關心支付結果的具體信息,而真正改變用戶金錢字段的業務操做則由服務端根據異步的支付信息通過驗籤後去作改變,固然若是服務端驗籤失敗了就說明支付結果被篡改了,是要報警的^_。
第11步:客戶端直接將同步支付結果做爲支付結束的一個憑證,根據狀態碼來做出相應的界面處理;
第12步、第13步:在第6步結束後,支付寶服務端會異步的把加簽支付結果返回給咱們服務端,服務端用支付寶公鑰對這個支付結果進行驗籤,驗籤後根據支付結果做出實際的業務處理(如支付成功則給用戶的金錢字段加上11,若是失敗則不作處理等等);第13步是服務端會把實際的業務處理完畢後還須要在他們服務端SDK的回調裏把業務的處理結果返回給支付寶服務端(如實際業務處理成功就返回給支付寶服務端success就算結束本次交易,如實際業務處理失敗就返回給支付寶服務端fail,支付寶服務端就去再次調用服務端SDK來從新處理實際的業務邏輯直到成功,若是超過了必定的次數,服務端仍是給支付寶服務端返回fail,說明是咱們的系統出了問題,支付寶服務端就不會再觸發服務端SDK來從新處理實際的業務了,這種狀況下我們的客戶就會打咱們的客服了,說個人支付寶都扣錢了,但爲何沒充值成功呢,咱們就人工的給人家處理一下)。
服務端對訂單信息加簽和對支付結果驗籤的簡單演示
上面已經說過了:訂單信息的加簽和支付結果的驗籤是必定要在服務端作的,絕對不能在客戶端作。
(1)服務端對訂單信息加簽的簡單演示
下面是在客戶端對訂單信息加簽的過程,僅僅是爲了模擬服務端來代表訂單信息是如何經過加簽最終轉變爲orderString的,千萬不要以爲訂單信息的加簽過程也能夠放在客戶端完成。
第1步:用AppID、簽名類型、商品標題、商品描述、商品價格啊等一些信息,構建一個支付寶SDK提供的訂單信息類的實體,如:
//將商品信息賦予AlixPayOrder的成員變量
Order* order = [Order new];// NOTE: app_id設置
order.app_id = appID;// NOTE: 支付接口名稱
order.method = @"alipay.trade.app.pay";// NOTE: 參數編碼格式
order.charset = @"utf-8";// NOTE: 當前時間點
NSDateFormatter* formatter = [NSDateFormatter new];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
order.timestamp = [formatter stringFromDate:[NSDate date]];// NOTE: 支付版本
order.version = @"1.0";// NOTE: sign_type設置
order.sign_type = @"RSA";// NOTE: 商品數據
order.biz_content = [BizContent new];
order.biz_content.body = @"我是測試數據";
order.biz_content.subject = @"1";
order.biz_content.out_trade_no = [self generateTradeNO]; //訂單ID(由商家自行制定)
order.biz_content.timeout_express = @"30m"; //超時時間設置
order.biz_content.total_amount = [NSString stringWithFormat:@"%.2f", 0.01]; //商品價格
第2步:使用支付寶SDK提供的API,把第一步構建的訂單信息實體轉換成一個訂單信息字符串,轉換後會是一個相似下面這樣的字符串:
app_id=2015052600090779&biz_content={"timeout_express":"30m","seller_id":"","product_code":"QUICK_MSECURITY_PAY","total_amount":"0.02","subject":"1","body":"我是測試數據","out_trade_no":"ZQLM3O56MJD4SK3"}&charset=utf-8&method=alipay.trade.app.pay&sign_type=RSA2×tamp=2016-07-28 20:36:11&version=1.0
第3步:調用支付寶SDK提供的API,用客戶端私鑰對第2步生成的訂單信息字符串進行加密生成數字簽名,生成的數字簽名相似於下面醬式兒:
GsSZgPloF1vn52XAItRAldwQAbzIgkDyByCxMfTZG%2FMapRoyrNIJo4U1LUGjHp6gdBZ7U8jA1kljLPqkeGv8MZigd3kH25V0UK3Jc3C94Ngxm5S%2Fz5QsNr6wnqNY9sx%2Bw6DqNdEQnnks7PKvvU0zgsynip50lAhJmflmfHvp%2Bgk%3D
第4步:按照支付寶規定的格式要求,把數字簽名拼接在原明文訂單信息字符串的後面就造成了最終可以調起支付寶支付的那個加簽定單信息--orderString,相似於下面這樣:
app_id=2015052600090779&biz_content={"timeout_express":"30m","seller_id":"","product_code":"QUICK_MSECURITY_PAY","total_amount":"0.02","subject":"1","body":"我是測試數據","out_trade_no":"ZQLM3O56MJD4SK3"}&charset=utf-8&method=alipay.trade.app.pay&sign_type=RSA2×tamp=2016-07-28 20:36:11&version=1.0&sign=GsSZgPloF1vn52XAItRAldwQAbzIgkDyByCxMfTZG%2FMapRoyrNIJo4U1LUGjHp6gdBZ7U8jA1kljLPqkeGv8MZigd3kH25V0UK3Jc3C94Ngxm5S%2Fz5QsNr6wnqNY9sx%2Bw6DqNdEQnnks7PKvvU0zgsynip50lAhJmflmfHvp%2Bgk%3D
(2)對支付結果驗籤的簡單演示
假設咱們服務端收到了來自支付寶服務端的支付結果,即:支付結果+數字簽名。
那麼咱們服務端就會對支付結果進行驗籤,怎麼個驗法呢?
首先,服務端會把數字簽名截取下來,用支付寶公鑰把數字簽名給它解密,若是能解密就能夠肯定確實是支付寶發給咱們服務端的支付結果,若是解不了就說明不是支付寶發給咱們的支付結果,該報警就報警;
而後,若是解密成功了,服務端還須要把解密後獲得的明文支付結果和支付結果來作個對比,若是同樣則說明是支付結果沒被篡改過,若是不同則說明支付結果中途被人篡改了,能夠打110了。
微信支付流程與支付寶支付流程的區別
微信支付的流程大致上和支付寶是同樣的,二者最關鍵的區別就是:
二者生成數字簽名的加密算法不一樣:微信支付使用MD5加密算法生成數字簽名的,而支付寶支付使用RSA加密算法生成數字簽名的。(可見數字簽名就是一個加密信息,並非說只有某種特定的加密方法才能生成數字簽名,只要是加密算法就能生成數字簽名)
還有一個不一樣是,支付寶支付,服務端會直接返回給咱們調起支付寶支付的orderString,而微信支付的話,服務端會返回給咱們一些信息,咱們須要將這些信息拼一個請求體來調起微信支付,不過都很簡單。
那麼在第二節,咱們已經演示了支付寶的加驗籤大概流程,這裏針對上面第一個區別,介紹一下微信支付數字簽名的生成的加驗籤流程:
咱們服務端加簽:
數字簽名=MD5加密(原明文訂單信息+該應用的Api密鑰)
發起請求的訂單信息=原明文訂單信息後面拼接上數字簽名
---->傳輸---->
微信服務端驗籤:
從發起請求的訂單信息截取原明文訂單信息和數字簽名
用來驗籤的數字簽名=MD5加密(原明文訂單信息+該應用的Api密鑰)
將用來驗籤的數字簽名和數字簽名對比,若是同樣就說明是指定商戶發起的請求,而不是壞蛋模擬的。
可見,這裏經過MD5加密也達到了加簽驗籤的效果,驗籤的關鍵參數就是該應用的Api密鑰,這個東西是在咱們申請微信支付功能的時候,在平臺上本身填寫的一個32爲的字符串,所以只有咱們商戶端和微信二者知道的,這樣就用一個Api密鑰達到了相似支付寶驗籤那樣公鑰私鑰的效果。