前言
1、jwt加密算法
JWT(JSON Web Token)支持多种加密和签名算法,用于确保JWT在传输过程中不被篡改,这些算法用于确保Token的安全性和数据的完整性。见Algorithm :
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.auth0.jwt.algorithms;
import com.auth0.jwt.exceptions.SignatureGenerationException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.ECDSAKeyProvider;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import java.security.interfaces.ECKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
public abstract class Algorithm {
private final String name;
private final String description;
public static Algorithm RSA256(RSAKeyProvider keyProvider) throws IllegalArgumentException {
return new RSAAlgorithm("RS256", "SHA256withRSA", keyProvider);
}
public static Algorithm RSA256(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException {
return RSA256(RSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm RSA256(RSAKey key) throws IllegalArgumentException {
RSAPublicKey publicKey = key instanceof RSAPublicKey ? (RSAPublicKey)key : null;
RSAPrivateKey privateKey = key instanceof RSAPrivateKey ? (RSAPrivateKey)key : null;
return RSA256(publicKey, privateKey);
}
public static Algorithm RSA384(RSAKeyProvider keyProvider) throws IllegalArgumentException {
return new RSAAlgorithm("RS384", "SHA384withRSA", keyProvider);
}
public static Algorithm RSA384(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException {
return RSA384(RSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm RSA384(RSAKey key) throws IllegalArgumentException {
RSAPublicKey publicKey = key instanceof RSAPublicKey ? (RSAPublicKey)key : null;
RSAPrivateKey privateKey = key instanceof RSAPrivateKey ? (RSAPrivateKey)key : null;
return RSA384(publicKey, privateKey);
}
public static Algorithm RSA512(RSAKeyProvider keyProvider) throws IllegalArgumentException {
return new RSAAlgorithm("RS512", "SHA512withRSA", keyProvider);
}
public static Algorithm RSA512(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException {
return RSA512(RSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm RSA512(RSAKey key) throws IllegalArgumentException {
RSAPublicKey publicKey = key instanceof RSAPublicKey ? (RSAPublicKey)key : null;
RSAPrivateKey privateKey = key instanceof RSAPrivateKey ? (RSAPrivateKey)key : null;
return RSA512(publicKey, privateKey);
}
public static Algorithm HMAC256(String secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
}
public static Algorithm HMAC384(String secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS384", "HmacSHA384", secret);
}
public static Algorithm HMAC512(String secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS512", "HmacSHA512", secret);
}
public static Algorithm HMAC256(byte[] secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
}
public static Algorithm HMAC384(byte[] secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS384", "HmacSHA384", secret);
}
public static Algorithm HMAC512(byte[] secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS512", "HmacSHA512", secret);
}
public static Algorithm ECDSA256(ECDSAKeyProvider keyProvider) throws IllegalArgumentException {
return new ECDSAAlgorithm("ES256", "SHA256withECDSA", 32, keyProvider);
}
public static Algorithm ECDSA256(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException {
return ECDSA256(ECDSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm ECDSA256(ECKey key) throws IllegalArgumentException {
ECPublicKey publicKey = key instanceof ECPublicKey ? (ECPublicKey)key : null;
ECPrivateKey privateKey = key instanceof ECPrivateKey ? (ECPrivateKey)key : null;
return ECDSA256(publicKey, privateKey);
}
public static Algorithm ECDSA384(ECDSAKeyProvider keyProvider) throws IllegalArgumentException {
return new ECDSAAlgorithm("ES384", "SHA384withECDSA", 48, keyProvider);
}
public static Algorithm ECDSA384(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException {
return ECDSA384(ECDSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm ECDSA384(ECKey key) throws IllegalArgumentException {
ECPublicKey publicKey = key instanceof ECPublicKey ? (ECPublicKey)key : null;
ECPrivateKey privateKey = key instanceof ECPrivateKey ? (ECPrivateKey)key : null;
return ECDSA384(publicKey, privateKey);
}
public static Algorithm ECDSA512(ECDSAKeyProvider keyProvider) throws IllegalArgumentException {
return new ECDSAAlgorithm("ES512", "SHA512withECDSA", 66, keyProvider);
}
public static Algorithm ECDSA512(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException {
return ECDSA512(ECDSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm ECDSA512(ECKey key) throws IllegalArgumentException {
ECPublicKey publicKey = key instanceof ECPublicKey ? (ECPublicKey)key : null;
ECPrivateKey privateKey = key instanceof ECPrivateKey ? (ECPrivateKey)key : null;
return ECDSA512(publicKey, privateKey);
}
public static Algorithm none() {
return new NoneAlgorithm();
}
protected Algorithm(String name, String description) {
this.name = name;
this.description = description;
}
public String getSigningKeyId() {
return null;
}
public String getName() {
return this.name;
}
String getDescription() {
return this.description;
}
public String toString() {
return this.description;
}
public abstract void verify(DecodedJWT var1) throws SignatureVerificationException;
public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException {
byte[] contentBytes = new byte[headerBytes.length + 1 + payloadBytes.length];
System.arraycopy(headerBytes, 0, contentBytes, 0, headerBytes.length);
contentBytes[headerBytes.length] = 46;
System.arraycopy(payloadBytes, 0, contentBytes, headerBytes.length + 1, payloadBytes.length);
return this.sign(contentBytes);
}
/** @deprecated */
@Deprecated
public abstract byte[] sign(byte[] var1) throws SignatureGenerationException;
}
2、jwt算法分类
1)HMAC属于对称加密:
这些算法使用同一个密钥(secret_key)进行签名和验证,属于对称加密。一旦密钥泄露,安全性就会受到威胁。适用于信任的系统内部,例如单体应用或集中式认证服务。
2)RSA和ECDSA算法属于非对称加密:
使用一对密钥,公钥用于验证,私钥用于签名,提供了更好的密钥管理和分发机制,即使公钥被泄露,只要私钥保持安全,Token依然是安全的。适用于分布式系统和多服务环境。
如我们项目曾经将HS256升级为ES256。
3、jwt算法比较
1)性能:
对称加密算法通常比非对称加密算法更快,但在分布式系统中,非对称加密算法(尤其是ECDSA)更受欢迎,因为它们提供了更好的安全性和灵活性。如我们项目曾将jwt由HS256升级为ES256。
2)Token大小:
非对称加密算法生成的签名通常比对称加密算法的签名要大,但ECDSA算法由于其较短的签名长度,可以在保持安全性的同时减少Token的大小。在选择JWT的加密算法时,需要根据系统的具体需求和安全要求来决定使用哪种算法。对于需要跨多个服务或不信任环境传输的Token,推荐使用非对称加密算法,如RS256或ES256。
一、HMAC算法(Hash-based Message Authentication Code):
1、HS256
1.1、介绍
HS256算法使用了HMAC-SHA256算法,其中HMAC代表散列消息认证码,SHA256是一种散列函数,能够为任意长度的数据生成一个固定长度的摘要。
HS256的工作原理是:
使用一个密钥和一个消息作为输入;使用SHA-256散列函数来计算消息的摘要;使用HMAC(散列消息认证码)函数,结合散列函数和密钥,来生成一个密钥相关的摘要。
1.2、demo
package com.demo.security.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.demo.security.constant.UserConstants;
import com.demo.security.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.Map;
@Slf4j
public class JwtUtilTest {
public static void main(String[] args) {
//1、生成token
String username = "zs";
Integer age = 18;
String key = "123456";
Algorithm algorithm = Algorithm.HMAC256(key);
UserEntity userEntity = UserEntity.builder()
.username(username)
.age(age)
.build();
String token = getToken(userEntity,algorithm);
log.info("token获取成功:{}",token);
log.info("token长度:{}",token.length());
//2、验证token,因为只要token是jwt,第三步一定会成功,这里是为了验证是不是我生成的,防止攻击
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
//3、解析token
UserEntity tokenUser = getUser(token);
if(username.equals(tokenUser.getUsername())){
log.info("解析成功");
}
}
public static UserEntity getUser( String token) {
try {
DecodedJWT jwt = JWT.decode(token);
Map<String, Claim> claims = jwt.getClaims();
String username = claims.get("username").asString();
int age = claims.get("age").asInt();
return UserEntity.builder()
.username(username)
.age(age)
.build();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min(分钟)后过期
* @return 加密的token
*/
public static String getToken(UserEntity userEntity, Algorithm algorithm) {
Date date = new Date(System.currentTimeMillis() + UserConstants.EXPIRE_TIME);
// 附带username信息
return JWT.create()
//iss:签发方
.withIssuer("wtyy")
//aud:接收jwt的一方
.withAudience("web-demo")
//exp:jwt的过期时间,这个过期时间必须要大于签发时间
//.withExpiresAt()
//其他自定义通信信息
.withClaim("username", userEntity.getUsername())
.withClaim("age",userEntity.getAge())
.withExpiresAt(date)
.sign(algorithm);
}
}
执行打印
14:50:25.610 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzM2MjUsImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.9dEWE3Q6zBa6szo1QVv5UaGfeXMsWyCJ64RSnWFx1Ns
14:50:25.624 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:179
14:50:25.645 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功
通过在线解析
这里为了证明第二步verify的重要性,改下代码:
public static void main(String[] args) {
//1、生成token
String username = "zs";
Integer age = 18;
String key = "123456";
Algorithm algorithm = Algorithm.HMAC256(key);
UserEntity userEntity = UserEntity.builder()
.username(username)
.age(age)
.build();
String token = getToken(userEntity,algorithm);
log.info("token获取成功:{}",token);
log.info("token长度:{}",token.length());
//2、验证token,因为只要token是jwt,第三步一定会成功,这里是为了验证是不是我生成的,防止攻击
try{
Algorithm errorAlgorithm = Algorithm.HMAC256(key+"error");
JWTVerifier verifier = JWT.require(errorAlgorithm).build();
verifier.verify(token);
}catch (Exception e){
//throw异常
log.error("验证失败");
}
//3、解析token
UserEntity tokenUser = getUser(token);
if(username.equals(tokenUser.getUsername())){
log.info("解析成功,name={}",tokenUser.getUsername());
}
}
打印:
15:48:42.922 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzcxMjIsImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.mVQ5cO-nPo1SSw0qhXFdDbjQK4WdKSLmLjFWSkt8fQQ
15:48:42.924 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:179
15:48:42.943 [main] ERROR com.demo.security.util.JwtUtilTest -- 验证失败
15:48:42.944 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功,name=zs
2、HS384
2.1、介绍
使用SHA-384算法进行HMAC签名。
2.2、demo
上面的代码修改为
Algorithm algorithm = Algorithm.HMAC384(secret);
执行打印
14:55:46.167 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzM5NDYsImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.wB0LaglQH_IcFKHa7oLm5r2Gof2DzCS0XmhvYC0m9q5nk4T4-CCTCpGllyc3waPR
14:55:46.167 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:200
14:55:46.183 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功
3、HS512
3.1、介绍
使用SHA-512算法进行HMAC签名。
3.2、demo
上面的代码修改为
Algorithm algorithm = Algorithm.HMAC512(secret);
执行输出:
14:57:29.979 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzQwNDksImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.J7-tJFmjP6Pnh9djMI6MnAEIFKK0Vvzn-ASVFRTWUf3kHa-NDOmYydP4jRDitui2EJNfxLu4taOo5YWZXYJoSQ
14:57:29.979 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:222
14:57:29.994 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功
二、RSA算法(Rivest–Shamir–Adleman)
1、RS256
1.1、介绍
使用RSA和SHA-256算法进行签名。
1.2、demo
上面的代码修改为:
package com.demo.security.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.demo.security.constant.UserConstants;
import com.demo.security.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.Map;
@Slf4j
public class JwtUtilTest {
public static void main(String[] args) throws Exception {
//1、先生成公钥私钥
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) pair.getPrivate();
RSAPublicKey rsaPublicKey = (RSAPublicKey) pair.getPublic();
//2、根据私钥生成jwt
String username = "zs";
Integer age = 18;
UserEntity userEntity = UserEntity.builder()
.username(username)
.age(age)
.build();
String token = getToken(userEntity,rsaPrivateKey,rsaPublicKey);
log.info("token获取成功:{}",token);
log.info("token长度:{}",token.length());
//3、先验证是否合法,注意第四步是一定可以成功的,这里是为了安全验证,校验token是不是我生成的,防止攻击
Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, null);
JWTVerifier verifier = JWT.require(algorithm).build();
//公钥验证不通过会抛异常
verifier.verify(token);
//log.info("验证成功");
//4、根据公钥解析jwt
UserEntity tokenUser = getUser(token);
if(username.equals(tokenUser.getUsername())){
log.info("解析成功");
}
}
public static UserEntity getUser( String token) {
try {
DecodedJWT jwt = JWT.decode(token);
Map<String, Claim> claims = jwt.getClaims();
String username = claims.get("username").asString();
int age = claims.get("age").asInt();
return UserEntity.builder()
.username(username)
.age(age)
.build();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min(分钟)后过期
* @param rsaPrivateKey
* @param rsaPublicKey
* @return 加密的token
*/
public static String getToken(UserEntity userEntity, RSAPrivateKey rsaPrivateKey, RSAPublicKey rsaPublicKey) throws Exception {
Date date = new Date(System.currentTimeMillis() + UserConstants.EXPIRE_TIME);
Algorithm algorithm = Algorithm.RSA256(rsaPublicKey,rsaPrivateKey);
// 附带username信息
return JWT.create()
//iss:签发方
.withIssuer("wtyy")
//aud:接收jwt的一方
.withAudience("web-demo")
//exp:jwt的过期时间,这个过期时间必须要大于签发时间
//.withExpiresAt()
//其他自定义通信信息
.withClaim("username", userEntity.getUsername())
.withClaim("age",userEntity.getAge())
.withExpiresAt(date)
.sign(algorithm);
}
private static RSAPublicKey convertStringToRSAPublicKey(String publicKeyString) throws Exception {
publicKeyString = publicKeyString.replaceAll("\\n", "")
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "");
// base64 decode
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
// X509EncodedKeySpec change to PublicKey
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
// change PublicKey to RSAPublicKey
return (RSAPublicKey) publicKey;
}
}
执行打印
15:39:47.174 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzY1ODcsImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.ERWm_kHMzFyEnrgaRC1TX19tmdalidKZ3h-SSrRkMuMmvYghFSsKWkIpvrhcXN-vtigweUThjg7IbHvS8unWV11Z6eb8vHldlq6kXnrTl802oEQqr6ORRnMafmxqEX7KqQsv7vv0JDwLbf8xEM4gebJqU_fKBOtw5mKivKt2fmZXs4vrBFHU_OXex4cUZp1TdMEAhJeysRhFGcLHqctgTkqx7NIUj80cODTMPfv2HNUJu-nZ5vn5oCmOkhD9yB5A3lpc7DM_qDL57jRQazoQMyTg97P9p_5x5qzI4mQNEqlnJyhJ9U-sqVsyFka_JiehyHJn_CEtpR031MFw0mUd-g
15:39:47.176 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:478
15:39:47.199 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功
为了验证第三步的重要性,改下代码:
//3、先验证是否合法,注意第四步是一定可以成功的,这里是为了安全验证,校验token是不是我生成的,防止攻击
try{
Algorithm algorithm = Algorithm.RSA256(null, null);
JWTVerifier verifier = JWT.require(algorithm).build();
//公钥验证不通过会抛异常
verifier.verify(token);
}catch (Exception e){
log.error("验证失败");
}
//4、根据公钥解析jwt
UserEntity tokenUser = getUser(token);
if(username.equals(tokenUser.getUsername())){
log.info("解析成功,name={}",tokenUser.getUsername());
}
这两步打印:
15:42:19.882 [main] ERROR com.demo.security.util.JwtUtilTest -- 验证失败
15:42:19.905 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功,name=zs
2、RS384
使用RSA和SHA-384算法进行签名。
上面的代码修改下,256改成384,执行下输出
15:51:33.549 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzM4NCJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzcyOTMsImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.lWIWB72Xnm3Gxdoa6TTM2g7SrgDY3m3sn1TShQPJrpyXm_Z9pnv9qup3yAvUmGyR2HbsqsjX9mG6kMz5A3Rxuvl2icIA0TzSq3SD4X0CWg6yOa1CO76W9yEHvFewPkYNfDNx1gHwcmyLW4PavcZaOnpMrOfILaVoqUeoaJ9d5fye39ASLbsgj76JL1KdX7EaM3eb_x3DPp7zt46rsV76z5IGlXnxAch4va5zF3h73Pj6HmbQ6g9FDCPa9Kz3Yd9PlxKm9lWTDMTNrEZ0Dphgi7mxs2XLJoYIfJEY9_jqLeFkl2otFLTAURoS_sNrOvQMtXRkDPoraIi5gfM-IyUnJA
15:51:33.551 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:478
15:51:33.573 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功
3、RS512
使用RSA和SHA-512算法进行签名。
上面的代码修改下,384改成512,执行下输出:
15:52:24.816 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzczNDQsImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.sC5D9qDnJg4fvtg3M9MSlJqZ2K7t88yFaP4-KWLyd5Y5Tm-PlwJImLtT3g2NwW-NGnmUpVZ0nYY47io49FN7IYKRv7NxpniAwWOc-JBJM1o2SeGAsG_dPBfdOxB4_6f1V5gMiDuqHIdyjW7DPl3w72j2-etjXg4xBG0ed-LihzZ7MWhWreEoPoBSJja8CDalE0bXAAHQn3EKvlbAlTO7QLdOpJvM1cQJlBMTzaes8sXbc-yqY-lQ7LOtKbqdd90d_muBUq5Qn4bjO5GWcq-QGTjzSXvQH-ttKJliVblvJYpCMs0mAFIniL41lKTNNVg3G-zRcFJtwtdOUSYMEadEIA
15:52:24.818 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:478
15:52:24.839 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功
三、ECDSA算法(Elliptic Curve Digital Signature Algorithm):
特点:与RSA算法类似,ECDSA也是非对称加密算法,但使用的是椭圆曲线加密,通常比RSA算法更快,签名和验证过程更高效。同时,ECDSA的密钥长度较短,有助于减少Token的大小。
1、ES256
使用椭圆曲线加密算法ECDSA和SHA-256进行签名。
demo:
package com.demo.security.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.demo.security.constant.UserConstants;
import com.demo.security.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.Map;
@Slf4j
public class JwtUtilTest {
public static void main(String[] args) throws Exception {
//1、先生成公钥私钥
// 初始化KeyPairGenerator
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
// 设置ECDSA使用的椭圆曲线为secp256r1
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
keyPairGenerator.initialize(ecSpec);
KeyPair pair = keyPairGenerator.generateKeyPair();
ECPrivateKey ecPrivateKey = (ECPrivateKey) pair.getPrivate();
ECPublicKey ecPublicKey = (ECPublicKey) pair.getPublic();
//2、根据私钥生成jwt
String username = "zs";
Integer age = 18;
UserEntity userEntity = UserEntity.builder()
.username(username)
.age(age)
.build();
String token = getToken(userEntity,ecPrivateKey,ecPublicKey);
log.info("token获取成功:{}",token);
log.info("token长度:{}",token.length());
//3、先验证是否合法,注意第四步是一定可以成功的,这里是为了安全验证,校验token是不是我生成的,防止攻击
Algorithm algorithm = Algorithm.ECDSA256(ecPublicKey, null);
JWTVerifier verifier = JWT.require(algorithm).build();
//公钥验证不通过会抛异常
verifier.verify(token);
log.info("验证成功");
//4、根据公钥解析jwt
UserEntity tokenUser = getUser(token);
if(username.equals(tokenUser.getUsername())){
log.info("解析成功");
}
}
public static UserEntity getUser( String token) {
try {
DecodedJWT jwt = JWT.decode(token);
Map<String, Claim> claims = jwt.getClaims();
String username = claims.get("username").asString();
int age = claims.get("age").asInt();
return UserEntity.builder()
.username(username)
.age(age)
.build();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min(分钟)后过期
* @param rsaPrivateKey
* @param rsaPublicKey
* @return 加密的token
*/
public static String getToken(UserEntity userEntity, ECPrivateKey rsaPrivateKey, ECPublicKey rsaPublicKey) throws Exception {
Date date = new Date(System.currentTimeMillis() + UserConstants.EXPIRE_TIME);
Algorithm algorithm = Algorithm.ECDSA256(rsaPublicKey,rsaPrivateKey);
// 附带username信息
return JWT.create()
//iss:签发方
.withIssuer("wtyy")
//aud:接收jwt的一方
.withAudience("web-demo")
//exp:jwt的过期时间,这个过期时间必须要大于签发时间
//.withExpiresAt()
//其他自定义通信信息
.withClaim("username", userEntity.getUsername())
.withClaim("age",userEntity.getAge())
.withExpiresAt(date)
.sign(algorithm);
}
}
执行输出:
16:00:37.510 [main] INFO com.demo.security.util.JwtUtilTest -- token获取成功:eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJ3ZWItZGVtbyIsImlzcyI6Ind0eXkiLCJleHAiOjE3MjEzNzc4MzcsImFnZSI6MTgsInVzZXJuYW1lIjoienMifQ.niLzXGc3kuiYm75UrbCtK9qUTdDIUyo89nLkb1tOyH1dE--G_IILabMFsOlYHgTdx-z_zoJFjHqtlaIYXXdD8w
16:00:37.512 [main] INFO com.demo.security.util.JwtUtilTest -- token长度:222
16:00:37.538 [main] INFO com.demo.security.util.JwtUtilTest -- 验证成功
16:00:37.540 [main] INFO com.demo.security.util.JwtUtilTest -- 解析成功
通过在线解析:
2、ES384
使用ECDSA和SHA-384进行签名。
3、ES512
使用ECDSA和SHA-512进行签名。