对称加密算法AES

对称加密算法加密和解密使用的是同一份秘钥,解密是加密的逆运算。

对称加密算法加密速度快,密文可逆,一旦秘钥文件泄露,就会导致原始数据暴露。

对称加密的结果一般使用Base64算法编码。

JDK8支持的对称加密算法主要有DES、DESede、AES、Blowfish,以及RC2和RC4等。

AES

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
import org.junit.Test;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class Demo {

@Test
public void test() throws Exception {
String value = "hankz";
System.out.println("待加密值:" + value);
// 加密算法
String algorithm = "AES";
// 转换模式
String transformation = "AES";
// --- 生成秘钥 ---
// 实例化秘钥生成器
KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
// 初始化秘钥长度
keyGenerator.init(256);
// 生成秘钥
SecretKey secretKey = keyGenerator.generateKey();
// 生成秘钥材料
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), algorithm);
System.out.println("AES秘钥:" + Base64.getEncoder().encodeToString(secretKey.getEncoded()));

// 实例化密码对象
Cipher cipher = Cipher.getInstance(transformation);
// 设置模式(ENCRYPT_MODE:加密模式;DECRYPT_MODE:解密模式)和指定秘钥
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
// 加密
byte[] encrypt = cipher.doFinal(value.getBytes());
System.out.println("AES加密结果:" + Base64.getEncoder().encodeToString(encrypt));
// 解密
// 设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] decrypt = cipher.doFinal(encrypt);
System.out.println("AES解密结果:" + new String(decrypt));
}
}

非对称加密算法RSA

公钥加密私钥解密,或者私钥加密公钥解密

该算法由美国麻省理工学院(MIT)的Ron Rivest、Adi Shamir和Leonard Adleman三位学者提出,并以这三位学者的姓氏开头字母命名,称为RSA算法。

  1. A构建RSA秘钥对;
  2. A向B发布公钥;
  3. A用私钥加密数据发给B;
  4. B用公钥解密数据;
  5. B用公钥加密数据发给A;
  6. A用私钥解密数据。
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
import org.junit.Test;

import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;


public class Demo {

@Test
public void test() throws Exception {
String value = "hankz";
// 加密算法
String algorithm = "RSA";
// 转换模式
String transform = "RSA/ECB/PKCS1Padding";
// 实例化秘钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 初始化,秘钥长度512~16384位,64倍数
keyPairGenerator.initialize(512);
// 生成秘钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 公钥
PublicKey publicKey = keyPair.getPublic();
System.out.println("RSA公钥: " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
// 私钥
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("RSA私钥: " + Base64.getEncoder().encodeToString(privateKey.getEncoded()));

// ------ 测试公钥加密,私钥解密 ------
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] pubEncryptBytes = cipher.doFinal(value.getBytes());
System.out.println("RSA公钥加密后数据: " + Base64.getEncoder().encodeToString(pubEncryptBytes));

cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] priDecryptBytes = cipher.doFinal(pubEncryptBytes);
System.out.println("RSA私钥解密后数据: " + new String(priDecryptBytes));

// ------ 测试私钥加密,公钥解密 ------
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] priEncryptBytes = cipher.doFinal(value.getBytes());
System.out.println("RSA私钥加密后数据: " + Base64.getEncoder().encodeToString(priEncryptBytes));

cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] pubDecryptBytes = cipher.doFinal(priEncryptBytes);
System.out.println("RSA公钥解密后数据: " + new String(pubDecryptBytes));
}
}

现在有RSA公钥:

1
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKBvz9cma+hXNiv2yXg6e1PyZhHVZm3bJXDvTJP2LyXo4vs9grH36Q9kNgr6quHtuU6fEoUxUu2zbEB8dkEWB9UCAwEAAQ==

RSA私钥:

1
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAoG/P1yZr6Fc2K/bJeDp7U/JmEdVmbdslcO9Mk/YvJeji+z2CsffpD2Q2Cvqq4e25Tp8ShTFS7bNsQHx2QRYH1QIDAQABAkEAjemZXORdesz52/WVzEVepai6ZHfw/Kdl/PmPMSoIFmz7mk55rprl2Akn2V0odSiHSnMWvDmOUIAvHaHF4Re4wQIhAN5GxVeF7ndyoWasxqIOVb6baNkUrapBM0nacPS4WA8JAiEAuMcvNM2Z1rW74JagoGlSIfRkNUqa+3LTCN/fK7VR2W0CICs/+gYduVjkpSMlW0ENKQH9m1kh/Oiz5xbnujLj676BAiBVGif7wdXgtcLaJYXFW7ygNtcQVFQdCz13EOTQVKpl4QIgY2YyH3vUYI2J68qCGtYjj5iNHUEwwze+Za1R7y0V43k=

还原为PublicKey和PrivateKey对象

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
import org.junit.Test;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;


public class Demo {

@Test
public void test() throws Exception {
String value = "hankz";
// 加密算法
String algorithm = "RSA";
// 转换模式
String transform = "RSA/ECB/PKCS1Padding";
// RSA公钥BASE64字符串
String rsaPublicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKBvz9cma+hXNiv2yXg6e1PyZhHVZm3bJXDvTJP2LyXo4vs9grH36Q9kNgr6quHtuU6fEoUxUu2zbEB8dkEWB9UCAwEAAQ==";
// RSA私钥BASE64字符串
String rsaPrivateKey = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAoG/P1yZr6Fc2K/bJeDp7U/JmEdVmbdslcO9Mk/YvJeji+z2CsffpD2Q2Cvqq4e25Tp8ShTFS7bNsQHx2QRYH1QIDAQABAkEAjemZXORdesz52/WVzEVepai6ZHfw/Kdl/PmPMSoIFmz7mk55rprl2Akn2V0odSiHSnMWvDmOUIAvHaHF4Re4wQIhAN5GxVeF7ndyoWasxqIOVb6baNkUrapBM0nacPS4WA8JAiEAuMcvNM2Z1rW74JagoGlSIfRkNUqa+3LTCN/fK7VR2W0CICs/+gYduVjkpSMlW0ENKQH9m1kh/Oiz5xbnujLj676BAiBVGif7wdXgtcLaJYXFW7ygNtcQVFQdCz13EOTQVKpl4QIgY2YyH3vUYI2J68qCGtYjj5iNHUEwwze+Za1R7y0V43k=";

// ------- 还原公钥 --------
byte[] publicKeyBytes = Base64.getDecoder().decode(rsaPublicKey);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

// ------- 还原私钥 --------
byte[] privateKeyBytes = Base64.getDecoder().decode(rsaPrivateKey);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

// ------- 测试加解密 --------
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] pubEncryptBytes = cipher.doFinal(value.getBytes());
System.out.println("RSA公钥加密数据: " + Base64.getEncoder().encodeToString(pubEncryptBytes));

cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] priDecryptBytes = cipher.doFinal(pubEncryptBytes);
System.out.println("RSA私钥解密数据: " + new String(priDecryptBytes));
}
}

一般来讲公司都有内部的工具类,不必编写如此繁琐的代码。

公钥是通过A发送给B的,其在传递过程中很有可能被截获,也就是说窃听者很有可能获得公钥。如果窃听者获得了公钥,向A发送数据,A是无法辨别消息的真伪的。因此,虽然可以使用公钥对数据加密,但这种方式还是会有存在一定的安全隐患。如果要建立更安全的加密消息传递模型,就需要AB双方构建两套非对称加密算法密钥,仅遵循“私钥加密,公钥解密”的方式进行加密消息传递;

RSA不适合加密过长的数据,虽然可以通过分段加密手段解决,但过长的数据加解密耗时较长,在响应速度要求较高的情况下慎用。一般推荐使用非对称加密算法传输对称加密秘钥,双方数据加密用对称加密算法加解密。

数字签名

  1. A在本地构建秘钥对,并将公钥发布给B;
  2. A使用私钥对数据进行签名;
  3. A发送签名和数据给B;
  4. B使用公钥对签名和数据进行验签。