[toc]
问题一:支付宝在Android 5.0以上无法调起客户端,报错Service Intent must be explicit: Intent { act=com.eg.android.AlipayGphone.IAlixPay }
解决方案:修改build.gradle中的targetSDKVersion为19,至少我这样修改是可行的
问题二:支付宝在没有客户端的情况下调起网页支付后无法正常返回到支付前页面,一直显示正在处理中,如下图:
解决方案:更新SDK到最新15.0.8即可,原来的太旧了,还是2013年6月份更新的版本
一、 密钥对的生成
RSA加密解密,类似于支付宝中的加解密功能,以前的app使用的是DES加密即对称加密算法,只需要一个密钥;而采用RSA实现加解密需要一个密钥对,即公钥和私钥。所以首先要做的操作是生成一个密钥对,在window 7环境下,这里借用支付宝demo中的openssl命令行工具,密钥对的生成流程大致如下:
1、生成RSA私钥
genrsa -out rsa_private_key.pem 1024
2、将RSA私钥转换成PKCS8格式,然后在命令行模式下复制到txt文件,供最后解密使用,以下二选一即可(服务端使用pem私钥解密)
(1)pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
(2)pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -out rsa_private_key_pkcs8.pem -nocrypt
3、生成RSA pem格式公钥(Android使用pem公钥加密)
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
4、根据私钥(注:或者是pkcs8编码下的pem,即2(2))生成csr文件,这里面会输入相关地址及公司密码等信息
(1)req -new -key rsa_private_key.pem -out company.csr
(2)req -new -key rsa_private_key_pkcs8.pem -out company.csr
5、根据私钥(注:或者是2(2))以及csr文件生成一个10年有效期的der格式公钥(IOS使用der公钥加密)
x509 -req -in company.csr -out rsa_public_key.der -outform der -signkey rsa_private_key.pem -days 3650
加密使用公钥,不过Android使用的是pem公钥,IOS使用的是der公钥,以下是Android平台下加密方法:把一个字符串用公钥加密后得到一个byte数组,然后使用base64编码得到一个字符串返回,作为传递给服务端的参数(声明:encryptByPublicKey这个方法不是原创,以下解密核心方法类同)
public static String toEncryptStr(String param) {
// TODO Auto-generated method stub
try {
System.out.println("加密前:" + param);
byte[] data = param.getBytes();
byte[] encodedData = encryptByPublicKey(data,
RSAConfig.RSA_PUBLIC);
String encryptStr = Base64Utils.encode(encodedData);
System.out.println("加密后:" + encryptStr);
return encryptStr;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public static byte[] encryptByPublicKey(byte[] data, String publicKey)
throws Exception {
byte[] keyBytes = Base64Utils.decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm(),
new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > 117) {
cache = cipher.doFinal(data, offSet, 117);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * 117;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
三、服务端解密过程的实现
解密使用私钥,正好和加密的流程相反,不过就目前来说,只能实现不同平台分开解密,从代码可以看出来,Android和IOS的解密区别就在于是否添加了BouncyCastleProvider这个provider,具体原因未知
public static String toDecryptStr(String param) {
// TODO Auto-generated method stub
try {
System.out.println("解密前:" + param);
byte[] data = Base64Utils.decode(param);
byte[] decodedData = decryptByPrivateKeyForAndroid(data,
RSAConfig.RSA_PRIVATE);
String target = new String(decodedData, "utf-8");
System.out.println("解密后:" + target);
return target;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public static byte[] decryptByPrivateKeyForAndroid(byte[] encryptedData,
String privateKey) throws Exception {
byte[] keyBytes = Base64Utils.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm(),
new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > 128) {
cache = cipher.doFinal(encryptedData, offSet, 128);
} else {
cache = cipher
.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * 128;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
public static byte[] decryptByPrivateKeyForIOS(byte[] encryptedData,
String privateKey) throws Exception {
byte[] keyBytes = Base64Utils.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > 128) {
cache = cipher.doFinal(encryptedData, offSet, 128);
} else {
cache = cipher
.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * 128;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
加解密过程中主要使用了两个第三方jar包:bcprov-jdk15-140.jar、javabase64-1.3.1.jar
4、Java加解密
参考附录8
结束语:大功告成,在借鉴各位大神提供的加解密思路以及方法的前提下,加上个人的不断调试,终于实现了加解密过程;
附录:参考博文地址
[1].java中RSA加解密的实现
[2].Android RSA加密解密
[3].java与IOS之间的RSA加解密
[4].ios下使用rsa算法与php进行加解密通讯
[5].java中RSA加解密的实现
[6].android和java webservice RSA处理的不同
[7].OpenSSL小结
[8].Java加解密