APP前端与后台数据RSA加密传输
程序员文章站
2024-03-14 17:32:04
...
加密算法概述
加密算法根据内容是否可还原分为:可逆加密和非可逆加密。首先,可逆加密是根据其加密与解密是否为同一个**又分为:对称加密和非对称加密。
所谓对称加密即是指在加密和解密时使用的是同一个**,这种加密方式有一个很大的缺点就是不安全,因为一旦加密用的**泄露了之后,就可以用这个****其他所有的密文。
非对称加密在加密和解密过程中使用不同的**,即公钥和私钥。公钥用于加密,所有人都可见,私钥用于解密,只有解密者持有。就算在一次加密过程中原文和密文发生泄漏,**者在知道原文、密文和公钥的情况下无法推理出私钥,很大程度上保证了数据的安全性。
此处,我们介绍一种非常具有代表性的非对称加密算法,利用RSA加密算法实现APP与后台数据传输。
话不多说,详细教程
1)导入JAR包
bcprov-jdk15-140.jar 提取码:4o1p 基于java1.5 的加密算法实现
2)添加工具类
public class RsaUtil {
private static final String RSA = "RSA";
private static final String NOPADDING = "RSA/ECB/NoPadding";
/**
* 生成公钥和私钥
*/
public static HashMap<String, Object> getKeys() {
HashMap<String, Object> map = new HashMap<String, Object>();
KeyPairGenerator keyPairGen = null;
try {
keyPairGen = KeyPairGenerator.getInstance(RSA);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
map.put("publicKey", publicKey);
map.put("privateKey", privateKey);
return map;
}
/**
* 使用模和指数生成RSA公钥
* 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,
* 不同JDK默认的补位方式可能不同,如Android默认是RSA/None/NoPadding】
*
* @param modulus 模
* @param exponent 指数
* @return
*/
public static RSAPublicKey getPublicKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 使用模和指数生成RSA私钥
* 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,
* 不同JDK默认的补位方式可能不同,如Android默认是RSA/None/NoPadding】
*
* @param modulus 模
* @param exponent 指数
* @return
*/
public static RSAPrivateKey getPrivateKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance(RSA,
new org.bouncycastle.jce.provider.BouncyCastleProvider());
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String encryption(String modulus, String exponent, String data){
RSAPublicKey pubKey = getPublicKey(modulus, exponent);
return encryptByPublicKey(data, pubKey);
}
/**
* 公钥加密
* @param data 密文
* @param publicKey
* @return
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) {
Cipher cipher;
try {
cipher = Cipher.getInstance(NOPADDING);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 私钥解密
* @param data 密文
* @param privateKey
* @return
*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance(NOPADDING);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 模长
int key_len = privateKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
// 如果密文长度大于模长则要分组解密
//String ming = "name:jjj";
StringBuffer ming = new StringBuffer();
byte[][] arrays = splitArray(bcd, key_len);
String str = null;
for (byte[] arr : arrays) {
str = new String(cipher.doFinal(arr), "UTF-8");
ming.append(str == null ? "" : str.trim());
}
return ming.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* ASCII码转BCD码
*/
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
byte[] bcd = new byte[asc_len / 2];
int j = 0;
for (int i = 0; i < (asc_len + 1) / 2; i++) {
bcd[i] = ASC_To_BCD(ascii[j++]);
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : ASC_To_BCD(ascii[j++])) + (bcd[i] << 4));
}
return bcd;
}
/**
* ASC码转BCD码
*/
public static byte ASC_To_BCD(byte asc) {
byte bcd;
if ((asc >= '0') && (asc <= '9'))
bcd = (byte) (asc - '0');
else if ((asc >= 'A') && (asc <= 'F'))
bcd = (byte) (asc - 'A' + 10);
else if ((asc >= 'a') && (asc <= 'f'))
bcd = (byte) (asc - 'a' + 10);
else
bcd = (byte) (asc - 48);
return bcd;
}
/**
* BCD转字符串
*/
public static String bcd2Str(byte[] bytes) {
char temp[] = new char[bytes.length * 2], val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
/**
* 拆分字符串
*/
public static String[] splitString(String string, int len) {
int x = string.length() / len;
int y = string.length() % len;
int z = 0;
if (y != 0) {
z = 1;
}
String[] strings = new String[x + z];
String str = "";
for (int i = 0; i < x + z; i++) {
if (i == x + z - 1 && y != 0) {
str = string.substring(i * len, i * len + y);
} else {
str = string.substring(i * len, i * len + len);
}
strings[i] = str;
}
return strings;
}
/**
* 拆分数组
*/
public static byte[][] splitArray(byte[] data, int len) {
int x = data.length / len;
int y = data.length % len;
int z = 0;
if (y != 0) {
z = 1;
}
byte[][] arrays = new byte[x + z][];
byte[] arr;
for (int i = 0; i < x + z; i++) {
arr = new byte[len];
if (i == x + z - 1 && y != 0) {
System.arraycopy(data, i * len, arr, 0, y);
} else {
System.arraycopy(data, i * len, arr, 0, len);
}
arrays[i] = arr;
}
return arrays;
}
}
3)先来验证工具类是否正常解密
public static void main(String[] args) {
// 生成公钥和私钥
HashMap<String, Object> map = RsaUtil.getKeys();
if(map == null){
System.out.println("错误错误,生成公私钥NO成功!");
return;
}
// 公钥
RSAPublicKey publicKey = (RSAPublicKey) map.get("publicKey");
// 私钥
RSAPrivateKey privateKey = (RSAPrivateKey) map.get("privateKey");
// 模
String modulus = publicKey.getModulus().toString();
// 公钥指数
String public_exponent = publicKey.getPublicExponent().toString();
// 私钥指数
String private_exponent = privateKey.getPrivateExponent().toString();
System.out.println("-----------------------------------------------");
System.out.println("模 :"+modulus);
System.out.println("公钥指数 :"+public_exponent);
System.out.println("私钥指数 :"+private_exponent);
System.out.println("-----------------------------------------------");
//明文
Map<String, Object> mapJson = new HashMap<String, Object>();
mapJson.put("account", "test");
mapJson.put("password", "123456");
String ming = JSON.toJSONString(mapJson, SerializerFeature.WriteMapNullValue);
System.out.println("加密前:"+ming);
//使用模和指数生成公钥和私钥
RSAPublicKey pubKey = RsaUtil.getPublicKey(modulus, public_exponent);
RSAPrivateKey priKey = RsaUtil.getPrivateKey(modulus, private_exponent);
//加密后的密文
String ciphertext = RsaUtil.encryptByPublicKey(ming, pubKey);
System.out.println("密文:"+ciphertext);
//解密后的明文
ming = RsaUtil.decryptByPrivateKey(ciphertext, priKey);
System.out.println("解密后:"+ming);
}
4)生成秘钥对
一切就绪完毕,接下来开始应用于APP前端与后台加密交互。首先后台生成秘钥接口提供给APP获取
/**
* 生成**对保存在回话中,并返回公钥
*/
@RequestMapping(value = "/getPublicKey", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
public @ResponseBody String getPublicKey(HttpServletRequest request, HttpServletResponse response) {
// 得到秘钥
HashMap<String, Object> kp = RsaUtil.getKeys();
if(kp == null){
return "ERROR";
}
// 得到公钥
RSAPublicKey publicKey = (RSAPublicKey) kp.get("publicKey");
// 保存到session(或者保存数据库)
request.getSession(true).setAttribute("myKeyPair", kp);
Map<String, Object> rsaMap = new HashMap<String, Object>();
// 模
rsaMap.put("modulus", publicKey.getModulus().toString());
// 公钥指数
rsaMap.put("publicExponent", publicKey.getPublicExponent().toString());
// 获取当前sessionID
rsaMap.put("sessionID", request.getSession().getId());
return JSON.toJSONString(rsaMap, SerializerFeature.WriteMapNullValue);
}
5)APP加密
APP获取到公钥指数后将数据进行加密,然后传输给后台。
String modulus = "模", public_exponent = "公钥指数", sessionId = "sessionId";
//要加密的数据
Map<String, Object> mapJson = new HashMap<String, Object>();
mapJson.put("account", "test");
mapJson.put("password", "123456");
String ming = JSON.toJSONString(mapJson, SerializerFeature.WriteMapNullValue);
//使用模和指数生成公钥
RSAPublicKey pubKey = RsaUtil.getPublicKey(modulus, public_exponent);
//开始加密,把密文以及sessionId一起传输到后台
String ciphertext = RsaUtil.encryptByPublicKey(ming, pubKey);
System.out.println("密文:"+ciphertext);
6)后台解密
String sessionId = "sessionId", ciphertext = "密文";
HttpSession session = MySessionContext.getSession(sessionId);
HashMap<String, Object> kp = (HashMap<String, Object>) session.getAttribute("myKeyPair");
RSAPrivateKey privateKey = (RSAPrivateKey) kp.get("privateKey");
// 解密
String content = decryptByPrivateKey(ciphertext, privateKey);
System.out.println("解密后:"+content);
遇到的坑
Android系统的RSA实现是"RSA/None/NoPadding",而标准JDK实现是"RSA/None/PKCS1Padding" ,这造成了在Android机上加密后无法在服务器上解密。