一、加密算法选择
密码学中两种常见的密码算法为:对称密码算法和非对称密码算法。
对称密钥加密,又称私钥加密,即信息的发送方和接收方用一个密钥去加密和解密数据。它的最大优势是加/解密速度快,适合于对大数据量进行加密,但密钥管理困难。
非对称密钥加密,又称公钥密钥加密。它需要使用一对密钥来分别完成加密和解密操作,一个公开发布,即公开密钥,另一个由用户自己秘密保存,即私用密钥。信息发送者用公开密钥去加密,而信息接收者则用私用密钥去解密。公钥机制灵活,但加密和解密速度却比对称密钥加密慢得多。
对称加密算法用来对敏感数据等信息进行加密,常用的算法包括:
DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合。
3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高。
AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高;
Mysql官网手册
https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_aes-encrypt
MySQL的内置加密函数里已经不推荐DES算法,所以选用AES算法来加解密账号密码。
二、加密策略
AES算法加解密都需要同一个key,如果这个key用来加密所有账号的密码,那么密码相同的不同账号得到的加密结果是相同的,这样会降低安全性,所以用可以唯一标识账号的aid来当做key,为了更不容易反向推导出key,实际应用中会用一个内部的salt字符串拼上账号aid来作为key。
三、实现
为了灵活和重用,没使用MySQL内置函数,改用Java实现。
AES算法实现与操作系统有关,本实现兼容Windows/Linux。
加密后获得的是byte[]类型结果,因此数据库内相应字段的类型应为BLOB,节省空间用TinyBlob类型就够。
加密算法需要使用外部类库javabase64-1.3.1.jar百度网盘下载链接
Java实现有两个类:
AESUtils:提供加解密静态方法
Base64Utils :辅助AESUtils类
AESUtils常用方法说明:
方法 |
返回 |
说明 |
参数 |
getSecretKey(String seed) |
String |
生成key |
seed: salt+aid |
encrypt(byte[] data, String key) |
byte[] |
加密 |
data: 密码明文getBytes(“UTF-8”) key: getScretKey方法结果 |
decrypt(byte[] data, String key) |
byte[] |
解密 |
data: 密码密文 key: getScretKey方法结果 |
AESUtils.java:
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
110
111
112
113
114
115
116
117
118
|
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.InputStream;
import
java.io.OutputStream;
import
java.security.Key;
import
java.security.SecureRandom;
import
javax.crypto.Cipher;
import
javax.crypto.CipherInputStream;
import
javax.crypto.CipherOutputStream;
import
javax.crypto.KeyGenerator;
import
javax.crypto.SecretKey;
import
javax.crypto.spec.SecretKeySpec;
public
class
AESUtils {
private
static
final
String ALGORITHM =
"AES"
;
private
static
final
int
KEY_SIZE =
128
;
private
static
final
int
CACHE_SIZE =
1024
;
public
static
String getSecretKey()
throws
Exception {
return
getSecretKey(
null
);
}
public
static
String getSecretKey(String seed)
throws
Exception {
try
{
KeyGenerator _generator = KeyGenerator.getInstance(ALGORITHM);
SecureRandom secureRandom = SecureRandom.getInstance(
"SHA1PRNG"
);
secureRandom.setSeed(seed.getBytes(
"UTF-8"
));
_generator.init(KEY_SIZE, secureRandom);
SecretKey secretKey = _generator.generateKey();
return
Base64Utils.encode(secretKey.getEncoded());
}
catch
(Exception e) {
throw
new
RuntimeException(
"初始化密钥出现异常"
);
}
}
public
static
byte
[] encrypt(
byte
[] data, String key)
throws
Exception {
Key k = toKey(Base64Utils.decode(key));
byte
[] raw = k.getEncoded();
SecretKeySpec secretKeySpec =
new
SecretKeySpec(raw, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return
cipher.doFinal(data);
}
public
static
void
encryptFile(String key, String sourceFilePath, String destFilePath)
throws
Exception {
File sourceFile =
new
File(sourceFilePath);
File destFile =
new
File(destFilePath);
if
(sourceFile.exists() && sourceFile.isFile()) {
if
(!destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
destFile.createNewFile();
InputStream in =
new
FileInputStream(sourceFile);
OutputStream out =
new
FileOutputStream(destFile);
Key k = toKey(Base64Utils.decode(key));
byte
[] raw = k.getEncoded();
SecretKeySpec secretKeySpec =
new
SecretKeySpec(raw, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
CipherInputStream cin =
new
CipherInputStream(in, cipher);
byte
[] cache =
new
byte
[CACHE_SIZE];
int
nRead =
0
;
while
((nRead = cin.read(cache)) != -
1
) {
out.write(cache,
0
, nRead);
out.flush();
}
out.close();
cin.close();
in.close();
}
}
public
static
byte
[] decrypt(
byte
[] data, String key)
throws
Exception {
Key k = toKey(Base64Utils.decode(key));
byte
[] raw = k.getEncoded();
SecretKeySpec secretKeySpec =
new
SecretKeySpec(raw, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
return
cipher.doFinal(data);
}
public
static
void
decryptFile(String key, String sourceFilePath, String destFilePath)
throws
Exception {
File sourceFile =
new
File(sourceFilePath);
File destFile =
new
File(destFilePath);
if
(sourceFile.exists() && sourceFile.isFile()) {
if
(!destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
destFile.createNewFile();
FileInputStream in =
new
FileInputStream(sourceFile);
FileOutputStream out =
new
FileOutputStream(destFile);
Key k = toKey(Base64Utils.decode(key));
byte
[] raw = k.getEncoded();
SecretKeySpec secretKeySpec =
new
SecretKeySpec(raw, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
CipherOutputStream cout =
new
CipherOutputStream(out, cipher);
byte
[] cache =
new
byte
[CACHE_SIZE];
int
nRead =
0
;
while
((nRead = in.read(cache)) != -
1
) {
cout.write(cache,
0
, nRead);
cout.flush();
}
cout.close();
out.close();
in.close();
}
}
private
static
Key toKey(
byte
[] key)
throws
Exception {
SecretKey secretKey =
new
SecretKeySpec(key, ALGORITHM);
return
secretKey;
}
}
|
Base64Utils.java:
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
|
import
it.sauronsoftware.base64.Base64;
import
java.io.ByteArrayInputStream;
import
java.io.ByteArrayOutputStream;
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.InputStream;
import
java.io.OutputStream;
public
class
Base64Utils {
private
static
final
int
CACHE_SIZE =
1024
;
public
static
byte
[] decode(String base64)
throws
Exception {
return
Base64.decode(base64.getBytes(
"UTF-8"
));
}
public
static
String encode(
byte
[] bytes)
throws
Exception {
return
new
String(Base64.encode(bytes));
}
public
static
String encodeFile(String filePath)
throws
Exception {
byte
[] bytes = fileToByte(filePath);
return
encode(bytes);
}
public
static
void
decodeToFile(String filePath, String base64)
throws
Exception {
byte
[] bytes = decode(base64);
byteArrayToFile(bytes, filePath);
}
public
static
byte
[] fileToByte(String filePath)
throws
Exception {
byte
[] data =
new
byte
[
0
];
File file =
new
File(filePath);
if
(file.exists()) {
FileInputStream in =
new
FileInputStream(file);
ByteArrayOutputStream out =
new
ByteArrayOutputStream(
2048
);
byte
[] cache =
new
byte
[CACHE_SIZE];
int
nRead =
0
;
while
((nRead = in.read(cache)) != -
1
) {
out.write(cache,
0
, nRead);
out.flush();
}
out.close();
in.close();
data = out.toByteArray();
}
return
data;
}
public
static
void
byteArrayToFile(
byte
[] bytes, String filePath)
throws
Exception {
InputStream in =
new
ByteArrayInputStream(bytes);
File destFile =
new
File(filePath);
if
(!destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
destFile.createNewFile();
OutputStream out =
new
FileOutputStream(destFile);
byte
[] cache =
new
byte
[CACHE_SIZE];
int
nRead =
0
;
while
((nRead = in.read(cache)) != -
1
) {
out.write(cache,
0
, nRead);
out.flush();
}
out.close();
in.close();
}
}
|