原文及更多文章請見個人博客:http://heartlifes.com
背景:
1.現在版本的支付寶wap支付需要到支付寶後臺獲取一個token,該字段是加密返回的,需要調用RSA類進行解密 2.銀聯APP支付是直接給sdk包,然後調用sdk包做tn獲取的,內部調用是個黑盒,開發是看不到的
現象:
1.在不調用銀聯APP SDK進行初始化的情況下,支付寶WAP支付整體流程都是正確的,token能拿到,也能正常解密 2.在進行一次銀聯支付後,即銀聯SDK初始化後,支付寶WAP支付開始一直報錯,現象爲token加密字符串能獲取,但是解密一直是亂碼
原因:
查看銀聯SDK後,發現在其CertUtil中有個init()靜態方法,其調用方法中,有以下兩行坑爹代碼:
Security.insertProviderAt(new BouncyCastleProvider(), 1);
Security.addProvider(new BouncyCastleProvider());
這兩行代碼是什麼意思呢? 在Security全局上下文環境,將BouncyCastleProvider這個算法類,直接變成默認算法類 導致了什麼結果呢? 當你調用支付寶提供的RSA類的時候,默認的算法類從JDK自帶的SUN RSA,變成了這個BouncyCastleProvider提供的RSA算法,直接導致和支付寶的加密算法不匹配,於是報錯亂碼。
解決:
修改RSA類如下,手動指定算法的provider類
package com.wonders.test;
public class RSA {
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
/**
* RSA驗簽名檢查
*
* @param content 待簽名數據
* @param sign 簽名值
* @param ali_public_key 支付寶公鑰
* @param input_charset 編碼格式
* @return 布爾值
*/
public static boolean verify(String content, String sign, String ali_public_key, String input_charset) {
try {
Provider provider = Security.getProvider("SunRsaSign");
KeyFactory keyFactory = KeyFactory.getInstance("RSA", provider);
byte[] encodedKey = Base64.decode(ali_public_key);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS, provider);
signature.initVerify(pubKey);
signature.update(content.getBytes(input_charset));
boolean bverify = signature.verify(Base64.decode(sign));
return bverify;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 解密
*
* @param content 密文
* @param private_key 商戶私鑰
* @param input_charset 編碼格式
* @return 解密後的字符串
*/
public static String decrypt(String content, String private_key, String input_charset) throws Exception {
System.out.println("alipay decrypt content..." + content);
System.out.println("alipay decrypt key..." + private_key);
PrivateKey prikey = getPrivateKey(private_key);
Provider provider = Security.getProvider("SunJCE");
Cipher cipher = Cipher.getInstance("RSA", provider);
cipher.init(Cipher.DECRYPT_MODE, prikey);
InputStream ins = new ByteArrayInputStream(Base64.decode(content));
ByteArrayOutputStream writer = new ByteArrayOutputStream(); // rsa解密的字節大小最多是128,將需要解密的內容,按128位拆開解密
byte[] buf = new byte[128];
int bufl;
while ((bufl = ins.read(buf)) != -1) {
byte[] block = null;
if (buf.length == bufl) {
block = buf;
} else {
block = new byte[bufl];
for (int i = 0; i < bufl; i++) {
block[i] = buf[i];
}
}
writer.write(cipher.doFinal(block));
}
return new String(writer.toByteArray(), input_charset);
}
/**
* 得到私鑰 * * @param key * 密鑰字符串(經過base64編碼) * @throws Exception
*/
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = Base64.decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
Provider provider = Security.getProvider("SunRsaSign");
KeyFactory keyFactory = KeyFactory.getInstance("RSA", provider);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
}
最後吐槽一下銀聯的SDK,拜託大哥你以後代碼寫的不要那麼暴力,稍微低調點OK?這麼修改全局參數,直接會導致工程中其它加解密類全部趴窩