RSA对token加密
1 概念
相关概念:
- 明文:未被加密过的数据
- 密文:明文被某种加密算法加密后,就会变成密文
- 密钥:一种参数,明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为:对称密钥和非对称密钥,分别用在对称加密和非对称加密中。
1.1 对称加密算法(私钥加密)
所谓对称就是指加密和解密都是使用同一个钥匙(密钥)
例如:
①小明要传递一个信息A给小红:A + B = C(小明通过对信息A进行加密后,得到信息C传给小红)
②小红拿到信息C后,C - B = A(小红拿到数据后,用密钥B进行解密,得到初始信息A)
特点:
算法公开、加密和解密速度快,适合对大数据量进行加密;但是与非对称加密相比,安全性降低
1.2 非对称加密算法
非对称加密也要公钥加密。与对称加密相比,安全性更好。对称加密的通信双方使用相同的秘钥,如果任意一方的密钥被泄露,整个通信就会被破解。
非对称加密使用的是一对密钥,即公钥和私钥,且二者成对出现。私钥被自己保存,不能对外泄露。公钥指的是公共的密钥,任何人都可以获得该密钥。用公钥或私钥中的任何一个进行加密,用另一个进行解密。
过程:
明文 + 加密算法 + 公钥 = 密文; 密文 + 解密算法 + 私钥 = 明文
例子:
- 小明对小红说,我这里有箱子A(对应钥匙A),我把A箱子给你(箱子A没锁),钥匙不给你,你给我发的信息放在箱子A里,锁起来,然后我自己用钥匙A就能打开来看。
- 小红对小明说,我这里有箱子B(对应钥匙B),我把B箱子给你(箱子B没锁),钥匙不给你,你给我发的消息放在箱子B里,锁起来,然后我自己用钥匙B就能打开来看。
1.3 加密算法比较
加密算法分为:对称加密和非对称加密【此外还有一类不需要密钥的散列算法】
常见的对称加密算法:DES、3DES、AES、HS256
常见的非对称加密算法:RSA、DSA
散列算法:SHA-1、MD5
2 实际应用
2.1 典型对称加密算法(以HS256为例)
见文章:
https://blog.csdn.net/weixin_45565886/article/details/126557256?spm=1001.2014.3001.5501
企业中开发代码:
package com.zi.api.commons.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.*;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.HashMap;
import java.util.Map;
/**
* 生成jwt工具类
*/
public class JJWTRootUtils {
//定义对应的编码算法
static SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
//盐值
static String secretKey = "d8de020f63754a9fa746ea74b831afc3";
//获取key(指定算法和盐值的key对象)
private static Key generateKey(){
//将盐值转成字节
byte[] bytes = DatatypeConverter.parseBase64Binary(secretKey);
//根据算法和盐值生成对应的key值
Key key = new SecretKeySpec(bytes, signatureAlgorithm.getJcaName());
return key;
}
/**
* 将我们的数据使用JWT的方式变成一个token xxx.yyy.zzz
* @param payLoad 负载(数据信息)
* @return
*/
public static String generatorToken(Map<String, String> payLoad){
ObjectMapper objectMapper = new ObjectMapper();
try{
//构建jwt生成器
JwtBuilder builder = Jwts.builder();
//将负载信息设置到jwt生成器中
JwtBuilder jwtBuilder = builder.setPayload(objectMapper.writeValueAsString(payLoad));
//根据签名算法和key值,生成新的jwtBuilder
JwtBuilder jwtBuilder1 = jwtBuilder.signWith(signatureAlgorithm, generateKey());
String token = jwtBuilder1.compact();
return token;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据指定的token, 返回对应的body信息
* @param token
* @return
*/
public static Claims phaseTokenGetBody(String token){
JwtParser jwtParser = Jwts.parser().setSigningKey(generateKey());
Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);
Claims body = claimsJws.getBody();//主要存放的信息
return body;
}
/**
* 根据指定的token获取签名信息
* @param token
* @return
*/
public static String phaseTokenGetSignature(String token){
JwtParser jwtParser = Jwts.parser().setSigningKey(generateKey());
Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);
String signature = claimsJws.getSignature();
return signature;
}
/**
* 根据指定的token获取头信息
* @param token
* @return
*/
public static JwsHeader phaseTokenGetHeader(String token){
//获取解析器
JwtParser parser = Jwts.parser();
//设置签名key(盐值)
parser = parser.setSigningKey(generateKey());
//解析token
Jws<Claims> claimsJws = parser.parseClaimsJws(token);
JwsHeader header = claimsJws.getHeader();
return header;
}
public static void main(String[] args) {
//随机获取盐值
// System.out.println(UUID.randomUUID().toString().replaceAll("-", ""));
Map<String, String> payLoad = new HashMap<>();
payLoad.put("name", "curry");
String s = generatorToken(payLoad);
//eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiY3VycnkifQ.Sf3GiF3p56nLzoAxEHLXcAckPmmPTtecj1_lGT9oV8s
System.out.println(s);
//调用自定义API获取结果
Claims claims = phaseTokenGetBody(s);
//{name=curry}
System.out.println(claims);
}
}
2.2 典型非对称加密算法(以RSA为例)
2.2.1 概念介绍
RSA算法
RSA算法 的保密强度随其密钥的长度增加而增强。但是,密钥越长,其加解密所耗用的时间也越长。
- RSA加密算法是目前最右影响力的公钥加密算法,且被普遍认为是最优秀的公钥方案之一。
- RSA是第一个能同时用于加密和数字签名的算法,它能够抵抗到目前为止已知的所有密码攻击,已经被ISO推荐为公钥数据加密标准。
- RSA核心思想:将两个大素数相乘十分容易,但是要想对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
2.2.2 具体实现(企业中工具类编写格式)
2.2.2.1 RSAUtil编写
首先编写RSAUtil工具类,用于生成RSA的公钥私钥
//RSA是一种非对称加密算法【公钥、私钥】
public class RASUtil {
/**
* 生成一对公私钥
*/
public static void generatorKey() throws NoSuchAlgorithmException {
//构建一个生成公私钥 RAS算法生成的对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
//初始化生成器 keySize:512起
keyPairGenerator.initialize(512, new SecureRandom());
//密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//获取公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//获取私钥字符串
String privateKeyStr = new String(Base64.encodeBase64(privateKey.getEncoded()));
String publicKeyStr = new String(Base64.encodeBase64(publicKey.getEncoded()));
System.out.println("privateKeyStr:" + privateKeyStr);
System.out.println("publicKeyStr:" + publicKeyStr);
//privateKeyStr:MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAhUfkmkFtrOlFiX8FmHiYx9mAPpfTA6oKE6VgFLDNQzYO1YMugA0ACChp6n3hfDWOp3XgWWRVmzU+z30h+01lyQIDAQABAkBtvjvKlDNFnDJou9GUsUBD1qDVaVgT/VAcyyApCUeKnehPLMISa5JBTUpJ03cMPh3hV7q4OJQweM7nuJ+GXcCBAiEAx5xHdUM3XnFZ6aQnAvKzPWqV/eqpDRj9aSFiR/vbJ3kCIQCq7rDN2qs1DUo8XS2WcbBOFLMU+uHDuV8rigFe0yyM0QIgfQ5hCotRDh9P6HwKYONy/kBftlQlE2qboRjkPRsCQ2kCIBUoVlokpux6KKYwImRszhXcGg6Ov0Mqvsz02BaUrP8BAiBrxV/0jPm2pr3GJXW5MHhJ4a2LrkYFfWdHQx+gpSJESg==
//publicKeyStr:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIVH5JpBbazpRYl/BZh4mMfZgD6X0wOqChOlYBSwzUM2DtWDLoANAAgoaep94Xw1jqd14FlkVZs1Ps99IftNZckCAwEAAQ==
}
public static void main(String[] args) throws NoSuchAlgorithmException {
generatorKey();
}
}
注意:企业中工具类中一般是没有main方法的,此处只是为了方便测试
2.2.2.2 JWTAuth0Util工具类编写【包含对称加密与非对称加密】
一般包含方法:
- String genTokenRAS(Map<String, String> payload):基于RSA加密生成token
- DecodedJWT decodedRSA(String token):基于RSA解密生成DecodedJWT
- String getToken(Map<String, String> payload):获取token
- DecodedJWT decodedJWT(String token):解析token
①非对称加密:根据RSA加密生成token
/**
* 根据RSA算法加密生成token
* @param payload
* @return
*/
public static String genTokenRAS(Map<String, String> payload){
//指定过期时间 【3天】
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 3);
//获取一个构建对象【静态内部类】
JWTCreator.Builder builder = JWT.create();
//将信息、数据(body)信息放到需要的claim里面
payload.forEach((k, v) -> builder.withClaim(k, v));
//通过hutool工具类来创建RSA对象
RSA rsa = new RSA(PRIVATE_KEY_STR, null);
//获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.RSA256(null, privateKey));
return token;
}
②非对称加密:根据RSA解密token
/**
* 使用RSA的方式来解密
* @param token
* @return
* 根据不同的异常来判断当前token到底是什么情况
*/
public static DecodedJWT decodedRSA(String token){
//通过公钥获取我们的RSA的公钥对象
RSA rsa = new RSA(null, PUBLIC_KEY_STR);
RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
//jwt的验证对象
JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA256(publicKey, null)).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
return decodedJWT;
}
③对称加密:HMAC256,定义盐值
private static final String SECRET = "H2OJ20FC35APACHE";
④对称加密:HMAC256,加密获取token
/**
* 获取token
* @param payload
* @return
*/
public static String getToken(Map<String, String> payload){
//指定过期时间【3天】
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 3);
//获取一个构建对象【静态内部类】
JWTCreator.Builder builder = JWT.create();
//将body信息放到我们需要生成的claim里面
payload.forEach((k, v) -> builder.withClaim(k, v));
//通过指定签名算法和过期时间生成一个token
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SECRET));
return token;
}
⑤对称加密:HMAC256,解密获取数据
/**
* 解析token
* @param token
* @return
*/
public static DecodedJWT decodedJWT(String token){
//构建一个验证jwt token的对象
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
//验证以后获取信息的对象
DecodedJWT decodedJWT = jwtVerifier.verify(token);
//返回信息对象
return decodedJWT;
}
2.2.2.3 全部代码
package com.zi.api.commons.util;
import cn.hutool.crypto.asymmetric.RSA;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
/**
* 加密解密token
*/
public class JWTAuth0Util {
private static final String SECRET = "H2OJ20FC35APACHE";
//私钥和公钥
private static final String PRIVATE_KEY_STR = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAjX+A8rA2a8tJNQ2DK5pnfaXqHyv5iSG2KdWJk3GMkfN0VuEO/JvbM0ljQgi6/qPmgUIFgL65meavzJXDMr8IZQIDAQABAkEAh7JZdWRsLGAd6tT0kGJZEXSF3DMN8eb0jZYNg+sHRgdovJtlHZBTiMbSoQphvsYe8dBHplOjktzTipTe0y/rmQIhAMP13C1UFEPdFv9D6mFg+hehr0Jk+vcxOoGFA1o0vOz/AiEAuNnk/2etuErfNdgN9ujc88TAG2mpzBvyxpIHNQridpsCIC44dYBzjnwbT+tRt5zUZOjiCBae/tsDT4txNkM2oUE9AiBoN4jxKi36KlRAEiiFXXI9CV9Z1S/DALrWkzv2/sUBIwIgXgMe3SW4vML/ieQHIEmk+GhDD/NJad9Hs4HOBkNcA1I=";
private static final String PUBLIC_KEY_STR = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI1/gPKwNmvLSTUNgyuaZ32l6h8r+YkhtinViZNxjJHzdFbhDvyb2zNJY0IIuv6j5oFCBYC+uZnmr8yVwzK/CGUCAwEAAQ==";
/**
* 根据RSA算法加密生成token【非对称】
* @param payload
* @return
*/
public static String genTokenRAS(Map<String, String> payload){
//指定过期时间 【3天】
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 3);
//获取一个构建对象【静态内部类】
JWTCreator.Builder builder = JWT.create();
//将信息、数据(body)信息放到需要的claim里面
payload.forEach((k, v) -> builder.withClaim(k, v));
//通过hutool工具类来创建RSA对象
RSA rsa = new RSA(PRIVATE_KEY_STR, null);
//获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.RSA256(null, privateKey));
return token;
}
/**
* 使用RSA的方式来解密【非对称】
* @param token
* @return
* 根据不同的异常来判断当前token到底是什么情况【比如被伪造...】
*/
public static DecodedJWT decodedRSA(String token){
//通过公钥获取我们的RSA的公钥对象
RSA rsa = new RSA(null, PUBLIC_KEY_STR);
RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
//jwt的验证对象
JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA256(publicKey, null)).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
return decodedJWT;
}
/**
* 获取token【对称加密】
* @param payload
* @return
*/
public static String getToken(Map<String, String> payload){
//指定过期时间【3天】
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 3);
//获取一个构建对象【静态内部类】
JWTCreator.Builder builder = JWT.create();
//将body信息放到我们需要生成的claim里面
payload.forEach((k, v) -> builder.withClaim(k, v));
//通过指定签名算法和过期时间生成一个token
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SECRET));
return token;
}
/**
* 解析token【对称加密】
* @param token
* @return
*/
public static DecodedJWT decodedJWT(String token){
//构建一个验证jwt token的对象
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
//验证以后获取信息的对象
DecodedJWT decodedJWT = jwtVerifier.verify(token);
//返回信息对象
return decodedJWT;
}
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("helicopter", "apache");
String rsaCodeStr = genTokenRAS(map);
System.out.println("非对称加密后的token:" + rsaCodeStr);
DecodedJWT decodedJWT = decodedRSA(rsaCodeStr);
String rsaDecodeStr = decodedJWT.getToken();
System.out.println("非对称解密后的token:" + rsaDecodeStr);
String hmacCodeStr = getToken(map);
System.out.println("对称加密后的token:" + hmacCodeStr);
DecodedJWT decodedJWT1 = decodedJWT(hmacCodeStr);
String hmacDeCodeStr = decodedJWT1.getToken();
System.out.println("对称解密后的token:" + hmacDeCodeStr);
}
}