声明:无恶意引导!
一、对称加密算法
1、AES
优势
劣势
2、DES、3DES
二、非对称加密算法
RSA
RSA用途
优势
劣势
三、线性散列算法
四、(RSA+AES)混合加密
五、Base64加密
实战
1.RSA+AES数据传输【混合加密】
代码
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.Scanner;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import io.jsonwebtoken.Claims;
import org.apache.commons.codec.binary.Base64;
/**
* RSA加密工具类
*/
public class RSAUtil {
private static Map<String, String> keyMap; // 用于封装随机产生的公钥与私钥
static {
// 如果是分布式项目,再加一个redis的分布式锁
synchronized (RSAUtil.class) {
// 1.生成秘钥
long start = System.currentTimeMillis();
keyMap = initRsaKeys();
System.out.println("服务端项目启动,公私秘钥生成...." + (System.currentTimeMillis() - start));// 这个耗时还是很长的,我本地大概是1.8s
System.out.println("写入到数据库或者redis....");
}
}
public static void main(String[] args) throws Exception {
// dataTransportCase();//测试AES的key:ABCDEFGHIJKLMNOP
jwtCase();
}
/**
* jwt登录实战
*/
public static void jwtCase() throws Exception {
// 1.用户登录[可以使用case1做账户密码的接收]
System.out.print("1.界面:用户输入用户名和密码:");
Scanner scanner = new Scanner(System.in);
String account = scanner.nextLine();
String privateKey = keyMap.get("private");
System.out.println("2.后台:判断用户存在,生成token给到前端....");
String token = JwtUtil.generateToken(account, "1000", privateKey);
// 2.token校验
System.out.print("3.前端:新发起一个请求,header中携带了token信息:" + token);
String publicKey = keyMap.get("public");
Claims playLoad = JwtUtil.getPlayLoad(token, publicKey);
System.out.print("n4.后台:解析前端在header中携带的token信息中的playLoad:");
System.out.println(JSON.toJSONString(playLoad));
System.out.println("5.后台:通过用户名去redis拿登录用户的缓存信息...."); }
/**
* RSA+AES加密传输实战
*/
public static void dataTransportCase() throws Exception {
// 1.项目启动
String publicKey = keyMap.get("public");
String privateKey = keyMap.get("private");
// 2.用户输入密码
Scanner scanner = new Scanner(System.in);
System.out.print("1.界面:用户输入用户名和密码:");
String userInfo = scanner.nextLine();
System.out.println("2.全局约定:请设置一个登录相关的AES的加密key(也可以直接通过api从后台拿):");
String aesKey = scanner.next();
// 3.前端将密码进行AES加密
String aesEncryptPwd = AESUtil.encrypt(aesKey, userInfo);
System.out.println("3.前端:对用户输入的密码加密结果传递给后台:" + aesEncryptPwd);
// 4.前端将加密的AES密码,再使用RSA的公钥进行加密
String rsaEncryptData = encrypt(aesEncryptPwd, publicKey);
// 5.后台直接通过项目启动时生成的RSA公私秘钥对,使用私钥进行解密
String rsaDecryptData = decrypt(rsaEncryptData, privateKey);
System.out.println("4.后台:接收到经过RSA加密的AES数据,先对其使用RSA秘钥解密...." + rsaDecryptData);
String aesDecryptPwd = AESUtil.decrypt(aesKey, rsaDecryptData);
System.out.println("5.后台:RSA解密后,再使用AES对密码解密...." + aesDecryptPwd);
System.out.println(aesDecryptPwd);
}
/**
* 模拟项目启动生成密钥对
* - 1.直接本地生成一个,丢到redis或者存储到文件里面,再存入到redis【不然服务挂了公私钥没了】
* - 2.项目启动的时候,直接读redis的数据,如果没有值则直接生成一对公私密钥,用base64进行编码(公司秘钥的获取可以提供一个api接口被前端获取)
* - 3.此处对公私秘钥用了一个base64加解密,其实没什么影响,只是做一下肉眼的保护而已,防止有心人直接拿去乱来
*/
public static Map<String, String> initRsaKeys() {
Map<String, String> result = Maps.newHashMap();
KeyPairGenerator keyPairGen = null;
try {
keyPairGen = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
assert keyPairGen != null;
keyPairGen.initialize(1024, new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
result.put("public", publicKeyString);
result.put("private", privateKeyString);
return result;
}
/**
* RSA公钥加密
*
* @param str 加密字符串
* @param publicKey 公钥
* @return 密文
*/
public static String encrypt(String str, String publicKey) {
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = null;
String outStr = null;
try {
pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
} catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException |
NoSuchPaddingException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
//RSA加密
return outStr;
}
/**
* RSA私钥解密
*
* @param str 加密字符串
* @param privateKey 私钥
* @return 明文
*/
public static String decrypt(String str, String privateKey) {
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey;
//RSA解密
Cipher cipher;
String outStr = null;
try {
priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, priKey);
outStr = new String(cipher.doFinal(inputByte));
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException |
IllegalBlockSizeException | InvalidKeyException e) {
e.printStackTrace();
}
return outStr;
}
/**
* 公钥获取
*/
public static PublicKey getPublicKey(byte[] publicKey) throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
/**
* 私钥获取
*/
public static PrivateKey getPrivateKey(byte[] privateKey) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
}
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AESUtil {
public static String encrypt(String key, String data) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decrypt(String key, String encryptedData) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decryptedBytes);
}
}
运行结果
2.JwtToken登录实战【签名算法】
相关概念
数字签名
数字摘要/指纹
加密算法
@SneakyThrows
private static String generateToken(Map<String, Object> claims) {
return Jwts.builder()
// 数字加密主体
.setClaims(claims)
// 过期时间
.setExpiration(generateExpirationDate())
// 数字签名算法
.signWith(SignatureAlgorithm.RS256, RsaKeyUtil.getPrivateKey(JwtRsaKeyAutoConfig.rsaKeyConfig.getPrivateKey()))
// 根据规则进行加密
.compact();
}
package io.jsonwebtoken;
import io.jsonwebtoken.lang.RuntimeEnvironment;
public enum SignatureAlgorithm {
NONE("none", "No digital signature or MAC performed", "None", (String)null, false),
HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true),
HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true),
HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true),
RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true),
RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true),
RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true),
ES256("ES256", "ECDSA using P-256 and SHA-256", "Elliptic Curve", "SHA256withECDSA", false),
ES384("ES384", "ECDSA using P-384 and SHA-384", "Elliptic Curve", "SHA384withECDSA", false),
ES512("ES512", "ECDSA using P-512 and SHA-512", "Elliptic Curve", "SHA512withECDSA", false),
PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "SHA256withRSAandMGF1", false),
PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "SHA384withRSAandMGF1", false),
PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "SHA512withRSAandMGF1", false);
......
HS256【默认,已过时】
RS256【常用,推荐】
ES256【资料不多】
PS256【资料不多】
数据结构
1.JWT头
{"alg": "HS256","typ": "JWT"}
2.有效载荷playload
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
3.签名
代码
import cn.hutool.core.codec.Base64;
import com.blue.base.common.exception.BaseException;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
/**
* 生成token
*/
public static String generateToken(String account, String userId, String privateKey) throws Exception {
// 1.header部分,用的不多
Map<String, Object> claims = new HashMap<>(10);
// 2.playLoad部分【自带的字段】
claims.put("aud", account);
claims.put("iat", new Date());
// playLoad部分【自定义的字段】
claims.put("userId", userId);
// 3.拿到rsa的私钥(base64做下解密)
PrivateKey rsaPrivateKey = RSAUtil.getPrivateKey(Base64.decode(privateKey));
return Jwts.builder()
// 数字加密主体
.setClaims(claims)
// 过期时间
.setExpiration(new Date(System.currentTimeMillis() + 84000L * 1000))// 1天
// 数字签名算法:rs256,使用私钥加密
.signWith(SignatureAlgorithm.RS256, rsaPrivateKey)
// 根据规则进行加密
.compact();
}
/**
* 获取jwt的claims部分(公钥解密token)
*/
static Claims getPlayLoad(String token, String publicKey) throws Exception {
if (StringUtils.isEmpty(token)) {
throw new IllegalArgumentException("token参数为空!");
}
PublicKey rsaPublicKey = RSAUtil.getPublicKey(Base64.decode(publicKey));
try {
return Jwts.parser()
.setSigningKey(rsaPublicKey)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
throw new BaseException(800, "登录已过期");
} catch (Exception e) {
throw new BaseException(800, "Token解析异常");
}
}
}
运行结果
3.SpringBoot配置文件加密
总结
关注东南网络安全,后期会逐步更新!
原文始发于微信公众号(东南网络安全):加密算法-AES和RSA
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论