Android AES加解密

项目需要对特定数据进行加密,看了一下网上的很多实现,在不同版本的安卓上需要做一些特殊处理。那么我想试着编写一个通用性比较强,不需要对版本进行适配的AES加解密库。
这个库还可以将AES的密文进行Base64编码之后进行传输,算是功能上的补充。

AES 加密

生成加解密Key

使用SecretKeySpec来将给定数组生成对应的key,由于这里是AES加密,所以algorithm为"AES"

1
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);

使用Cipher进行加密

Cipher是javax.crypto中的类,他可以提供加解密的功能。使用getInstance方法来构建Cipher对象,并提供转换(加解密)模式。
这里我使用的是AES/ECB/PKCS5Padding模式,这里可以简单的说一下这几个参数的含义。
transformation使用分隔符分割的三个参数分别为:算法/模式/填充,如果找不到对应的方法,会抛出NoSuchAlgorithmException异常。

首先说算法部分,常用的算法有:

  • AES - Advanced Encryption Standard 高级数据加密标准
  • DES - Data Encryption Standard 数据加密标准
  • 3DES - Triple DES、DESede 进行了三重DES加密的算法

第二部分是,分组密码的工作模式,常用的模式有:

  • ECB - 电子密码本,每次加密均产生独立的密文分组,并且对其他的密文分组不会产生影响,也就是相同的明文加密后产生相同的密文
  • CBC - 密文链接,明文加密前需要先和前面的密文进行异或运算,也就是相同的明文加密后产生不同的密文

第三部分,分组密码的填充模式:

  • NoPadding - 不进行填充
  • ZeroPadding - 数据长度不对齐时使用0填充,否则不填充
  • PKCS5Padding - PKCS7Padding的子集,块大小固定为8字节
  • PKCS7Padding - 假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n,如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小
1
2
3
4
5
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
//加密模式
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
//按单部分操作加密或解密数据
return cipher.doFinal(data);

使用Cipher进行解密

解密方式与加密方式很相似,在init方法中使用的是Cipher.DECRYPT_MODE,也就是解密模式。由于此时解密完毕的是原文的字节数组,所以需要按照编码格式转换为String。
charset中填入编码时使用的字符集,这里是UTF-8

1
2
3
4
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] result = cipher.doFinal(data);
return new String(result, charset);

使用Base64编码

由于加密之后的byte二进制不便于传输,这里将字节数组编码为Base64,在解密时,先将Base64解码为密文原文,然后再进行解密。
这里使用Android sdk包下面的Base64,如果使用java库下面的Base64,则需要API版本大于26,也就是Android8.0。

1
2
3
4
//编码为Base64
Base64.encodeToString(input, Base64.NO_WRAP);
//Base64解码
Base64.decode(input, Base64.NO_WRAP);

第一个参数为编解码字节数组,第二个参数为编码方式,这个编解码模式,一般采用NO_WRAP

Base64编解码模式:

  • NO_WRAP:略去所有的换行符
  • CRLF:Win风格的换行符,使用CR LF这一对作为一行的结尾而不是Unix风格的LF
  • DEFAULT:使用默认的方法来编码
  • NO_PADDING:略去编码字符串最后的"="
  • URL_SAFE:编码时不使用对URL和文件名有特殊意义的字符来作为编码字符,具体就是以-和_取代+和/

完整工具类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
public class AesUtil {

//加密算法
private static final String algorithm = "AES";
//模式
private static final String transformation = "AES/ECB/PKCS5Padding";
//字符集
private static final String charset = "UTF-8";

/**
* 加密后转为Base64编码
*
* @param key 秘钥 16位
* @param data 原文
* @return Base64密文
*/
public static String encryptToBase64(String key, String data) {
try {
if (!TextUtils.isEmpty(data)) {
byte[] valueByte = encrypt(data.getBytes(charset), key.getBytes(charset));
return encodeBase64(valueByte);
}
return "";
} catch (Exception e) {
e.printStackTrace();
return "";
}
}

/**
* 解密Base64编码的密文
*
* @param key 秘钥 16位
* @param data Base64密文
* @return 原文
*/
public static String decryptFromBase64(String key, String data) {
try {
return decrypt(decodeBase64(data), key.getBytes(charset));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}

/**
* 编码为Base64
*
* @param input 原文
* @return 编码后密文
*/
public static String encodeBase64(byte[] input) {
return Base64.encodeToString(input, Base64.NO_WRAP);
}

/**
* Base64解码
*
* @param input Base64文本
* @return 原文
*/
public static byte[] decodeBase64(String input) {
try {
return Base64.decode(input.getBytes(charset), Base64.NO_WRAP);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}

/**
* 加密
*
* @param data 加密byte数组
* @param key 秘钥byte数组
* @return 密文byte数组
*/
public static byte[] encrypt(byte[] data, byte[] key) {
try {
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* 解密
*
* @param data 密文byte数组
* @param key 秘钥byte数组
* @return 原文
*/
public static String decrypt(byte[] data, byte[] key) {
try {
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] result = cipher.doFinal(data);
return new String(result, charset);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

最后推荐一本扩展阅读的书:《图解密码技术》这本书用通俗易懂的方式讲解了密码学的演进,里面就有关于AES具体的原理,以及分组密码究竟是什么,可以看一下。

参考

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×