本文转自我的博客,转载请申明地址:http://www.heartlifes.com/archives/4/
背景:###
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类
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?这么修改全局参数,直接会导致工程中其它加解密类全部趴窝