• 基于hutool实现非对称加密(RSA和国密SM2)的加解密和加签验签


    背景

    对外服务的接口为了安全起见,往往需要进行相应的安全处理:数据加密传输和身份认证。数据加密传输有对称加密和非对称加密两种,为了更加安全起见采用非对称加密比较好些,身份认证则采用数字签名可以实现。

    非对称加密缺点:加解密速度慢、RSA有最大长度要求。

    方案一

    仅采用非对称加密

    RSA对内容长度的要求可以通过分组加解密解决

    参考:https://blog.csdn.net/draven1122/article/details/55212252

     

    方案二

    非对称加密+对称加密

    内容采用对称加密(AES)加密,非对称加密仅加密AES密钥

    加密工具类可以使用:https://hutool.cn/docs/#/crypto/%E6%A6%82%E8%BF%B0 

    基于hutool中密码工具类实现的rsa和国密的非对称加解密算法和加签验签算法代码如下

    maven依赖

     <!-- huTool工具箱 -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.7.22</version>
            </dependency>
    
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk15to18</artifactId>
                <version>1.69</version>
            </dependency>

    rsa非对称加密

    package com.hdwang.test.hutool;
    
    import cn.hutool.core.util.CharsetUtil;
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.crypto.SecureUtil;
    import cn.hutool.crypto.asymmetric.KeyType;
    import cn.hutool.crypto.asymmetric.RSA;
    import cn.hutool.crypto.asymmetric.Sign;
    import cn.hutool.crypto.asymmetric.SignAlgorithm;
    
    import java.nio.charset.StandardCharsets;
    import java.security.KeyPair;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.util.Base64;
    
    /**
     * @author wanghuidong
     * @date 2022/5/25 21:00
     */
    public class RsaTest {
    
        public static void main(String[] args) {
            String text = "人最宝贵的是生命.生命对每个人只有一次.人的一生应当这样度过:当他回首往事的时候,不会因为虚度年华而悔恨,也不会因为碌碌无为而羞耻.这样,在临死的时候,他能够说:“我已把自己的整个的生命和全部的精力献给了世界上最壮丽的事业---------为人类的解放而斗争.”";
            System.out.println("原文:" + text);
    
            //生成公私钥对
            KeyPair pair = SecureUtil.generateKeyPair("RSA");
            PrivateKey privateKey = pair.getPrivate();
            PublicKey publicKey = pair.getPublic();
            //获得私钥
            String privateKeyStr = bytesToBase64(privateKey.getEncoded());
            System.out.println("私钥:" + privateKeyStr);
            //获得公钥
            String publicKeyStr = bytesToBase64(publicKey.getEncoded());
            System.out.println("公钥:" + publicKeyStr);
    
            RSA rsa = new RSA(privateKeyStr, publicKeyStr);
            System.out.println(rsa);
    
            //公钥加密,私钥解密
            byte[] encrypt = rsa.encrypt(StrUtil.bytes(text, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
            System.out.println("公钥加密:" + bytesToBase64(encrypt));
    
            byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
            System.out.println("私钥解密:" + new String(decrypt,StandardCharsets.UTF_8));
    
    //        //私钥加密,公钥解密
    //        byte[] encrypt2 = rsa.encrypt(StrUtil.bytes(text, CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
    //        System.out.println("私钥加密:" + bytesToBase64(encrypt2));
    //        byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);
    //        System.out.println("公钥解密:" + bytesToBase64(decrypt2));
    
            Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKeyStr, publicKeyStr);
            //签名
            byte[] data = text.getBytes(StandardCharsets.UTF_8);
            byte[] signed = sign.sign(data);
            String signedStr = bytesToBase64(signed);
            System.out.println("签名:" + signedStr);
    
            //验证签名
            boolean verify = sign.verify(data, base64ToBytes(signedStr));
            System.out.println("验签:" + verify);
    
        }
    
        /**
         * 字节数组转Base64编码
         *
         * @param bytes 字节数组
         * @return Base64编码
         */
        private static String bytesToBase64(byte[] bytes) {
            byte[] encodedBytes = Base64.getEncoder().encode(bytes);
            return new String(encodedBytes, StandardCharsets.UTF_8);
        }
    
        /**
         * Base64编码转字节数组
         *
         * @param base64Str Base64编码
         * @return 字节数组
         */
        private static byte[] base64ToBytes(String base64Str) {
            byte[] bytes = base64Str.getBytes(StandardCharsets.UTF_8);
            return Base64.getDecoder().decode(bytes);
        }
    }

    国密非对称加密(SM2)

    package com.hdwang.test.hutool;
    
    import cn.hutool.core.util.HexUtil;
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.crypto.SecureUtil;
    import cn.hutool.crypto.SmUtil;
    import cn.hutool.crypto.asymmetric.KeyType;
    import cn.hutool.crypto.asymmetric.SM2;
    
    import java.nio.charset.StandardCharsets;
    import java.security.KeyPair;
    import java.util.Base64;
    
    /**
     * 国密非对称加解密和加签验签算法
     *
     * @author wanghuidong
     * @date 2022/5/25 20:50
     */
    public class SmTest {
        public static void main(String[] args) {
            String text = "人最宝贵的是生命.生命对每个人只有一次.人的一生应当这样度过:当他回首往事的时候,不会因为虚度年华而悔恨,也不会因为碌碌无为而羞耻.这样,在临死的时候,他能够说:“我已把自己的整个的生命和全部的精力献给了世界上最壮丽的事业---------为人类的解放而斗争.”";
            System.out.println("原文:" + text);
    
            KeyPair pair = SecureUtil.generateKeyPair("SM2");
            byte[] privateKey = pair.getPrivate().getEncoded();
            byte[] publicKey = pair.getPublic().getEncoded();
            System.out.println("公钥:\n" + bytesToBase64(publicKey));
            System.out.println("私钥:\n" + bytesToBase64(privateKey));
    
            SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
            // 公钥加密,私钥解密
            String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
            System.out.println("加密后:" + encryptStr);
    
            String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));
            System.out.println("解密后:" + decryptStr);
            //加签
            String sign = sm2.signHex(HexUtil.encodeHexStr(text));
            System.out.println("签名:" + sign);
            //验签
            boolean verify = sm2.verifyHex(HexUtil.encodeHexStr(text), sign);
            System.out.println("验签:" + verify);
    
        }
    
    
        /**
         * 字节数组转Base64编码
         *
         * @param bytes 字节数组
         * @return Base64编码
         */
        private static String bytesToBase64(byte[] bytes) {
            byte[] encodedBytes = Base64.getEncoder().encode(bytes);
            return new String(encodedBytes, StandardCharsets.UTF_8);
        }
    
        /**
         * Base64编码转字节数组
         *
         * @param base64Str Base64编码
         * @return 字节数组
         */
        private static byte[] base64ToBytes(String base64Str) {
            byte[] bytes = base64Str.getBytes(StandardCharsets.UTF_8);
            return Base64.getDecoder().decode(bytes);
        }
    }

    目前本人实现了一套简单的安全对外开放api的sdk,可以直接拿来使用,支持上述方案一和方案二。

    https://github.com/hdwang123/openapi

    参考文章:

    https://www.cnblogs.com/pcheng/p/9629621.html

    https://blog.csdn.net/woniu211111/article/details/108114402

    https://blog.csdn.net/draven1122/article/details/55212252

  • 相关阅读:
    GL线程
    Texture,TextureRegion,Sprite,SpriteBatch
    设置壁纸
    CentOS7下安装MySQL,修改端口
    读《大道至简》有感
    课程作业01汇总整理
    读《大道至简》有感(伪代码)
    01实验性问题总结归纳
    oracle日期时间的加减法
    C# 根据年、月、周、星期获得日期等
  • 原文地址:https://www.cnblogs.com/hdwang/p/16310314.html
Copyright © 2020-2023  润新知