Java 安全编程:基于口令加密(PBE)

在之前的文章中曾讲到过 DES 加密算法,类似这种加密算法都有一个密钥,密钥的长度决定了加密的安全性,但是这种密钥比较难记忆,是需要存储的。

PBE 算法是一种基于口令的加密算法,它并不是构建了一种新的加解密算法,而是对比如 DES 这样的算法进行了包装,采用随机数加口令的方式保证数据的安全。

在 PBE 算法中有口令一说,相当于我们记忆的密码,但是口令的长度以及安全性是有限的,所以这时需要采用随机数附加在口令上通过消息摘要算法经过迭代产生密钥。

使破译的难度加大。常用的 PBE 算法有 PBEWITHMD5andDES。

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
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import junit.framework.TestCase;

public class PBETest extends TestCase {

/** 算法 */
public static final String ALGORITHM = "PBEWITHMD5andDES";
/** 迭代次数 */
public static int ITERAT_COUNT = 100;

/**
* 构造一个 8 位的盐
* @return
*/
private byte[] initSalt() {
SecureRandom random = new SecureRandom();
return random.generateSeed(8);
}

/**
* 生成口令的 Key
* @param password
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private Key getKey(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
return factory.generateSecret(spec);
}

/**
* 加密
* @param password
* @param salt
* @param data
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public byte[] encript(String password, byte[] salt, byte[] data)
throws NoSuchAlgorithmException, InvalidKeySpecException,
NoSuchPaddingException, InvalidKeyException, BadPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException {
Key key = this.getKey(password);
// 实例化 PBE 参数材料
PBEParameterSpec params = new PBEParameterSpec(salt, ITERAT_COUNT);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, params);
return cipher.doFinal(data);
}

/**
* 解密
* @param password
* @param salt
* @param data
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public byte[] decript(String password, byte[] salt, byte[] data)
throws NoSuchAlgorithmException, InvalidKeySpecException,
NoSuchPaddingException, InvalidKeyException, BadPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException {
Key key = this.getKey(password);
// 实例化 PBE 参数材料
PBEParameterSpec params = new PBEParameterSpec(salt, ITERAT_COUNT);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, params);
return cipher.doFinal(data);
}

public void test() throws Exception{
String data = "需要处理的数据";
String password = "123456";
byte[] salt = this.initSalt();
byte[] cryptograph = this.encript(password, salt, data.getBytes("GBK"));
byte[] newData = this.decript(password, salt, cryptograph);

System.out.println(data);
System.out.println(Arrays.toString(cryptograph));
System.out.println(new String(newData, "GBK"));
}
}