四、公鑰和私鑰,加密和數字簽名

目錄

一、名詞解釋
 1、什麼是公鑰和私鑰
 2、什麼是加密和數字簽名
二、支付寶SDK支付流程解釋
 1、一些關鍵詞
 2、支付寶支付流程
 3、服務端對訂單信息加簽和對支付結果驗籤的簡單演示


本文涉及到支付寶SDK的內容,均摘自支付寶開放平臺。

因爲支付寶SDK使用RSA來加密和生成數字簽名,所以本文中涉及到的概念也都是針對於RSA的。



一、名詞解釋


1、什麼是公鑰和私鑰

一對兒密鑰生成後,會有公鑰和私鑰之分,我們需要把私鑰保存下來,而把公鑰發佈出去。一對兒公鑰和私鑰,不能由其中一個導出另一個。

比如使用支付寶SDK的時候,我們商戶端會生成一對兒密鑰A和B,A是私鑰,B是公鑰,支付寶也會生成一對兒密鑰C和D,C是私鑰,D是公鑰。我們商戶端需要把商戶端私鑰A保存下來,而把商戶端公鑰B發佈出去給支付寶,支付寶需要把支付寶私鑰C保存下來,而把支付寶公鑰D發佈出去給我們商戶端。

2、什麼是加密和數字簽名

加密是指我們使用一對兒密鑰中的一個來對數據加密,而使用另一個來對數據解密的技術,需要注意的是公鑰和私鑰都可以用來加密,也都可以用來解密,並不是規定死了只能用公鑰加密私鑰解密,但是加解密必須是一對兒密鑰之間的互相加解密,否則不能成功。

加密的目的是爲了保證數據的不可讀性,防止數據在傳輸過程中被截獲。

知道了加密這個概念,我們先看一下支付寶的加密過程,再引出數字簽名這個概念。接着第1小節的例子,當我們商戶端和支付寶互相發佈了公鑰之後,我們商戶端手裏就有商戶端私鑰支付寶公鑰兩個密鑰,支付寶手裏也有商戶端公鑰支付寶私鑰兩個密鑰。現在假設我們商戶端要給支付寶傳輸訂單信息,那麼爲了保證傳輸訂單信息時數據的安全性,結合我們商戶端手裏所擁有的密鑰,可以有兩套加密方案

  • 方案一:
    (商戶端)明文訂單信息 + 商戶端私鑰加密 = 加密訂單信息
    (--->網絡傳輸--->)
    (支付寶)加密訂單信息 + 商戶端公鑰解密 = 明文訂單信息

  • 方案二:
    (商戶端)明文訂單信息 + 支付寶公鑰加密 = 加密訂單信息
    (--->網絡傳輸--->)
    (支付寶)加密訂單信息 + 支付寶私鑰解密 = 明文訂單信息

貌似這兩套加密方案都能達到對訂單信息加密的效果,而且如果採用方案二,我們商戶端甚至只需要存儲支付寶公鑰這一個密鑰,都不用去申請一對兒商戶端的公私鑰來維護,支付寶也不用保存我們一堆商戶那麼多的商戶端公鑰了,這不是更簡單嗎,那爲什麼支付寶開放平臺讓我們採用的是方案一而不是方案二呢?下面來回答一下。

支付寶開放平臺說明:當我們採用RSA(1024位密鑰)來加密的時候,支付寶分配給所有商戶的支付寶公鑰都是一樣的,即支付寶針對那麼多的商戶只負責維護一對兒支付寶公私鑰,這就意味着支付寶公鑰隨便什麼人拿到後都是一樣的;而當我們採用RSA2(2048位密鑰)來加密的時候,支付寶會分配給每個商戶單獨的一個支付寶公鑰,即支付寶爲每一個的商戶單獨的維護一對獨立的支付寶公私鑰,當然一個商戶下的多個App的支付寶公鑰是一樣的。RSA是早就支持的,RSA2是最近才支持的。

知道了上面這段話,現在假設我們採用的是方案二,並且採用RSA加密(很多老業務並沒有使用RSA2加密),業務邏輯將會是下面這樣。

這就出問題了,RSA加密下,支付寶公鑰是公開發布的,而且所有的商戶用的都是同一個支付寶公鑰(上面聲明瞭RSA2加密下,支付寶才針對每個商戶維護了一對兒公私鑰),攻擊者很容易就能獲取到,而notify_url也很容易被截獲,那攻擊者拿到這兩個東西就可以做和商戶一樣的操作來發起支付請求,這樣就會一直給小明充錢了。

所以支付寶就需要確認支付請求確實是商戶發給他們的,而不是攻擊者發給他們的。這就用到了數字簽名,我們會通過方案一的實現流程來引出數字簽名的具體概念。如果我們採用的是方案一,我們商戶端保存的就是商戶端私鑰和支付寶公鑰,而支付寶保存的就是需要存着商戶端公鑰和支付寶私鑰的,業務邏輯將會是下面這樣。

這樣就可以保證交易的安全性了,我們也可以看出使用支付寶SDK保證交易的安全性注重的其實不是訂單信息是否加密,而是如何確保商戶端和支付寶能夠互相確認身份,訂單信息是明文的,但是後面拼接了數字簽名。

數字簽名其實就是明文數據加密之後得到的一個密文,只不過它是用私鑰加密生成的而已,我們一般會把數字簽名拼接在明文數據後面一起傳遞給接收方,接收方收到後用公鑰解密數字簽名,從而驗證發送方的身份、以及明文數據是否被篡改。數字簽名的生成過程其實就是一個加密過程,數字簽名的驗簽過程就是一個解密過程。

數字簽名的目的有兩個:一、發送方和接收方互相驗證身份;二、驗證數據是否被篡改。

加密和數字簽名可以單獨使用,當然也可以共同使用來完成數據的安全傳輸。


二、支付寶SDK支付流程解釋


從上面第一部分我們知道爲了確保商戶和支付寶交易的安全性,約定採用的是給訂單信息加數字簽名傳輸的方式。支付寶也爲我們提供了一鍵生成RSA密鑰的工具,可以幫助我們很快的生成一對商戶端公私鑰。以下會對支付寶SDK的支付流程做個大概的解釋,並點出實際開發中我們使用支付寶SDK時應該注意的地方。

1、一些關鍵詞

  • 商戶端私鑰

由我們商戶端自己生成的RSA私鑰(必須與商戶端公鑰是一對),生成後要保存在服務端,絕對不能保存在客戶端,也絕對不能從服務端傳輸給客戶端。

用來對訂單信息加簽,加簽過程一定要在服務端完成,絕對不能在客戶端做加,客戶端只負責用加簽後的訂單信息調起支付寶來支付。

  • 商戶端公鑰

由我們商戶端自己生成的RSA公鑰(必須與商戶端私鑰是一對),生成後需要填寫在支付寶開放平臺。

用來給支付寶服務端驗籤經過我們加簽後的訂單信息,以確保訂單信息確實是我們商戶端發給支付寶的,並且確保訂單信息在傳輸過程中未被篡改。

  • 支付寶私鑰

這個和我們就沒關係了,支付寶私鑰是他們自己生成的,也是他們自己保存的。

用來對支付結果進行加簽。

  • 支付寶公鑰

支付寶公鑰和支付寶私鑰是一對,也是支付寶生成的,當我們把商戶端公鑰填寫在支付寶開放平臺後,平臺就會給我們生成一個支付寶公鑰,我們可以複製下來保存在服務端,同樣不要保存在客戶端,並且不要傳輸給客戶端。

用來讓服務端對支付寶服務端返給我們的同步或異步支付結果進行驗籤,以確保支付結果確實是由支付寶服務端返給我們服務端的,而且沒有被篡改,對支付結果的驗籤工作也一定要在服務端完成。

2、支付寶SDK的支付流程

  • 第1步:用戶在我們客戶端裏選擇好訂單信息後(比如要充值11塊錢),然後選擇支付寶支付;

  • 第2步:我們客戶端會走個接口告訴服務端用戶是選擇了哪個訂單信息,服務端收到請求後,就會使用商戶端私鑰對這個訂單信息進行加密生成數字簽名,然後把這個數字簽名拼接在明文訂單信息後,形成一個加簽訂單信息orderString;(下面會用客戶端的代碼來簡單演示一下這個加簽的過程,讓我們看到訂單信息是如何通過加簽最終轉變爲調起支付寶那個orderString的)

  • 第3步:服務端通過第2步那個接口把orderString返回給客戶端;

  • 第4步、第5步:客戶端使用這個orderString調用一下支付寶SDK的提供的支付API發起支付就可以調起支付寶客戶端來支付了,與此同時當然支付寶服務端也收到了這個支付請求;

  • 第6步:支付寶服務端收到這個orderString後就會使用我們填寫在支付寶開放平臺的那個商戶端公鑰對這個加簽訂單信息進行驗籤,並處理交易來完成支付,然後就會得到一個交易結果(交易成功啊、失敗啊、中途取消啊等等),支付寶服務端又會使用支付寶私鑰對這個交易結果進行加簽;(支付寶服務端的加簽、驗籤操作和我們服務端做的加簽、驗籤操作是類似的)

  • 第7、8、9、10步: (實際開發中會忽略掉9、10步,這裏只是說明它們在幹什麼)支付寶服務端會把這個加簽後的交易結果同步的返回給我們客戶端,然後我們客戶端需要把這個加簽交易結果返回給服務端去驗籤(注意千萬不能在客戶端驗籤,倒不是說不能在客戶端驗籤,主要的意思是說不要把支付寶公鑰存在客戶端),服務端驗簽結束後把真正的支付結果返回給我們客戶端,客戶端根據這個支付結果作出相應的界面處理。但是,支付寶開放平臺說了:

    • 1、強烈建議我們開發者直接依賴支付寶服務端返回給我們服務端的異步支付結果(即第12步),而忽略同步支付結果,因爲這個同步支付結果有可能收不到啊,比如我們App在調用支付寶支付的過程中突然閃退了,那這個同步支付結果我們App是收不到的,自然也就無法傳給服務端去驗籤,那不就完犢子了嘛,但是異步支付結果支付寶服務端是肯定能確保返回給我們的服務端的;
    • 2、但是同步的支付結果和異步的支付結果都可以作爲支付完成的憑證,所以爲了簡化集成流程,我們客戶端就可以直接將同步支付結果作爲支付結束的一個憑證,在忽略掉第7、8、9、10步的基礎上,直接根據同步支付結果的狀態碼來作出相應的界面處理,甚至都不去關心支付結果的具體信息,而真正改變用戶金錢字段的業務操作則由服務端根據異步的支付信息經過驗籤後去做改變,當然如果服務端驗籤失敗了就說明支付結果被篡改了,是要報警的^_。
  • 第11步:客戶端直接將同步支付結果作爲支付結束的一個憑證,根據狀態碼來作出相應的界面處理;

  • 第12步、第13步:在第6步結束後,支付寶服務端會異步的把加簽支付結果返回給我們服務端,服務端用支付寶公鑰對這個支付結果進行驗籤,驗籤後根據支付結果作出實際的業務處理(如支付成功則給用戶的金錢字段加上11,如果失敗則不做處理等等);第13步是服務端會把實際的業務處理完畢後還需要在他們服務端SDK的回調裏把業務的處理結果返回給支付寶服務端(如實際業務處理成功就返回給支付寶服務端success就算結束本次交易,如實際業務處理失敗就返回給支付寶服務端fail,支付寶服務端就去再次調用服務端SDK來重新處理實際的業務邏輯直到成功,如果超過了一定的次數,服務端還是給支付寶服務端返回fail,說明是我們的系統出了問題,支付寶服務端就不會再觸發服務端SDK來重新處理實際的業務了,這種情況下咱們的客戶就會打我們的客服了,說我的支付寶都扣錢了,但爲什麼沒充值成功呢,我們就人工的給人家處理一下)。

3、服務端對訂單信息加簽和對支付結果驗籤的簡單演示

上面已經說過了:訂單信息的加簽和支付結果的驗籤是一定要在服務端做的,絕對不能在客戶端做。

3.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&timestamp=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&timestamp=2016-07-28 20:36:11&version=1.0&sign=GsSZgPloF1vn52XAItRAldwQAbzIgkDyByCxMfTZG%2FMapRoyrNIJo4U1LUGjHp6gdBZ7U8jA1kljLPqkeGv8MZigd3kH25V0UK3Jc3C94Ngxm5S%2Fz5QsNr6wnqNY9sx%2Bw6DqNdEQnnks7PKvvU0zgsynip50lAhJmflmfHvp%2Bgk%3D

3.2 對支付結果驗籤的簡單演示

假設我們服務端收到了來自支付寶服務端的支付結果,即:支付結果+數字簽名

那麼我們服務端就會對支付結果進行驗籤,怎麼個驗法呢?

  • 首先,服務端會把數字簽名截取下來,用支付寶公鑰把數字簽名給它解密,如果能解密就可以確定確實是支付寶發給我們服務端的支付結果,如果解不了就說明不是支付寶發給我們的支付結果,該報警就報警;

  • 然後,如果解密成功了,服務端還需要把解密後得到的明文支付結果和支付結果來做個對比,如果一樣則說明是支付結果沒被篡改過,如果不一樣則說明支付結果中途被人篡改了,可以打110了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章