摘要:工商銀行中國工商銀行提現支付域名項目名接口我們第一次使用支付請求對象,是為了將其生成簽名原串。第一次加密是將不包含屬性值的支付請求對象封裝的簽名原串和我們生成的私鑰共同加密成簽名字符串,放進支付請求對象中的屬性中。
引題
【備注】簽名原串的源碼放在git上了,請大家參看:項目源碼
筆者最近在做支付、調用天貓優惠券、綁定銀行卡相關的業務,在這些業務中,我們都需要將數據加密。然而,數據的加密方式不同,綁定銀行卡用md5加密,這不涉及金錢上的往來,使用MD5加密沒問題。然而,一旦涉及了金錢,比如支付業務,那么,這種方式并不好。因為黑客很有可能截取報文,修改密碼后盜取金額,因而,我們采用RSA的加密方式。這里以連連支付為講解示例。
講解連連支付之前,需要介紹非對稱加密算法。
非對稱加密我們在通過ip傳輸數據時,如果采用對稱加密,即一個主站和用戶之間可以使用相同的密鑰對傳輸內容進行加密,主站和用戶之間是知道彼此的密鑰。然而,ip報文就好比在官道上運輸糧草、黃金、物資,雖然相對來說比較安全,但很容易被人盯上。密鑰本身如果被盜,那么,再復雜的密鑰也無濟于事。自然的想法是在密鑰上再加密,這就是遞歸的窮舉問題了。
這并不是最好的辦法,有沒有一種方式,即報文被截取之后,黑客依然無計可施。這就出現了一種全新的算法,即RSA加密算法。它把密碼革命性地分成公鑰和私鑰,由于兩個密鑰并不相同。
首先通過openssl genrsa -out rsa_private_key.pem 1028 生成pkcs1格式的1028個字節的私鑰(適合PHP等前端),即:
MIICXgIBAAKBgQsyeT57L81ie1Lm1hEb7RVa9JszkhmuNAu7garMbmHInXRJBkqj
GWMqRFp0KQWYGGRYRqG59XVXYub3KuTE/9FamifG+d+EyUNFbwcG9H1g+kSnm868
MhBp1wr2zec/s47Bbx0fbtRYPXeQrkdzz6oAxVLoNDp+7eRixvlTe6c0LwIDAQAB
AoGBCx+1vBD9yHlSM2YIvS6VNmYKJDXzq3eZVR6PD3PRJWv8oQ37JiMqkY3oIkTM
jDYx5V6drQXliRGru/FJt8TOsNM7nmu1sGQH2Ae6WPHnqWHDJpSlEQ/rSzAv4XYx
WZtYWq/6ToT25foJ7e+BL2uMKKAq/64deiLt+K7hQWUi6nTBAkEDlqt/j/cYEGnT
eY2GBRTbLLLJGZ+c3hSHSS84n82l0U2qnNA3zrxshZc7hU6NTPrrQzmjIl0MGimP
VbDNwC59qQJBAx7IQx6ec1OoNA+chz1Xh/ipklcximKdPNW6QByEZ8B6lp74l2SJ
aISeqe+WCHvnk6FVpOTqC3rWmQWsVje42hcCQQGOZL9EKq8X5xzbuOEm8P1/q+UE
JLD9qj9lIIJY4vEHDLxxluas1A/n+0bHr+IdQS+njqZNb7ag3ecYDT2dG0xJAkB6
Fv/zUSKtebsjW7hsDtHwlvKQMzlEo2XmAQbFlRNKnzIgcDyrmDkKdDnjLdp0Hcw5
z55ZgtBoYR6YeGPhNnbXAkEC/hvl31bulAqTGdZsVYY6FEVn9TXbsF9mTFSyFbGH
XjjILiDu9dQasPVBP5vLNt+ClGJJJ36ffVaX7FSbHVs7iA==
然而,我們后臺使用的是Java,需要將其轉為pkcs8格式的私鑰,即:
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBCzJ5PnsvzWJ7UubW
ERvtFVr0mzOSGa40C7uBqsxuYciddEkGSqMZYypEWnQpBZgYZFhGobn1dVdi5vcq
5MT/0VqaJ8b534TJQ0VvBwb0fWD6RKebzrwyEGnXCvbN5z+zjsFvHR9u1Fg9d5Cu
R3PPqgDFUug0On7t5GLG+VN7pzQvAgMBAAECgYELH7W8EP3IeVIzZgi9LpU2Zgok
NfOrd5lVHo8Pc9Ela/yhDfsmIyqRjegiRMyMNjHlXp2tBeWJEau78Um3xM6w0zue
a7WwZAfYB7pY8eepYcMmlKURD+tLMC/hdjFZm1har/pOhPbl+gnt74Eva4wooCr/
rh16Iu34ruFBZSLqdMECQQOWq3+P9xgQadN5jYYFFNsssskZn5zeFIdJLzifzaXR
Taqc0DfOvGyFlzuFTo1M+utDOaMiXQwaKY9VsM3ALn2pAkEDHshDHp5zU6g0D5yH
PVeH+KmSVzGKYp081bpAHIRnwHqWnviXZIlohJ6p75YIe+eToVWk5OoLetaZBaxW
N7jaFwJBAY5kv0QqrxfnHNu44Sbw/X+r5QQksP2qP2Ugglji8QcMvHGW5qzUD+f7
Rsev4h1BL6eOpk1vtqDd5xgNPZ0bTEkCQHoW//NRIq15uyNbuGwO0fCW8pAzOUSj
ZeYBBsWVE0qfMiBwPKuYOQp0OeMt2nQdzDnPnlmC0GhhHph4Y+E2dtcCQQL+G+Xf
Vu6UCpMZ1mxVhjoURWf1NduwX2ZMVLIVsYdeOMguIO711Bqw9UE/m8s234KUYkkn
fp99VpfsVJsdWzuI
我們將pkcs8格式的私鑰轉化為公鑰,即
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQsyeT57L81ie1Lm1hEb7RVa9Jsz
khmuNAu7garMbmHInXRJBkqjGWMqRFp0KQWYGGRYRqG59XVXYub3KuTE/9FamifG
+d+EyUNFbwcG9H1g+kSnm868MhBp1wr2zec/s47Bbx0fbtRYPXeQrkdzz6oAxVLo
NDp+7eRixvlTe6c0LwIDAQAB
你會發現,不論是pkcs1的私鑰,還是pkcs8格式的私鑰,其與公鑰并不相等。因為, 這就是所謂的非對稱加密。私鑰是用來對公鑰加密信息解密的,需要保密。而公鑰是對信息進行加密,任何人都可以知道,包括hack。我們在傳輸的時候,雙方都遵守這個契約:
甲該訴乙,使用RSA算法進行加密,乙說,好的。
甲和乙分別根據RSA生成一對密鑰,互相發送公鑰。
甲使用乙的公鑰給乙加密報文信息。
乙收到信息,并用自己的密鑰進行解密。
乙使用同樣的方式給甲發送消息,甲使用相同方式進行解密。
其實,我們在使用連連支付時,也遵守這個規則。我們首先生成一對公私鑰。將生成的公鑰上傳到連連商戶站的后臺,連連那邊就接收到了我們的公鑰。我們再從連連商戶站的后臺下載連連公鑰,我們將私鑰和簽名原串共同加密生成簽名,這就是加簽。加簽后的數據和連連公鑰再次加密,通過HttpClient調用連連支付的接口,將加簽后的信息傳遞給連連。連連驗簽通過后,給我們回傳他們加簽后的簽名信息,我們這邊進行驗簽。這樣的加密方式是比較安全的。
上面提到了兩次加密和簽名原串,那么,簽名原串到底是什么?
簽名原串、加簽我們調用連連支付時,肯定涉及到金額,商戶號,簽名方式,銀行卡名稱的。這些就是支付請求對象,假設,我們現在有一個請求支付的javabean類:
/** * 這是支付父類的bean */ public class BaseRequestBean { private String oid_partner; private String sign; private String sign_type; } @Data @AllArgsConstructor @NoArgsConstructor public class PaymentRequestBean extends BaseRequestBean { private String api_version; private String card_no; private String flag_card; private String notify_url; private String no_order; private String dt_order; public String money_order; private String acct_name; private String bank_name; private String info_order; private String memo; private String brabank_name; }
在上面的父類中有一個sign屬性,這里存儲的是簽名原串加密后的數據。
什么是簽名原串?
即上面各個屬性(但不包含sign屬性)的值,按照一定格式,拼接而成的字符串。
為什么除去sign屬性?
sign屬性存儲的將簽名原串加密后的字符串。
我們首先要講支付請求對象賦值,如圖所示:
我們通過一系列的操作,將其轉變為如下格式的字符串,按照首字母由低到高的方式排名,如果首字母相同,再比較第二個,以此類推。。。具體怎么生成的,下面會提到。
acct_name=jack&api_version=1.2&bank_name=工商銀行&brabank_name=中國工商銀行&card_no=123456677756&dt_order=20190302023423&flag_card=1212121&info_order=提現支付&memo=ceshi&money_order=12.00¬ify_url=https://域名/項目名/接口&no_o...
我們第一次使用支付請求對象,是為了將其生成簽名原串。簽名原串和我們生成的pkcs8格式的私鑰加簽,第一次加密(加簽)涉及到我們自己生成的私鑰,如代碼所示:
/** * 簽名處理 * * @param prikeyvalue:私鑰 * @param sign_str:簽名原串 * @return */ public static String sign(String prikeyvalue, String sign_str) { try { //【1】獲取私鑰 KeyFactory keyFactory = KeyFactory.getInstance(PaymentConstant.SIGN_TYPE); //將BASE64編碼的私鑰字符串進行解碼 BASE64Decoder decoder = new BASE64Decoder(); byte[] encodeByte = decoder.decodeBuffer(prikeyvalue); //生成私鑰對象 PrivateKey privatekey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodeByte)); //【2】使用私鑰 // 獲取Signature實例,指定簽名算法(本例使用SHA1WithRSA) Signature signature = Signature.getInstance(PaymentConstant.MD5_WITH_RSA); //加載私鑰 signature.initSign(privatekey); //更新待簽名的數據 signature.update(sign_str.getBytes(BaseConstant.CHARSET)); //進行簽名 byte[] signed = signature.sign(); //將加密后的字節數組,轉換成BASE64編碼的字符串,作為最終的簽名數據 return new String(org.apache.commons.codec.binary.Base64.encodeBase64(signed)); } catch (Exception e) { e.printStackTrace(); } return null; }
我們將加簽后的數據放置在請求對象的sign中,如圖所示
我們第二次使用支付請求對象,這次對象中的sign已經存值。我們此時可以將加簽后的請求對象和連連公鑰共同加密。這次涉及到的是我們從商戶站下載下來的連連公鑰。調用連連的支付接口,如圖所示:
書寫簽名原串我們上面一直在提簽名原串,其實怎么生成的呢,我采用的是選擇排序算法,如代碼所示:
public static void main(String[] args) { JSONObject jsonObject = new JSONObject(); jsonObject.put("oid_partner", "12121212121"); jsonObject.put("api_version", "1.2"); jsonObject.put("sign_type", "rsa"); jsonObject.put("flag_card", "1212121"); jsonObject.put("notify_url", "https://域名/項目名/接口"); jsonObject.put("no_order", "20190302023423zby"); jsonObject.put("dt_order", "20190302023423"); jsonObject.put("money_order", "12.00"); jsonObject.put("card_no", "123456677756"); jsonObject.put("acct_name", "jack"); jsonObject.put("bank_name", "工商銀行"); jsonObject.put("info_order", "提現支付"); jsonObject.put("memo", "ceshi"); jsonObject.put("brabank_name", "中國工商銀行"); System.out.println(concatString(jsonObject,null)); } /** * Created By zby on 15:07 2019/3/6 * 拼接字符串 */ public static String concatString(JSONObject jsonObject, String type) { Listkeys = keysSort(jsonObject); if (null == keys && keys.size() <= 0) { return null; } if (StringUtils.isBlank(type)) { type = "&"; } StringBuilder concatBuilder = new StringBuilder(); for (String key : keys) { concatBuilder.append(key + "=" + jsonObject.getString(key) + type); } return StringUtils.substring(concatBuilder.toString(), 0, concatBuilder.length() - 1); } /** * Created By zby on 14:55 2019/3/6 * 獲取排序后的值 */ public static List keysSort(JSONObject jsonObject) { if (null == jsonObject && jsonObject.size() <= 0) { return null; } List keyList = new ArrayList<>(jsonObject.keySet()); if (null != keyList && keyList.size() > 0) { for (int i = 0; i < keyList.size() - 1; i++) { for (int j = 0; j < keyList.size() - (i + 1); j++) { String currKey = keyList.get(j); String afterKey = keyList.get(j + 1); if (StringUtils.isBlank(currKey) && StringUtils.isBlank(afterKey)) { throw new RuntimeException("當前值為空currKey=" + currKey + ",或者下一個值afterKey=" + afterKey); } char[] currKeyChars = currKey.toCharArray(); for (int k = 0; k < currKeyChars.length; k++) { //保證當前字符是有效字符,即在26個字母之中,不在,直接放到后面 if (validateLetter(currKeyChars[k])) { // 小于,不用排序,直接跳出 if (currKeyChars[k] < afterKey.charAt(k)) { break; // 等于,跳過此循環 } else if (currKeyChars[k] == afterKey.charAt(k)) { continue; // 大于,看清而定 } else { if (validateLetter(afterKey.charAt(k))) { keyList.set(j, afterKey); keyList.set(j + 1, currKey); } break; } } else { keyList.set(j, afterKey); keyList.set(j + 1, currKey); break; } } } } } return keyList; } /** * Created By zby on 14:52 2019/3/6 * 驗證字符 */ public static boolean validateLetter(Character c) { if (c == null) { return false; } return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z"); }
生成結果為:
acct_name=jack&api_version=1.2&bank_name=工商銀行&brabank_name=中國工商銀行&card_no=123456677756&dt_order=20190302023423&flag_card=1212121&info_order=提現支付&memo=ceshi&money_order=12.00¬ify_url=https://域名/項目名/接口&no_o...
支付并不復雜,說白了,無非是兩次加密。
第一次加密是將不包含sign屬性值的支付請求對象封裝的簽名原串和我們生成的私鑰共同加密成簽名字符串,放進支付請求對象中的sign屬性中。
第二次加密是我們使用連連支付的加密算法,將第一次加密的后支付請求對象和連連公鑰共同加密,封裝為pay_load,調用連連支付的的接口請求支付。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73589.html
摘要:錢可以存儲在自己的余額中,這就相當于微信錢包,余額可以提現到銀行卡的中。我們的第三方支付平臺是連連支付,杭州的一家公司。私鑰怎么加簽每個公司的加簽方式是不一樣的,支付寶有支付寶的加簽方式,微信有微信的加簽方式。 導讀 筆者在校期間,通過自學java。學校里也開過這門課,但是,講的都是一些基礎,比如java的表達式、基本類型、自定義類型等等。也都是很基礎的東西,就連lambda表達式都沒...
摘要:問題定義最長回文子串問題給定一個字符串,求它的最長回文子串長度??梢圆捎脛討B規劃,列舉回文串的起點或者終點來解最長回文串問題,無需討論串長度的奇偶性。 0. 問題定義 最長回文子串問題:給定一個字符串,求它的最長回文子串長度。 如果一個字符串正著讀和反著讀是一樣的,那它就是回文串。下面是一些回文串的實例: 12321 a aba abba aaaa tatt...
摘要:字符串方法還是比較強大的,做個筆記總結。美元符號連字符與正則表達式相匹配的子字符串。美元符號單引號位于匹配子字符串右側的文本。否則,第至個參數對應為捕獲組匹配項,倒數的兩個參數為匹配下標,原串。函數的匹配返回值,作為每次的匹配替換值。 javascript字符串方法replace還是比較強大的,做個筆記總結。 第一個參數 replace的第一個參數為字符串或者正則表達式。第二個參數...
閱讀 1419·2021-09-22 15:52
閱讀 1459·2019-08-30 15:44
閱讀 895·2019-08-30 14:24
閱讀 2705·2019-08-30 13:06
閱讀 2700·2019-08-26 13:45
閱讀 2782·2019-08-26 13:43
閱讀 1015·2019-08-26 12:01
閱讀 1436·2019-08-26 11:56