安卓apk反编译的问题很让人头疼,对于一些机密的数据,不加密传输要是被提取出来是会造成巨大损失,研究了两种方式(相似的)
第一种:
1 首先需要有一对RSA的公钥和私钥 , 然后私钥留在服务器,公钥随apk发布(即安卓端写成文件,用的是时候读取,我感觉不是绝对安全,但相对方便);
2 通信的时候,首先由android端,随机生成一个字符串作为AES加密算法的key,用AES加密算法来加密与服务器传输的主要内容(因为AES速度比较快);
3 然后客户端再使用RSA的公钥将AES的key进行加密,与AES加密后的内容一同传输给服务器;
4 服务器端先用RSA的私钥将AES的key进行解密,然后使用这个key来解密传输的内容;
5 需要注意的是,android端的AES加密算法和服务器的AES加密算法的默认序列(默认序列就是决定根据传入key生成真正加解密key的一个东西,类似c++生成随机数的玩意)是不同的,所以网上的那些AES加密算法都只适合android和android,服务器和服务器之间的使用,不适合android和服务器之间使用;
第二种(我个人认为非常安全,但是效率可能没这么高):
1 首先安卓程序打开的时候携带imsi和公钥(生成方式自己定,可以每次换,但是我觉得可以每天或者一段时间换一次)请求java接口;
2 然后java端用安卓的公钥加密AES的key,返回给安卓端;
3 然后安卓端再使用AES的key加密数据加密后的内容传输给服务器;
4 服务器端解密传输的内容(key可以服务端定期更换);
上代码:
安卓AES:
import android.util.Base64;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Created by Administrator on 2018/10/14.
*/
public class Aes {
private static String AesKey = "12345678910Asdfx";// 密钥
private static String ivParameter = "12345678910Asdfx";// 密钥默认偏移,可更改
public static String Jie_Mi(String encData) throws Exception {
//获取AESkey
Key secretKey = new SecretKeySpec(AesKey.getBytes(), "AES"); //密钥
//获取加密工具
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//初始化加密工具
cipher.init(Cipher.DECRYPT_MODE,secretKey,new IvParameterSpec(ivParameter.getBytes()));
//还原文本框的base64文本为密文
byte[] bytes = Base64.decode(encData, Base64.DEFAULT);
//把密文解密为明文
byte[] bytes1 =cipher.doFinal(bytes);
return new String(bytes1);
}
public static String Jia_Mi(String srcData) throws Exception {
//获取AESkey
Key secretKey = new SecretKeySpec(AesKey.getBytes(), "AES"); //密钥
//获取加密工具
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//初始化加密工具
cipher.init(Cipher.ENCRYPT_MODE,secretKey,new IvParameterSpec(ivParameter.getBytes()));
//放入我们要加密的内容 并加密
byte[] bytes = cipher.doFinal(srcData.getBytes());
//得到的字节在进行Base64换算
byte[] base = Base64.encode(bytes,Base64.DEFAULT);
return new String(base).replaceAll("(\r\n|\r|\n|\n\r)", "");
}
public static String buildReqStr(String data) {
String dataNew = data.replace("\\n", "");
String origin = "{\"data\":\"" + dataNew + "\"}";
return origin;
}
}
javaAES:
加密:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Encoder;
public class Base64AesEncrypt {
/**
* BASE64加密
* @param base64Content 被加密的字符串
* @return
*/
public static String encryptBASE64(String base64Content) {
byte[] bt = base64Content.getBytes();
String aesContent = (new BASE64Encoder()).encodeBuffer(bt);
return aesContent;
}
/**
* AES加密
* @param aesContent 被Base64加密过的字符串
* @param key 秘钥
* @param ivParameter 偏移量
* @return
*/
public static String encryptAES(String aesContent,String key, String ivParameter) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = key.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(aesContent.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码。
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
解密:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
public class Base64AesDecipher {
/**
* base64解密
* @param content 被AES解密过的字符串
* @return
*/
public static String DecipherBase64(String content){
byte[] b = null;
String result = null;
if (content != null) {
BASE64Decoder decoder = new BASE64Decoder();
try {
b = decoder.decodeBuffer(content);
result = new String(b, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
/**
* @param content 被Base64+AES加密过的字符串
* @param aesKey 秘钥
* @param ivParameter 偏移量
* @return
*/
public static String decryptAES(String content, String aesKey,String ivParameter) {
try {
byte[] raw = aesKey.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(content);//先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original,"utf-8");
return originalString;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
}
RAS加密:
public class RSAUtils {
/**
* 生成公钥和私钥
*/
public static HashMap getKeys() throws NoSuchAlgorithmException {
HashMap map = new HashMap();
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
map.put("public", publicKey);
map.put("private", privateKey);
return map;
}
/**
* 公钥加密
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception {
/* 这块将padding变成和服务器默认的一致 */
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
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;
}
/**
* 私钥解密
*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
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 = "";
byte[][] arrays = splitArray(bcd, key_len);
for (byte[] arr : arrays) {
ming += new String(cipher.doFinal(arr));
}
return ming;
}
/**
* 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;
}
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;
}
/**
* 使用模和指数生成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");
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
需要注意的是安卓和java这里不太一样
//安卓
// Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding",new BouncyCastleProvider());
//java
Cipher cipher = Cipher.getInstance("RSA");