文章目录
- 1.对称/不对称加密
-
- 1、简介
- 2、RSA和AES介绍
-
- 2.1 RSA
- 2.2 AES
- 3、RSA/AES组合
- 4、Base64编码的作用
- 二、Java实现加解密/加验签
-
- 1、全局Config
- 2、RSA非对称加密
- 3、AES对称加密
- 三、加解密 starter实战
-
- 1、介绍
- 2、前期准备
-
- 2.1 引入依赖
- 2.2 封装公共相应类
- 2.3 定义加解密工具
- 2.4 定义两个注释
- 2.5 设置自定义key
- 加密和解密接口
-
- 3.1 介绍
- 3.2 接口加密
- 3.3 接口解密
- 4、打包发布starter
-
- 4.1 定义自动配置类
- 4.2 在线发布使用
- 5.使用新项目
1.对称/不对称加密
1、简介
只有一个密钥,加密和解密使用相同的密钥,因此称为对称加密。
有两个秘密钥匙,一个是公钥,另一个是私钥。不对称的特点是,公钥加密的私钥可以解密,但如果私钥加密,公钥无法解决,只能验证私钥是否加密
有两种常见的加密方法,一种是,一种是
2、RSA和AES介绍
2.1 RSA
:只需交换公钥;公钥/秘钥机制、公钥加密、私钥解密(或私钥加密、公钥解密);公钥加密,私钥解密;私钥签名,公钥验证
:解密速度慢,尤其是解密
2.2 AES
:用同一把钥匙加解密
:速度快,效率高;
:密钥交换问题
3、RSA/AES组合
这是因为一旦给出了秘钥,
在实际使用过程中经常使用
-
随机生成AES秘钥字符串
-
使用RSA公钥加密AES再用秘钥AES密钥加密真实内容
-
把skey=加密的AES秘钥,body=AES秘密钥加密的内容已经传过去
-
对面使用RSA私钥解密AES然后用秘钥AES密钥解密内容
4、Base64编码的作用
二、Java实现加解密/加验签
1、全局Config
public class Config {
public static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding"; public static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; //必须是PKCS8格式 public static final String CLIENT_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO/8ucCgOTJ7DCPC" "rCCL1VKDnUX61QnxwbAvpGp1/lletEIcjUouM7F0VvMHzViNLvpw7N7NBHPa 5gO" "js68t9hKMUh a6RTE34SWIqSDRPCzDKVWugsFb04o3vRl3rZ1z6B QDdW7xwOhEr" "PPoEqmjjIOjQPcU6xs0SPzSimOa1AgMBAAECgYAO5m0OBaSnerZNPhf7yVLMVbmd" "D67MeEMjUkHuDjdlixi8BhPLqESzXtrLKg/Y0KM7D2nVh3sgSldWoIjDUzpCx8Z2" "yHLU1K2wakMdBgEF3xeJPxxZRpP earl0SyLTA4hMxl48uAjn/mkPgzoMgQkqyQz" +
"5HOWjjsCLJFyEvqmoQJBAP5cBk0KXpHnCMgOupbi/pXDyaF1o+dCE97GaEdrV/0P" +
"uwDfYDYfY3wzd1QM7C4b4MmE+SNVpC0W9PyaMONJlN0CQQDxiPiGdwX9actMNJea" +
"JZ+k3BjCN+mM6Px7j/mtYcXWNZkyCXSXUBI62drZ0htenrh2qwichMlMgNJClvG6" +
"Gu+5AkEA30R7q2gstrkrNh/nnMZHXcJr3DPc2QNhWayin/4TT+hc51krpJZMxxqN" +
"5dMqBRcnavwzi9aCs6lxBcF6pCdUaQJANhd7uPls4PzRZ6abkQz9/LjB3rUQ29rN" +
"uIpc2yR7XuawAVG2x7BJ9N4XMhLoyD75hrH1AsCGKFjtPbZ6OjiQGQJAF2DbIodC" +
"uYb6eMZ8ux1Ab0wBEWWc5+iGgEVBNh22uZ/klE1/C0+KKzZhqgzaA/vPapq6dhuJ" +
"sNXlJia10PwYrQ==";
public static final String CLIENT_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv/LnAoDkyewwjwqwgi9VSg51F" +
"+tUJ8cGwL6Rqdf5ZXrRCHI1KLjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFI" +
"fmukUxN+EliKkg0TwswylVroLBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo" +
"0D3FOsbNEj80opjmtQIDAQAB";
public static final String SERVER_PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPGkxlAJPKR3BRxT" +
"PIeB3pDv117j8XbpuEik5UIOlY3GUtAV1sad5NNDUAnP/DB80yAQ8ycm9Xdkutuo" +
"f25Xlb7w0bRQNpfJlijx9eF8PsB6t63r8KAfWJlqbNHgN8AMK9P5XzVyN4YiEnUl" +
"Jh/EYiwLiYzflNnmnnfRrI4nUo8fAgMBAAECgYEAvwTxm81heeV4Tcbi33/jUBG4" +
"4BMzCzyA6DQp4wkiYju3tTS+Xq3seLEKcWdPxYi3YO7lODsM6j/fksrlSXXFMe1i" +
"ZAF3FNuDVZPz2zdFYS8vh6kdlDHMJAUnU/POMMWJ880MQDtkwTuzH8Tao8OKcAP4" +
"kc0QuG00wOrmuE+5gZECQQD9bqZkJsN+tj3+pxs57azy6B6gOqgm54/ujB+u63XU" +
"rO9Sf57asgF4OfUFltaVhjlUMSrWcgp6f4HSy7hBSKJpAkEA9BeML5iDIHOgTIws" +
"+ID55ELbzO7A/YtcYnUU09mkKCdonMXbXke+EhLApf5vX9ZmreoEfJCdsTnMEcQi" +
"fkjkRwJBALpf2TXl2/cfhs/zjG45f+rTEVK8UFTsDklb+yDkQC87TnTZLbWfGr2T" +
"wcFugDhOEXL9BYfXLiWQB6VB9Crug6ECQGEmTiFTbj0oSBCvaeauTsdO5PS3whAn" +
"u2lkeBmpcfCZXsWm6hyoKTpARHTMw789Mjjd/1Mkq96xxkr76U6h7FkCQHRc2elg" +
"Dh84wqHIptwa+moosVvd7aSzktuOB4CQRO10qKkSHVFuI+sl47A4KGzH/nX9ydUm" +
"tpsTnQAlXwBczd4=";
public static final String SERVER_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDxpMZQCTykdwUcUzyHgd6Q79de" +
"4/F26bhIpOVCDpWNxlLQFdbGneTTQ1AJz/wwfNMgEPMnJvV3ZLrbqH9uV5W+8NG0" +
"UDaXyZYo8fXhfD7Aeret6/CgH1iZamzR4DfADCvT+V81cjeGIhJ1JSYfxGIsC4mM" +
"35TZ5p530ayOJ1KPHwIDAQAB";
}
2、RSA非对称加密
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.security.*;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.springframework.util.Base64Utils;
public class RSACipher {
/** * 获取公钥 * @param key 密钥字符串(经过base64编码) * @return 公钥 */
public static PublicKey getPublicKey(String key) throws Exception {
// 按照X.509标准对其进行编码的密钥
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Utils.decode(key.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 生成公钥
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
/** * 获取私钥 * @param key 密钥字符串(经过base64编码) * @return 私钥 */
public static PrivateKey getPrivateKey(String key) throws Exception {
// 按照PKCS8格式标准对其进行编码的密钥,首先要将key进行base64解码
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Utils.decode(key.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
/** * 加密方法 * @param publicKey 公钥 * @param raw 待加密明文 * @return 加密后的密文 */
public static byte[] encrypt(String publicKey, byte[] raw) throws Exception {
Key key = getPublicKey(publicKey);
Cipher cipher = Cipher.getInstance(Config.RSA_ALGORITHM);
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
byte[] encryption = cipher.doFinal(raw);
// 最后将加密后的数据进行base64编码
return Base64Utils.encode(encryption);
}
/** * 解密方法 * @param privateKey 私钥 * @param enc 待解密密文 * @return 解密后的明文 */
public static byte[] decrypt(String privateKey, byte[] enc) throws Exception {
Key key = getPrivateKey(privateKey);
Cipher cipher = Cipher.getInstance(Config.RSA_ALGORITHM);
// 初始化
cipher.init(Cipher.DECRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
// 先进行base64解密,然后解码
return cipher.doFinal(Base64Utils.decode(enc));
}
/** * 签名 * @param privateKey 私钥 * @param content 要进行签名的内容 * @return 签名 */
public static String sign(String privateKey, byte[] content) {
try {
// privateKey进行base64编码,然后生成PKCS8格式私钥
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64Utils.decode(privateKey.getBytes()));
KeyFactory key = KeyFactory.getInstance("RSA");
PrivateKey priKey = key.generatePrivate(priPKCS8);
// 签名摘要算法
Signature signature = Signature.getInstance("SHA256WithRSA");
// 用私钥初始化此对象以进行签名
signature.initSign(priKey);
// 使用指定的字节数组更新签名或验证
signature.update(content);
// 获得签名字节
byte[] signed = signature.sign();
// 进行base64编码返回
return new String(Base64Utils.encode(signed));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/** * 验签 * @param publicKey 公钥 * @param content 要验签的内容 * @param sign 签名 * @return 验签结果 */
public static boolean checkSign(String publicKey, byte[] content, String sign) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 进行base64解码
byte[] encodedKey = Base64Utils.decodeFromString(publicKey);
// 生成公钥
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
// 签名摘要算法
Signature signature = Signature.getInstance("SHA256WithRSA");
// 用公钥初始化签名
signature.initVerify(pubKey);
// 使用指定的字节数组更新签名或验证
signature.update(content);
// base64解码后进行验证
return signature.verify(Base64Utils.decodeFromString(sign));
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static void main(String[] args) throws Exception {
//客户端代码
String text = "hello";
//使用服务端公钥加密
byte[] encryptText = RSACipher.encrypt(Config.SERVER_PUBLIC_KEY, text.getBytes());
System.out.println("加密后:\n" + new String(encryptText));
//使用客户端私钥签名
String signature = RSACipher.sign(Config.CLIENT_PRIVATE_KEY, encryptText);
System.out.println("签名:\n" + signature);
//服务端代码
//使用客户端公钥验签
boolean result = RSACipher.checkSign(Config.CLIENT_PUBLIC_KEY, encryptText, signature);
System.out.println("验签:\n" + result);
//使用服务端私钥解密
byte[] decryptText = RSACipher.decrypt(Config.SERVER_PRIVATE_KEY, encryptText);
System.out.println("解密后:\n" + new String(decryptText));
}
}
输出结果
加密后:
ODdEkwo1RgRW8UMoHXPKe9Gwcp6lTCkg4P/Ra3gfkrO+Fw6pSgo0H54nMC5sYSsoUVy1wy2/QXeLSwR6Obfl7SU7DeW+XdGee83O2kgdsDQPbYFwlPYTd0cdOmWwZxtgEOIB9d5G75Iut4kci15vrhXZVtku92U+7aNwtYimSDQ=
签名:
RL1qIScizRyu79/y+r2TN2FL/bSQDxnDj4JlDwSZM6XZR7CL7u5ZjLNHbsSYpHaCv9qKMS4ump50LyF+go05dsPjWZOvFNkgcm9LepkDP1qm8AzKdTGwlzhdBmy2397Ed8uBrQocFGj/721Y2xM/Db0nt7r54zKZkDXbMMlsd9k=
验签:
true
解密后:
hello
3、AES对称加密
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
public class AESCipher {
public static SecureRandom random = new SecureRandom();
/** * 获取随机16位key,key必须要是10的整数倍,否则会出错 */
public static String getRandom(int length) {
StringBuilder ret = new StringBuilder();
for (int i = 0; i < length; i++) {
// 输出字母还是数字
boolean isChar = (random.nextInt(2) % 2 == 0);
// 字符串
if (isChar) {
// 取得大写字母还是小写字母
int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;
ret.append((char) (choice + random.nextInt(26)));
} else {
// 数字
ret.append(random.nextInt(10));
}
}
return ret.toString();
}
/** * 加密方法,使用key充当向量iv,增加加密算法的强度 * 更加安全 * @param key 密钥 * @param raw 需要加密的内容 * @return */
public static String encrypt(byte[] key, String raw) throws Exception {
// 第一次加密
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] enCodeFormat = secretKey.getEncoded();
// 获取二次加密的key
SecretKeySpec secondSecretKey = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance(Config.AES_ALGORITHM);
// 向量iv,增加加密算法的强度
IvParameterSpec iv = new IvParameterSpec(key);
// 初始化加密器
cipher.init(Cipher.ENCRYPT_MODE, secondSecretKey, iv);
// 加密
byte[] result = cipher.doFinal(raw.getBytes());
// 进行base64编码
return Base64Utils.encodeToString(result);
}
/** * 解密方法,使用key充当向量iv,增加加密算法的强度 * @param key 密钥 * @param enc 待解密内容 * @return */
public static String decrypt(byte[] key, String enc) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] enCodeFormat = secretKey.getEncoded();
// 二次加密
SecretKeySpec secondSecretKey = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance(Config.AES_ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(key);
// 初始化
cipher.init(Cipher.DECRYPT_MODE, secondSecretKey, iv);
// 首先进行base64解码
byte[] bytes = Base64Utils.decodeFromString(enc);
// 解密
byte[] result = cipher.doFinal(bytes);
return new String(result);
}
public static void main(String[] args) throws Exception {
//客户端代码
String text = "hello";
//随机生成16位aes密钥,也可以自己指定16位
byte[] aesKey = getRandom(16).getBytes();
String encryptText = AESCipher.encrypt(aesKey, text);
System.out.println("加密后:\n" + encryptText);
String decryptText = AESCipher.decrypt(aesKey, encryptText);
System.out.println("解密后:\n" + decryptText);
}
}
输出结果
加密后:
hwkYAF9eXj/dytmDBD30xg==
解密后:
hello
三、加解密 starter实战
1、介绍
加密解密本身并不是难事,问题是在何时去处理?定义一个过滤器,将请求和响应分别拦截下来进行处理也是一个办法,这种方式虽然粗暴,但是灵活,因为可以拿到一手的请求参数和响应数据。不过 SpringMVC 中给我们提供了 ResponseBodyAdvice
和 RequestBodyAdvice
,利用这两个工具可以对请求和响应进行预处理,非常方便。
参考: RSA+AES混合加密-JavaWebSpringBoot自定义starter
2、前期准备
2.1 引入依赖
因为我们这个工具是为 Web 项目开发的,以后必然使用在 Web 环境中,所以这里添加依赖时 scope 设置为 provided
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
<version>2.7.0</version>
</dependency>
-
compile:默认值 他表示被依赖项目需要参与当前项目的编译,还有后续的测试,运行周期也参与其中,是一个比较强的依赖。打包的时候通常