ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

JWT(3)JWT的签名算法

JWT(3)JWT的签名算法

在JWT(JSON Web Token)中,涉及到两种主要的算法类型:加密算法和签名算法。

签名算法用于确保 JWT 数据的完整性和真实性,必须使用;而加密算法用于保护 JWT 内容的机密性,选择性使用。这里看下签名算法。

介绍

1、简介

签名算法的主要目的是确保 JWT 的内容未被篡改,并验证其来源。签名算法用于生成一个数字签名,接收方使用相同的算法来验证签名,从而确认数据的完整性。

2、jwt常见的签名算法

不同的jwt插件有不同的加密算法名称,原理都一样

(1)auth0插件
// // 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)jjwt插件

io.jsonwebtoken.SignatureAlgorithm

NONE("none", "No digital signature or MAC performed", "None", (String)null, false, 0, 0), HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true, 256, 256, "1.2.840.113549.2.9"), HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true, 384, 384, "1.2.840.113549.2.10"), HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true, 512, 512, "1.2.840.113549.2.11"), RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true, 256, 2048), RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true, 384, 2048), RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true, 512, 2048), ES256("ES256", "ECDSA using P-256 and SHA-256", "ECDSA", "SHA256withECDSA", true, 256, 256), ES384("ES384", "ECDSA using P-384 and SHA-384", "ECDSA", "SHA384withECDSA", true, 384, 384), ES512("ES512", "ECDSA using P-521 and SHA-512", "ECDSA", "SHA512withECDSA", true, 512, 521), PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "RSASSA-PSS", false, 256, 2048), PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "RSASSA-PSS", false, 384, 2048), PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "RSASSA-PSS", false, 512, 2048);
3、签名算法分类
(1)对称加密

HMAC类属于对称加密;

这些算法使用同一个密钥(secret_key)进行签名和验证,属于对称加密。一旦密钥泄露,安全性就会受到威胁。适用于信任的系统内部,例如单体应用或集中式认证服务。

(2)非对称加密

RSA类和ECDSA类算法属于非对称加密;

使用一对密钥,公钥用于验证,私钥用于签名,提供了更好的密钥管理和分发机制,即使公钥被泄露,只要私钥保持安全,Token依然是安全的。适用于分布式系统和多服务环境。

如我们项目曾经将HS256升级为ES256。

比较:

1)性能:

对称加密算法通常比非对称加密算法更快,但在分布式系统中,非对称加密算法(尤其是ECDSA)更受欢迎,因为它们提供了更好的安全性和灵活性。如我们项目曾将jwt由HS256(HMAC + SHA-256)升级为ES256(ECDSA + SHA-256)。

2)Token大小:

非对称加密算法生成的签名通常比对称加密算法的签名要大,但ECDSA算法由于其较短的签名长度,可以在保持安全性的同时减少Token的大小。在选择JWT的加密算法时,需要根据系统的具体需求和安全要求来决定使用哪种算法。对于需要跨多个服务或不信任环境传输的Token,推荐使用非对称加密算法,如RS256或ES256。

4、工作原理
(1)签名生成
  • JWT 的Header和Payload被编码为 Base64Url 格式。
  • 然后,使用指定的签名算法(例如HS256、RS256等)生成签名。
  • 生成的签名会附加到 JWT 的最后,形成 JWT 令牌。
(2)签名验证
  • 接收方使用相应的签名算法和密钥(对称密钥或公钥)来验证 JWT 是否未被篡改。
  • 如果签名匹配,则证明数据完整且来自授权方;否则,数据可能已经被篡改或伪造。

一、对称加密--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进行签名。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进