Java 算法SM2加密解密_java sm2加密-程序员宅基地

技术标签: Java 算法  算法  加密解密  java  

简介

什么是SM2

SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法。

SM2 算法和 RSA 算法都是公钥密码算法,SM2 算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换 RSA 算法。

随着密码技术和计算机技术的发展,目前常用的 1024RSA 算法面临严重的安全威胁,我们国家密码管理部门经过研究,决定采用 SM2 椭圆曲线算法替换RSA算法。

SM2算法和RSA算法比较

SM2性能更优更安全:密码复杂度高、处理速度快、机器性能消耗更小

- SM2 RSA
算法结构 基本椭圆曲线(ECC) 基于特殊的可逆模幂运算
计算复杂度 完全指数级 亚指数级
存储空间 192-256bit 2048-4096bit
秘钥生成速度 较RSA算法快百倍以上
解密加密速度 较快 一般
POM依赖
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-ext-jdk15to18</artifactId>
    <version>1.68</version>
</dependency>
<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15to18</artifactId>
	<version>1.68</version>
</dependency>
Java工具类
package com.file.system.tools.sm2;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

/**
 * @ClassName SM2Utils
 * @Description SM2算法工具类
 * @Author msx
 * @Date 2021/9/24 16:50
 * @Version 1.0
 */
public class SM2Utils {
    

    /**
     * @Description 生成秘钥对
     * @Author msx
     * @return KeyPair
     */
    public static KeyPair createECKeyPair() {
    
        //使用标准名称创建EC参数生成的参数规范
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");

        // 获取一个椭圆曲线类型的密钥对生成器
        final KeyPairGenerator kpg;
        try {
    
            kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
            // 使用SM2算法域参数集初始化密钥生成器(默认使用以最高优先级安装的提供者的 SecureRandom 的实现作为随机源)
            // kpg.initialize(sm2Spec);

            // 使用SM2的算法域参数集和指定的随机源初始化密钥生成器
            kpg.initialize(sm2Spec, new SecureRandom());

            // 通过密钥生成器生成密钥对
            return kpg.generateKeyPair();
        } catch (Exception e) {
    
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @Description 公钥加密
     * @Author msx
     * @param publicKeyHex SM2十六进制公钥
     * @param data         明文数据
     * @return String
     */
    public static String encrypt(String publicKeyHex, String data) {
    
        return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);
    }

    /**
     * @Description 公钥加密
     * @Author msx
     * @param publicKey SM2公钥
     * @param data      明文数据
     * @param modeType  加密模式
     * @return String
     */
    public static String encrypt(BCECPublicKey publicKey, String data, int modeType) {
    
        //加密模式
        SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
        if (modeType != 1) {
    
            mode = SM2Engine.Mode.C1C2C3;
        }
        //通过公钥对象获取公钥的基本域参数。
        ECParameterSpec ecParameterSpec = publicKey.getParameters();
        ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                ecParameterSpec.getG(), ecParameterSpec.getN());
        //通过公钥值和公钥基本参数创建公钥参数对象
        ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);
        //根据加密模式实例化SM2公钥加密引擎
        SM2Engine sm2Engine = new SM2Engine(mode);
        //初始化加密引擎
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
        byte[] arrayOfBytes = null;
        try {
    
            //将明文字符串转换为指定编码的字节串
            byte[] in = data.getBytes("utf-8");
            //通过加密引擎对字节数串行加密
            arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
        } catch (Exception e) {
    
            System.out.println("SM2加密时出现异常:" + e.getMessage());
            e.printStackTrace();
        }
        //将加密后的字节串转换为十六进制字符串
        return Hex.toHexString(arrayOfBytes);
    }

    /**
     * @Description 私钥解密
     * @Author msx
     * @param privateKeyHex SM2十六进制私钥
     * @param cipherData    密文数据
     * @return String
     */
    public static String decrypt(String privateKeyHex, String cipherData) {
    
        return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1);
    }

    /**
     * @Description 私钥解密
     * @Author msx
     * @param privateKey SM私钥
     * @param cipherData 密文数据
     * @param modeType   解密模式
     * @return
     */
    public static String decrypt(BCECPrivateKey privateKey, String cipherData, int modeType) {
    
        //解密模式
        SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
        if (modeType != 1)
            mode = SM2Engine.Mode.C1C2C3;
        //将十六进制字符串密文转换为字节数组(需要与加密一致,加密是:加密后的字节数组转换为了十六进制字符串)
        byte[] cipherDataByte = Hex.decode(cipherData);
        //通过私钥对象获取私钥的基本域参数。
        ECParameterSpec ecParameterSpec = privateKey.getParameters();
        ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                ecParameterSpec.getG(), ecParameterSpec.getN());
        //通过私钥值和私钥钥基本参数创建私钥参数对象
        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),
                ecDomainParameters);
        //通过解密模式创建解密引擎并初始化
        SM2Engine sm2Engine = new SM2Engine(mode);
        sm2Engine.init(false, ecPrivateKeyParameters);
        String result = null;
        try {
    
            //通过解密引擎对密文字节串进行解密
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
            //将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)
            result = new String(arrayOfBytes, "utf-8");
        } catch (Exception e) {
    
            System.out.println("SM2解密时出现异常" + e.getMessage());
        }
        return result;
    }
    //椭圆曲线ECParameters ASN.1 结构
    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    //椭圆曲线公钥或私钥的基本域参数。
    private static ECParameterSpec ecDomainParameters = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());

    /**
     * @Description 公钥字符串转换为 BCECPublicKey 公钥对象
     * @Author msx
     * @param pubKeyHex 64字节十六进制公钥字符串(如果公钥字符串为65字节首个字节为0x04:表示该公钥为非压缩格式,操作时需要删除)
     * @return BCECPublicKey SM2公钥对象
     */
    public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) {
    
        //截取64字节有效的SM2公钥(如果公钥首个字节为0x04)
        if (pubKeyHex.length() > 128) {
    
            pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128);
        }
        //将公钥拆分为x,y分量(各32字节)
        String stringX = pubKeyHex.substring(0, 64);
        String stringY = pubKeyHex.substring(stringX.length());
        //将公钥x、y分量转换为BigInteger类型
        BigInteger x = new BigInteger(stringX, 16);
        BigInteger y = new BigInteger(stringY, 16);
        //通过公钥x、y分量创建椭圆曲线公钥规范
        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecDomainParameters);
        //通过椭圆曲线公钥规范,创建出椭圆曲线公钥对象(可用于SM2加密及验签)
        return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    /**
     * @Description 私钥字符串转换为 BCECPrivateKey 私钥对象
     * @Author msx
     * @param privateKeyHex 32字节十六进制私钥字符串
     * @return BCECPrivateKey SM2私钥对象
     */
    public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {
    
        //将十六进制私钥字符串转换为BigInteger对象
        BigInteger d = new BigInteger(privateKeyHex, 16);
        //通过私钥和私钥域参数集创建椭圆曲线私钥规范
        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters);
        //通过椭圆曲线私钥规范,创建出椭圆曲线私钥对象(可用于SM2解密和签名)
        return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    public static void main(String[] args) {
    
        String publicKeyHex = null;
        String privateKeyHex = null;
        KeyPair keyPair = createECKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        if (publicKey instanceof BCECPublicKey) {
    
            //获取65字节非压缩缩的十六进制公钥串(0x04)
            publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
            System.out.println("---->SM2公钥:" + publicKeyHex);
        }
        PrivateKey privateKey = keyPair.getPrivate();
        if (privateKey instanceof BCECPrivateKey) {
    
            //获取32字节十六进制私钥串
            privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
            System.out.println("---->SM2私钥:" + privateKeyHex);
        }

        /**
         * 公钥加密
         */
        String data = "=========待加密数据=========";

        //将十六进制公钥串转换为 BCECPublicKey 公钥对象
        String encryptData = encrypt(publicKeyHex, data);
        System.out.println("---->加密结果:" + encryptData);

        /**
         * 私钥解密
         */
        //将十六进制私钥串转换为 BCECPrivateKey 私钥对象
        data = decrypt(privateKeyHex, encryptData);
        System.out.println("---->解密结果:" + data);
    }
}

扩展资料

国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。
(1)SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
(2)SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
(3)SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
(4)SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。

这里有一篇关于SM1、SM2 、SM3、 SM4算法详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_36949163/article/details/120511612

智能推荐

java数据结构与算法刷题-----LeetCode405. 数字转换为十六进制数-程序员宅基地

文章浏览阅读694次,点赞14次,收藏16次。LeetCode405. 数字转换为十六进制数

multisim出现收敛问题解决办法_multisim仿真收敛问题-程序员宅基地

文章浏览阅读1w次,点赞8次,收藏28次。multisim出现收敛问题解决方法_multisim仿真收敛问题

asp.net 几种跨页面传值的方法_asp.net数据传递到另一个页面-程序员宅基地

文章浏览阅读941次。第一种方法:通过URL链接地址传递,也即QueryString优点:实现起来非常简单缺点:传递的值是会显示在浏览器的地址栏上的(不安全),同时又不能传递对象,但是在传递的值少而安全性要求不高的情况下,这个方法还是一个不错的方案send.aspxResponse.Redirect("receive.aspx?name=xxx");receive.aspxstring us_asp.net数据传递到另一个页面

女网红靠GPT-4交1000+男朋友,聊天按分钟收费,一周收入50万!-程序员宅基地

文章浏览阅读629次。点击“开发者技术前线”,选择“星标”让一部分开发者看到未来来自:量子位 | 公众号 QbitAI注意看,这个女人叫卡琳,靠着GPT-4,她现在同时谈着1000+男朋友。对,我知道事情听上去有些离谱。就连GPT-4自己,都直呼“我一个AI都觉得非常不常见”。但是先别急,因为更让人挠头的事情还在后面……在与“卡琳”相处的过程中,男友们每聊一分钟天,就要支付给她1美元。并且就在我敲字这档口,候选男友列表..._这位在snapchat上坐拥180万粉丝的网红,已经利用包括gpt-4在内的一波最新ai技术,直

AmberTools23的安装-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏8次。e. 配置环境 即 在 用户根目录下 修改 .bashrc文件,添加 export PATH=/home/user/miniconda/bin:$PATH 其中 User换成用户名,并最好核实一下安装路径。5、安装跑完就可以了,平时要用的话,首先要 激活环境 conda activate AmberTools23 然后就可以开始命令计算了。2、利用终端对*.sh文件进行可执行授权 (在文件目录下执行 chmod +x ./*.sh,星号换成文件名)c. 确认安装路径,或者按需修改。_ambertools

高中信息技术python练习题_Python基础练习题5-程序员宅基地

文章浏览阅读5.2k次,点赞2次,收藏5次。import random"""1、实现剪刀石头布游戏,提示用户输入要出的拳 :石头(1)/剪刀(2)/布(3)/退出(4) 电脑随机出拳比较胜负,显示 用户胜、负还是平局。(提示:while循环加条件判断,做判断时建议先分析胜负的情况)user random1 2 1-2 = -12 3 2-3 = -13 1 3- 1 ..._高中信息技术python编程例题

随便推点

C语言函数调用栈_函数指针跳转的堆栈问题-程序员宅基地

文章浏览阅读855次。程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call stack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。 不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。1 寄存器分配 寄存器是处理器加工数据或运行程序的重要载体,用于存..._函数指针跳转的堆栈问题

别再瞎考证了!关于网络安全行业的证书,看完这篇,明明白白考证_网安参加比赛重要还是考证重要-程序员宅基地

文章浏览阅读742次。3、如果你是银行证券等金融行业,可报考CISA(国际注册信息系统审计师)、CISSP(国际注册信息安全专家)、CISM(国际注册信息安全经理)。4、如果你是安全技术支持、技术售前、工程师,可报考 CISSP(注册信息安全专家)、CISP(注册信息安全人员)。6、如果你专注于应急响应,可报考CISP-IRE(注册应急响应工程师)、CISP-IRE(注册应急响应专家)。5、如果你专注于渗透测试,可报考 CISP-PTE(注册渗透测试工程师)、CISP-PTS(注册渗透专家)。_网安参加比赛重要还是考证重要

怎样查找linux内核所在的分区,linux运维笔记:CentOS 系统的分区、启动及目录查询...-程序员宅基地

文章浏览阅读655次。8种机械键盘轴体对比本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?写在前面主要整理初次CentOS的基本操作,可能会显得比较乱。系统分区CentOS分区基本规则分区名称空间大小/boot200M/swap4G/20G/data剩余磁盘空间注:如果是虚拟机仅仅用来学习linux,磁盘空间有限的情况下,处 /boot 、/swap 之外的空间都可以分给 / 根目录,没有必要再分出/data..._内核在哪个分区

JavaScript随机生成一个满足数独结果的二维数组-程序员宅基地

文章浏览阅读219次。【代码】JavaScript随机生成一个满足数独结果的二维数组。

初识Cpp之 一、基础知识-程序员宅基地

文章浏览阅读1.8k次。​ C++融合了三种不同的编程方式:1、C语言代表的过程性语言,2、C++在C语言的基础上添加的类代表的面向对象语言,3、C++模板支持的泛型编程。使用C++的原因之一是为了利用其面向对象的特性。要利用这种特性,必须对标准C语言知识有较为深入的了解,因为它提供了基本类型、运算符、控制结构和语法规则。​ 面向对象编程(Object Oriented Programming, OOP)的诞生,试图让语言来满足问题的要求,其理念是设计与问题本质特性相对应的数据格式。_cpp

计算机教研评课记录,信息技术2.0 | 评课磨课共成长 信息技术促进步 ——东光县第二实验小学信息技术2.0数学组 课例研讨...-程序员宅基地

文章浏览阅读6.8k次。在我校推广信息技术2.0活动中,数学教研组成员推出高世甜老师在二年级进行《认识时间》的磨课评课活动。活动中二年级组数学教师和青蓝工程教师及教务处何春香主任参加听评课活动。高老师利用现代化教学手段,优化教学过程,突破教学重点、难点。利用微课视频,引导学生自学钟面的认识和时间的认读方法,通过开展小组合作,锻炼了学生自主学习、合作交流和语言表达能力。通过信息技术手段对各班学生进行学情分析,根据每个班的学..._信息技术2.0教研组听评课记录

推荐文章

热门文章

相关标签