• 加密算法 — — 对token进行非对称加密【RSA】


    RSA对token加密

    1 概念

    相关概念:

    • 明文:未被加密过的数据
    • 密文:明文被某种加密算法加密后,就会变成密文
    • 密钥:一种参数,明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为:对称密钥和非对称密钥,分别用在对称加密和非对称加密中。

    1.1 对称加密算法(私钥加密)

    所谓对称就是指加密和解密都是使用同一个钥匙(密钥)
    例如:
    ①小明要传递一个信息A给小红:A + B = C(小明通过对信息A进行加密后,得到信息C传给小红)
    ②小红拿到信息C后,C - B = A(小红拿到数据后,用密钥B进行解密,得到初始信息A)

    特点:
    算法公开、加密和解密速度快,适合对大数据量进行加密;但是与非对称加密相比,安全性降低

    1.2 非对称加密算法

    非对称加密也要公钥加密。与对称加密相比,安全性更好。对称加密的通信双方使用相同的秘钥,如果任意一方的密钥被泄露,整个通信就会被破解。
    非对称加密使用的是一对密钥,即公钥和私钥,且二者成对出现。私钥被自己保存,不能对外泄露。公钥指的是公共的密钥,任何人都可以获得该密钥。用公钥或私钥中的任何一个进行加密,用另一个进行解密。
    过程:
    明文 + 加密算法 + 公钥 = 密文; 密文 + 解密算法 + 私钥 = 明文

    例子:

    1. 小明对小红说,我这里有箱子A(对应钥匙A),我把A箱子给你(箱子A没锁),钥匙不给你,你给我发的信息放在箱子A里,锁起来,然后我自己用钥匙A就能打开来看。
    2. 小红对小明说,我这里有箱子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);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    注意:企业中工具类中一般是没有main方法的,此处只是为了方便测试

    2.2.2.2 JWTAuth0Util工具类编写【包含对称加密与非对称加密】

    一般包含方法:

    • String genTokenRAS(Map payload):基于RSA加密生成token
    • DecodedJWT decodedRSA(String token):基于RSA解密生成DecodedJWT
    • String getToken(Map 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;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    ②非对称加密:根据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;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    ③对称加密:HMAC256,定义盐值
    private static final String SECRET = "H2OJ20FC35APACHE";
    
    • 1
    ④对称加密: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;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    ⑤对称加密: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;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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);
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
  • 相关阅读:
    vueRouter个人笔记
    ISTQB术语表
    PyCharm安装和使用教程
    c++中的类模板
    【斗罗大陆2】动画新增12集备案,冰碧帝皇蝎形象被吐槽遭狂喷!
    VCS+Verdi ubuntu18.04安装教程
    09 -- 回文对
    EasyPoi
    Win 10出现bitlocke恢复,蓝屏错误代码0x1600007e
    什么是ADC测试,能进行自动化测试吗?
  • 原文地址:https://blog.csdn.net/weixin_45565886/article/details/126581097