业务需要,小程序上送到后台的数据需要使用RSA加密,后台返回的数据需要RSA解密。折腾了好久,微信小程序的加解密结果始终不一样。终于解决这问题记录一下。
PS:后续使用过程中,发现小程序加密内容过长的话就会报错,又整理了一下小程序分段加解密放在另一篇文章中吧!
1.注意区分RSA私钥的类型,分为pkcs1和pkcs8, pkcs8格式的私钥主要用于Java中,微信小程序使用的私钥格式主要为pkcs1。所以要先生成pkcs1格式的私钥,然后转换成pkcs8格式私钥供java使用。
(需要用到OpenSSL,我是Win10,可以参考这里安装使用OpenSSL安装使用无需编译源代码原文链接)
生成pkcs1私钥:openssl genrsa -out rsa_private_key.pem 1024
pkcs1私钥转pkcs8私钥:openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform pem -nocrypt -out rsa_private_key_8.pem
pkcs1私钥转pkcs1公钥:openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
pkcs8私钥转pkcs8公钥:openssl rsa -in rsa_private_key_8.pem -pubout -out rsa_public_key_8.pem
(在私钥不使用密码的情况下,公钥的 pkcs1 和 pkcs8 格式内容应该是一模一样的)
java加密代码
public class UseRSAUtil {
/** *//**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/** *//**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
public static final String KEY_ALGORITHM = "RSA";
private static KeyFactory keyFactory = null;
public static String PrivateKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALiT90DF4YZ8hAjW\n" +
"j5FcjQdNXsCJnhWb8vV9F1iW3glIGKe5RVSiepfXr1nqhy1bK6/FhOPYRIFKzKOd\n" +
"+eeRNmOS+ryDdJxbo+/yoDI5u0CV4Ycu1yGzjr3NxW675TextllQKA34XQykZN+9\n" +
"h+eC83NM+Wn+7XH0sQHVXKtKA5APAgMBAAECgYB/0+F5V1GPvO4H3OhCqFD3DKsh\n" +
"Nm1n07/YKZCghqMw1GBWz+Vm7rtuKy5CXDzPuYMw0N7umFkhHXdJnN90csmx0OnA\n" +
"4E91eznT00Gn25ikio7/lruNTLabbTYdy3TT35q7ZHj0v8UeBSjl06VArt2detZt\n" +
"vl1Ag9uXh/J9sbv7wQJBAN8O8o+et7vQXhyq/nUjxsSfnjwxSOBjJn/P4kdhpAdI\n" +
"L0i4uFiSJA+N+KhKInxnQn8GWU2O3Sk9ZzfOniwLn2ECQQDT1jXAR9GUGp8Liep2\n" +
"0EvY3+P8Xv4IcbeUbsQcds+O+xbRFn3XdsQ40S2BdnzJ3eDa7EEFU5mfTb/JxQwG\n" +
"9pVvAkEAsPNUrWaF7zJvVmK4FglZp6XQi4O1gnxlkENECMiGrVgwWopfg7P7c3N2\n" +
"7VpSXUo1UDujIInTHOIfPgBlLh/SQQJBALuYJk+aCjHjBVedkeIYAiMT2n0izkpC\n" +
"EjXDha5DVPmkyAbvqQ+HIKvEKk1M1aOa9sZIlCgYaaHWo3IWKtl7wscCQQCFyINO\n" +
"rzyHZTtJfjxVJTHQNpBLMEkZ2wIa3pn14Ruv16YRRcfRSmSDLBnYrqTlTzqoDJid\n" +
"R4iA40R/dlGXErZ+";
public static String publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4k/dAxeGGfIQI1o+RXI0HTV7A\n" +
"iZ4Vm/L1fRdYlt4JSBinuUVUonqX169Z6octWyuvxYTj2ESBSsyjnfnnkTZjkvq8\n" +
"g3ScW6Pv8qAyObtAleGHLtchs469zcVuu+U3sbZZUCgN+F0MpGTfvYfngvNzTPlp\n" +
"/u1x9LEB1VyrSgOQDwIDAQAB";
static {
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 解密方法
* @param dataStr 要解密的数据
* @return 解密后的原数据
* @throws Exception
*/
public static String decrypt(String dataStr) throws Exception{
//要加密的数据
System.out.println("要解密的数据:"+dataStr);
//对私钥解密
Key decodePrivateKey = getPrivateKeyFromBase64KeyEncodeStr(PrivateKey);
//Log.i("机密",""+decodePrivateKey);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, decodePrivateKey);
byte[] encodedData = Base64.decode(dataStr);
byte[] decodedData = cipher.doFinal(encodedData);
String decodedDataStr = new String(decodedData,"utf-8");
System.out.println("私钥解密后的数据:"+decodedDataStr);
return decodedDataStr;
}
public static Key getPrivateKeyFromBase64KeyEncodeStr(String keyStr) {
byte[] keyBytes = Base64.decode(keyStr);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
Key privateKey=null;
try {
privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
} catch (InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return privateKey;
}
/**
* 获取base64加密后的字符串的原始公钥
* @param keyStr
* @return
*/
public static Key getPublicKeyFromBase64KeyEncodeStr(String keyStr) {
byte[] keyBytes = Base64.decode(keyStr);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
Key publicKey = null;
try {
publicKey = keyFactory.generatePublic(x509KeySpec);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return publicKey;
}
/**
* 公钥加密方法
* @param dataStr 要加密的数据
* @param dataStr 公钥base64字符串
* @return 加密后的base64字符串
* @throws Exception
*/
public static String encryptPublicKey(String dataStr) throws Exception{
//要加密的数据
System.out.println("要加密的数据:"+dataStr);
byte[] data = dataStr.getBytes();
// 对公钥解密
Key decodePublicKey = getPublicKeyFromBase64KeyEncodeStr(publicKey);
// 对数据加密
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, decodePublicKey);
byte[] encodedData = cipher.doFinal(data);
String encodedDataStr = new String(Base64.encode(encodedData));
System.out.println("公钥加密后的数据:"+encodedDataStr);
return encodedDataStr;
}
/**
* 使用公钥进行分段加密
* @param dataStr 要加密的数据
* @return 公钥base64字符串
* @throws Exception
*/
public static String encryptByPublicKey(String dataStr)
throws Exception {
//要加密的数据
System.out.println("要加密的数据:"+dataStr);
byte[] data = dataStr.getBytes();
// 对公钥解密
Key decodePublicKey = getPublicKeyFromBase64KeyEncodeStr(publicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 对数据加密
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, decodePublicKey);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
String encodedDataStr = new String(Base64.encode(encryptedData),"utf-8");
System.out.println("公钥加密后的数据:"+encodedDataStr);
return encodedDataStr;
}
/**
* 使用私钥进行分段解密
* @param dataStr 使用base64处理过的密文
* @return 解密后的数据
* @throws Exception
*/
public static String decryptByPrivateKey(String dataStr)
throws Exception {
byte[] encryptedData = Base64.decode(dataStr);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key decodePrivateKey = getPrivateKeyFromBase64KeyEncodeStr(PrivateKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, decodePrivateKey);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
String decodedDataStr = new String(decryptedData,"utf-8");
System.out.println("私钥解密后的数据:"+decodedDataStr);
return decodedDataStr;
}
/**
* 仅适用于 String 其他的自己写 ,我用不到 懒得写
* @param context 上下文
* @param spKey key
* @param defaultValue value
* @return
*/
public static String acquireDataBySputils(Context context, String spKey, String defaultValue) {
String spValue = (String) SPUtil.get(context, spKey, defaultValue);
String decryptResult;
try {
decryptResult = decryptByPrivateKey(spValue);
} catch (Exception e) {
e.printStackTrace();
return spValue;
}
return decryptResult;
}
public static String decryptData(String data) {
try {
return UseRSAUtil.decryptByPrivateKey(data);
} catch (Exception e) {
e.printStackTrace();
return "{\"code\":\"999999\", \"data\":null, \"message\":\"数据解析出错(来自前端)\"}";
}
}
public static void main(String[] args) throws Exception {
String jiami = UseRSAUtil.encryptByPublicKey("测试信息 ");
System.out.println(jiami);
String jiemi = UseRSAUtil.decryptByPrivateKey(jiami);
System.out.println(jiemi);
}
}
小程序RSA加密可以参考这个,有demo。原文链接