JWT(2)JWT的加密算法

前言

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进行签名。

实现JWT的RS256加密算法需要使用PHP的openssl扩展,以下是一个简单的示例代码: ```php <?php // 生成JWT Token function generateToken($payload, $privateKey) { // JWT 头部 $header = array( "alg" => "RS256", "typ" => "JWT" ); // JWT 载荷 $payload = json_encode($payload); // JWT 签名 $headerEncoded = base64UrlEncode(json_encode($header)); $payloadEncoded = base64UrlEncode($payload); $data = $headerEncoded . '.' . $payloadEncoded; $signature = ''; openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256); $signatureEncoded = base64UrlEncode($signature); // JWT Token $jwtToken = $headerEncoded . '.' . $payloadEncoded . '.' . $signatureEncoded; return $jwtToken; } // 验证JWT Token function verifyToken($jwtToken, $publicKey) { $jwtParts = explode('.', $jwtToken); if (count($jwtParts) != 3) { return false; } $headerEncoded = $jwtParts[0]; $payloadEncoded = $jwtParts[1]; $signatureEncoded = $jwtParts[2]; $header = json_decode(base64UrlDecode($headerEncoded), true); if (!isset($header['alg']) || $header['alg'] !== 'RS256') { return false; } $payload = json_decode(base64UrlDecode($payloadEncoded), true); if (!$payload) { return false; } $data = $headerEncoded . '.' . $payloadEncoded; $signature = base64UrlDecode($signatureEncoded); // 验证签名 $verifyResult = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256); if ($verifyResult === 1) { return true; } else { return false; } } // Base64 URL 编码 function base64UrlEncode($data) { $urlsafe = strtr(base64_encode($data), '+/', '-_'); return rtrim($urlsafe, '='); } // Base64 URL 解码 function base64UrlDecode($data) { $data = strtr($data, '-_', '+/'); $dataLen = strlen($data) % 4; if ($dataLen) { $data .= str_repeat('=', 4 - $dataLen); } return base64_decode($data); } // 读取私钥 $privateKey = openssl_pkey_get_private("file://path/to/private_key.pem"); // 读取公钥 $publicKey = openssl_pkey_get_public("file://path/to/public_key.pem"); // 生成Token $payload = array( "sub" => "1234567890", "name" => "John Doe", "iat" => 1516239022 ); $jwtToken = generateToken($payload, $privateKey); echo "JWT Token: " . $jwtToken . "\n"; // 验证Token $verifyResult = verifyToken($jwtToken, $publicKey); if ($verifyResult) { echo "Token 验证成功\n"; } else { echo "Token 验证失败\n"; } ``` 在以上示例代码中,`generateToken` 函数用于生成JWT Token,`verifyToken` 函数用于验证JWT Token。`base64UrlEncode` 和 `base64UrlDecode` 分别用于进行Base64 URL编码和解码。请注意,需要将私钥和公钥保存在文件中,并且需要指定正确的文件路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值