在Java中,加密英文文本通常涉及到使用加密算法,比如对称加密(如AES)或非对称加密(如RSA)。本文整理了几种java常用的几种加密方法。

参考文章:

Java常用加密方式

加密算法分类

  • 对称加密:指加密和解密的密钥相同,优点就是加解密的效率高且易于实现。
  • 非对称加密:指加密和解密的密钥不相同,也称为公私钥加密。
  • 不可逆加密:特征就是加密过程不需要密钥,并且加密后的数据不能被解密,只能输入同样的数据并且经过同样的不可逆加密算法才能获取同样的加密数据。

加密算法的应用

  • 数字签名:进行身份认证和数据完整性验证,主要用到了非对称密钥加密技术与数字摘要技术。
  • 数字证书:主要用来确保数字签名是安全有效的,数字证书由独立的证书发行机构发布。数字证书各不相同,每种证书可提供不同级别的可信度,该证书内包含用户的个人信息和他的公钥信息,同时还附有认证中心的签名信息。
  • MD5:对用户密码进行加密并进行保存。
  • 网络数据加密:保障传输的数据安全,即使被截获报文,在没有密匙的情况下也无法得知报文真实内容。
  • SSL协议:握手阶段使用的是非对称加密,在传输阶段使用的是对称加密,即在SSL上传送的数据是使用对称密钥加密的。同时HTTPS也是由SSL+HTTP协议构建的可进行加密传输、身份认证(确认客户端连接的目标主机是否是真实正确的主机)的网络协议。

对称加密算法

  • 优点:算法对消息双方公开、计算量小、加解密速度快、效率高。
  • 缺点:在数据传送前,发送方和接收方必须商定好秘钥,然后双方保存好秘钥。如果一方的秘钥被泄露,那么加密信息就会被破解。

DES介绍

DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,24小时内即可被破解。DES加密和解密过程中,**密钥长度都必须是8的倍数**。

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
public class DESHelper {
public String encrypt(String dataSource, String password) throws Exception {
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes(StandardCharsets.UTF_8));
// 创建一个密钥工厂,然后用它对desKey进行转换
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密钥初始化Cipher对象,ENCRYPT_MODE用于将Cipher初始化为加密模式的常量
cipher.init(Cipher.ENCRYPT_MODE,secretKey,random);
// 正式对数据进行加密操作
return new String(cipher.doFinal(dataSource.getBytes(StandardCharsets.UTF_8)));
}

public String decrypt(String src, String password) throws Exception {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE,secretKey,random);
return new String(cipher.doFinal(src.getBytes(StandardCharsets.UTF_8)));
}
}

加解密测试:

1
2
3
4
5
6
7
8
9
10
@Test
public void DESTest() throws Exception {
DESHelper desHelper = new DESHelper();
String source = "满天星辰不及你!";
System.out.println("原始数据:"+source);
byte[] encryptData = desHelper.encrypt(source, "1qaz2wsx");
System.out.println("加密后数据:"+encryptData);
byte[] decryptData = desHelper.decrypt(encryptData, "1qaz2wsx");
System.out.println("解密后数据:"+new String(decryptData));
}

IDEA介绍

  • 这种算法是在DES算法的基础上发展出来的,类似于三重DES
  • 发展IDEA也是因为感到DES具有密钥太短等缺点。
  • DEA的密钥为128位,这么长的密钥在今后若干年内应该是安全的。
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
public class IDEAHelper {
public static final String KEY_ALGORITHM = "IDEA"; // 密钥算法
// 加密、解密算法、工作模式、填充方式
public static final String CIPHER_ALGORITHM = "IDEA/ECB/ISO10126Padding";

/**
* 生成密钥,只有bouncycastle支持
* @return byte[] 二进制密钥
* */
public static byte[] initKey() throws Exception {
Security.addProvider(new BouncyCastleProvider()); // 加入bouncyCastle支持
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM); // 实例化密钥生成器
kg.init(128); // 初始化密钥生成器,IDEA要求密钥长度为128位
SecretKey secretKey=kg.generateKey(); // 生成密钥
return secretKey.getEncoded(); // 获取二进制密钥编码形式
}
/**
* 转换密钥
* @param key 二进制密钥
* @return Key 密钥
* */
public static Key toKey(byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key,KEY_ALGORITHM); // 实例化DES密钥
return secretKey; // 生成密钥
}

/**
* 加密数据
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密后的数据
* */
public static byte[] encrypt(byte[] data,byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider()); // 加入bouncyCastle支持
Key k = toKey(key); // 还原密钥
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 实例化
cipher.init(Cipher.ENCRYPT_MODE, k); // 初始化,设置为加密模式
return cipher.doFinal(data); // 执行操作
}
/**
* 解密数据
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密后的数据
* */
public static byte[] decrypt(byte[] data,byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider()); // 加入bouncyCastle支持
Key k = toKey(key); // 还原密钥
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k); // 初始化,设置为解密模式
return cipher.doFinal(data); // 执行操作
}
}

加解密测试:

1
2
3
4
5
6
7
8
9
10
11
@Test
public void IDEATest() throws Exception {
String str = "满天星辰不及你!";
System.out.println("原始数据:"+str);
byte[] key = IDEAHelper.initKey(); // 初始化密钥
System.out.println("密钥:"+ Base64.encodeBase64String(key));
byte[] data = IDEAHelper.encrypt(str.getBytes(), key); // 加密数据
System.out.println("加密后数据:"+Base64.encodeBase64String(data));
data=IDEAHelper.decrypt(data, key);
System.out.println("解密后数据:"+new String(data));
}

非对称加密算法

  • 优点:非对称加密与对称加密相比其安全性更好,只要私钥不泄露,很难被破解。
  • 缺点:加密和解密花费时间长、速度慢,只适合对少量数据进行加密。

RSA介绍

RSA是目前最有影响力和最常用的公钥加密算法。它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准RSA公开密钥密码体制的原理是:根据数论,寻求两个大素数比较简单,而将它们的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
public class RSAHelper {
public static final String KEY_ALGORITHM = "RSA";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
private static final String PUBLIC_KEY = "RSAPublicKey";
private static final String PRIVATE_KEY = "RSAPrivateKey";

public static byte[] decryptBASE64(String key) {
return Base64.decodeBase64(key);
}

public static String encryptBASE64(byte[] bytes) {
return Base64.encodeBase64String(bytes);
}

/**
* 用私钥对信息生成数字签名
* @param data 加密数据
* @param privateKey 私钥
* @throws Exception
*/
public static String sign(byte[] data, String privateKey) throws Exception {
// 解密由base64编码的私钥
byte[] keyBytes = decryptBASE64(privateKey);
// 构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 用私钥对信息生成数字签名
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(priKey);
signature.update(data);
return encryptBASE64(signature.sign());

}

/**
* 校验数字签名
* @param data 加密数据
* @param publicKey 公钥
* @param sign 数字签名
* @return 校验成功返回true 失败返回false
* @throws Exception
*/
public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
// 解密由base64编码的公钥
byte[] keyBytes = decryptBASE64(publicKey);
// 构造X509EncodedKeySpec对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取公钥匙对象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(pubKey);
signature.update(data);
// 验证签名是否正常
return signature.verify(decryptBASE64(sign));
}

public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
// 对密钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}

/**
* 用私钥解密
* @param data
* @param key
* @throws Exception
*/
public static byte[] decryptByPrivateKey(String data, String key) throws Exception {
return decryptByPrivateKey(decryptBASE64(data), key);
}

/**
* 用公钥解密
* @param data
* @param key
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
// 对密钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}

/**
* 用公钥加密
* @param data
* @param key
* @throws Exception
*/
public static byte[] encryptByPublicKey(String data, String key) throws Exception {
// 对公钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data.getBytes());
}

/**
* 用私钥加密
* @param data
* @param key
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
// 对密钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}

/**
* 取得私钥
* @param keyMap
* @throws Exception
*/
public static String getPrivateKey(Map<String, Key> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encryptBASE64(key.getEncoded());
}

/**
* 取得公钥
* @param keyMap
* @throws Exception
*/
public static String getPublicKey(Map<String, Key> keyMap) throws Exception {
Key key = keyMap.get(PUBLIC_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* 初始化密钥
* @throws Exception
*/
public static Map<String, Key> initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
Map<String, Key> keyMap = new HashMap(2);
keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥
keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥
return keyMap;
}
}

加解密测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void RSATest() throws Exception {
Map<String, Key> keyMap = initKey();
String publicKey = getPublicKey(keyMap);
String privateKey = getPrivateKey(keyMap);
System.out.println("公钥:" + publicKey);
System.out.println("私钥:" + privateKey);
String src = "满天星辰不及你!";
System.out.println("原始数据:" + src);
byte[] encryptByPrivateKey = encryptByPrivateKey(src.getBytes(), privateKey);
byte[] encryptByPublicKey = encryptByPublicKey(src, publicKey);
System.out.println("私钥加密后数据" + encryptByPrivateKey);
System.out.println("公钥加密后数据" + encryptByPublicKey);
String sign = sign(encryptByPrivateKey, privateKey);
System.out.println("数字签名:" + sign);
boolean verify = verify(encryptByPrivateKey, publicKey, sign);
System.out.println("签名验证结果:" + verify);
byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey);
byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey);
System.out.println("公钥解密私钥加密后的数据:"+ new String(decryptByPublicKey));
System.out.println("私钥解密公钥加密后的数据:"+ new String(decryptByPrivateKey));
}

不可逆算法

MD5介绍

MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被”压缩”成一种保密的格式(也就是把一个任意长度的字节串变换成一定长的十六进制数字串)。主要有以下特点:

  1. 压缩性: 任意长度的数据,算出的MD5值长度都是固定的。
  2. 容易计算: 从原数据计算出MD5值很容易。
  3. 抗修改性: 对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
  4. 强抗碰撞: 已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MD5Helper {
public String encode(String s) throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] md5Bytes = md5.digest(s.getBytes(StandardCharsets.UTF_8));
return md5ToString(md5Bytes);
}

// 将md5数组转化为16进制字符串
public String md5ToString(byte[] md5Bytes) {
StringBuilder hexValue = new StringBuilder();
for (int i = 0; i < md5Bytes.length; i++) {
int val = md5Bytes[i] & 0Xff;
if (val < 16){
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
}

加密测试:

1
2
3
4
5
6
7
8
@Test
public void MD5Test() throws NoSuchAlgorithmException {
MD5Helper md5Helper = new MD5Helper();
String src1 = "满天星辰不及你!";
String src2 = "满天星辰不及你!";
System.out.println("src1 加密后数据:" + md5Helper.encode(src1));
System.out.println("src2 加密后数据:" + md5Helper.encode(src2));
}

SHA1介绍

对于长度小于2^64位的消息,SHA1会产生一个160位(40个字符)的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。SHA1有如下特性

  • 不可以从消息摘要中复原信息;
  • 两个不同的消息不会产生同样的消息摘要,(但会有1x10 ^ 48分之一的机率出现相同的消息摘要,一般使用时忽略)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SHA1Helper {
public String encode(String str) throws NoSuchAlgorithmException {
if (null == str || str.length() == 0){
return null;
}
char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); // 创建SHA1算法消息摘要对象
mdTemp.update(str.getBytes(StandardCharsets.UTF_8)); // 使用指定的字节数组更新摘要
byte[] md = mdTemp.digest(); // 生成hash值的字节数组
// SHA1算法生成信息摘要的关键过程
int j = md.length;
char[] buf = new char[j*2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
}
}

加密测试:

1
2
3
4
5
6
7
8
@Test
public void SHA1Test() throws NoSuchAlgorithmException {
SHA1Helper sha1Helper = new SHA1Helper();
String src1 = "满天星辰不及你!";
String src2 = "满天星辰不及你!";
System.out.println("src1 加密后数据:" + sha1Helper.encode(src1));
System.out.println("src2 加密后数据:" + sha1Helper.encode(src2));
}

HMAC 介绍

HMAC 是密钥相关的哈希运算消息认证码Hash-based Message Authentication Code),HMAC 运算利用 哈希算法 (MD5、SHA1 等),一个密钥 和 一个消息 为输入,生成一个 消息摘要 作为 输出。HMAC 发送方 和 接收方 都有的 key 进行计算,而没有该 key 的第三方,则 无法计算 出正确的 散列值,这样就可以 防止数据被篡改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HMACHelper {
private Mac mac;
// MAC算法可选以下多种算法:HmacMD5/HmacSHA1/HmacSHA256/HmacSHA384/HmacSHA512
private static final String KEY_MAC = "HmacMD5";
public HMACHelper(String key) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), KEY_MAC);
mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
}

public String sign(String content){
return new String(mac.doFinal(content.getBytes(StandardCharsets.UTF_8)));
}

public boolean verify(String signature, String content){
byte[] result = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
return Arrays.equals(result, signature.getBytes(StandardCharsets.UTF_8));
}
}

加密测试:

1
2
3
4
5
6
7
8
9
@Test
public void HMACTest() throws Exception {
HMACHelper hmacHelper = new HMACHelper("123456");
String src = "满天星辰不及你!";
byte[] signature = hmacHelper.sign(src);
boolean b = hmacHelper.verify(signature, src);
System.out.println("src 生成数字签名:" + signature);
System.out.println("签名验证结果:" + b);
}