三步实现Java的SM2前端加密后端解密

这篇具有很好参考价值的文章主要介绍了三步实现Java的SM2前端加密后端解密。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

秦医如毒,无药可解。

话不多说,先上需要用到的js文件下载链接  和  jsp前端代码

第一步:下载两个必备的js文件—— crypto-js.jssm2.js 

它们的下载链接如下↓(该网页不魔法上网的话会很卡,毕竟github,建议卡的话就多重新加载几次,我差不多试了8次才进去):

https://github.com/Saberization/SM2

进入网页,下载后拿取下方↓图片上的两个js就行:

jdk 1.6 实现sm2,前端,java,spring,后端,tomcat

然后,将这两js放到存放静态资源(static)的目录下,我放的是static中的sm2目录(sm2这个目录是我创的)

后面,在jsp的<head></head>中引入这两个js文件

<!--jsp引入sm2,实现前端加密-->
<script type="text/javascript" src="${pageContext.request.contextPath}/static/sm2/crypto-js.js" charset="utf-8"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/sm2/sm2.js" charset="utf-8"></script>
第二步前端jsp代码实现,下方的代码写在jsp页面的<script type="text/javascript"></script>中。

作用是,当调用下方↓函数的时候,会给你传入的值进行加密并返回。

注:【你生成的公钥】的生成方式可以在文章末尾的SM2Utils.java工具类中可以找到,是个main方法,一键生成,简单粗暴

<!--sm2加密函数-->
function sm2EncryptPwd(data) {
		<!--sm2公钥-->
		var publicKey = '你生成的公钥';
		return sm2Encrypt(data, publicKey, 1);
}

至此,前端加密结束,进入后端解密环节——

后端需要的jar包是:bcprov-jdk15on-1.68.jar

注意!!!该jar包必须要1.60版本以上,不然解密的时候会缺少必要的条件!!!并且我用的时候,本地环境是JDK1.8的,其他环境没试过,建议如有不同,先查一下。

<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk15on</artifactId>
  <version>1.68</version>
</dependency>
第三步后端代码实现。

导入jar包后,就是必要的工具类实体类:SM2Utils.java

你可以用工具类本身的加密方法解密,也可以用工具类中我写的 getJieMiJieGuo() 方法解密。

我个人建议用我写的,因为操作起来更简单些,只需要传入加密后的字符串,就可以返回解密的数据。

其中【你生成的公钥】和【你生成的私钥】可以通过工具类中的方法生成,生成完复制粘贴进去就行

注!后端和前端的公钥要保持一致!因为公钥私钥是配对使用的!

package com.test.common.utils;

import org.apache.commons.net.util.Base64;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
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;

public class SM2Utils {

    private static final String publicKey = "你生成的公钥";
    private static final String privateKey = "你生成私钥";


    /**
     * 解密,并且获取解密后的字符串
     *
     * @return 秦无解
     */
    public static String getJieMiJieGuo(String cipherData) {
        byte[] cipherDataByte = Hex.decode(cipherData);
        BigInteger privateKeyD = new BigInteger(privateKey, 16);
        X9ECParameters sm2ECParameters1 = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters1 = new ECDomainParameters(sm2ECParameters1.getCurve(), sm2ECParameters1.getG(), sm2ECParameters1.getN());
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters1);

        //用私钥解密,SM2Engine.Mode.C1C3C2得加,不然就会报错invalid cipher text
        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2Engine.init(false, privateKeyParameters);

        //processBlock得到Base64格式,记得解码
        byte[] arrayOfBytes = null;
        try {
            arrayOfBytes = Base64.decodeBase64(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        //得到明文:SM2 Encryption Test
        return new String(arrayOfBytes);
    }


    /**
     * @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 encrypt1(String publicKeyHex, String data) {
        return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);
    }

    /**
     * @Description 公钥加密
     * @Author msx
     * @param data         明文数据
     * @return String
     */
    public static String encrypt(String data) {
        return encrypt(getECPublicKeyByPublicKeyHex(publicKey), 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 decrypt1(String privateKeyHex, String cipherData) {
        return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1);
    }

    /**
     * @Description 私钥解密
     * @Author msx
     * @param s
     * @param cipherData    密文数据
     * @return String
     */
    public static String decrypt(String s, String cipherData) {
        return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKey), 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 = encrypt1(publicKeyHex, data);
        System.out.println("---->加密结果:" + encryptData);

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

最后,在后端接口中调用工具类的解密方法,就可以对前端传来的加密数据进行解密。

至此,✿✿ヽ(°▽°)ノ✿撒花。

此章暂时完结。文章来源地址https://www.toymoban.com/news/detail-766350.html

到了这里,关于三步实现Java的SM2前端加密后端解密的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java实现国密SM4的加密和解密方式(即时通讯的消息对话加密采用SM2还是SM4)

    1.对于即时通讯的消息对话加密采用SM2还是SM4更合适? 对于即时通讯的消息对话加密,建议采用SM4对称加密算法,而不是SM2非对称加密算法。 SM2主要用于数字签名和密钥交换,其加密速度比SM4慢,而且SM2不太适合对长消息进行加密,因为它只能对比较短的数据块进行加密,这

    2024年02月07日
    浏览(82)
  • javascript实现SM2加密解密

    前提JavaWeb环境 前端代码 实现步骤 java

    2024年02月14日
    浏览(65)
  • 国密SM2算法(JS加密,C#、Java解密)

    常见的渗透测试会将网站登录时密码使用明文传输视为风险。推荐使用国密算法或者RSA算法对密码进行加密传输。 RSA加密(JS加密,C#、Java解密)请参考《RSA对称加密(JS加密,C#、Java解密)》​​​​​​ 本文描述国密SM2算法的实现。 一、工作原理 前端js使用公钥进行加密

    2024年02月02日
    浏览(55)
  • java和js实现前端加密后端解密,后端加密前端解密(Base64)

    目录 1.前端加密后端解密 2.后端加密前端解密 在前端和后端数据传输时,常常涉及到隐私数据的传输(例如用户名和密码),这时,我们就需要对隐私数据进行加密解密 1.前端加密后端解密         1.1 前端jquery实现         1.2后端 2.后端加密前端解密         2.1后端加密

    2024年02月16日
    浏览(59)
  • Java实现SM2前后端加解密

    Sm2加解密原理,非对称加密,公钥加密,私钥解密。公私钥对成对生成,加密端解密端各自保存。用公钥加密必须要用对应的私钥才能解密,保证安全性。 这里我们实现的功能是前端加密,后端解密,这样前端暴露了公钥,后端持有配对的私钥,保证安全性。 1.jsp引入sm2 2

    2024年02月11日
    浏览(41)
  • 国密SM2: 加解密实现 java代码完整示例

    目录  具体Java代码SM2算法加解密实现Demo: pom依赖引入 :  国家密码管理局于2010年12月17日发布了SM2算法,并要求现有的基于RSA算法的电子认证系统、密钥管理系统、应用系统进升级改造,使用支持国密SM2算法的证书。    基于ECC椭圆曲线算法的SM2算法,则普遍采用256位密钥

    2024年02月13日
    浏览(46)
  • SM2加密实现之JAVA方式

    如有问题,请私信。

    2024年02月10日
    浏览(42)
  • 【vue+sm2】前端使用国密sm2,加解密

    1.第一步就是安装依赖 2.导入sm2 3.先定义私钥或者公钥,私钥是解密,公钥是加密 4.设置加密模式 5.解密的使用全码 6.加密的实现全码

    2024年02月16日
    浏览(49)
  • Java SM2加密相关实现与简单原理

            首先我们应该了解SM2加密的主要用途:数字签名、密钥交换和公钥加密等应用。以下为SM2加密的简单原理: 密钥生成:首先,生成一对公钥和私钥。公钥用于加密和验证签名,私钥用于解密和生成签名。 加密过程: 随机选择一个临时的非零整数k,计算椭圆曲线点

    2024年01月23日
    浏览(42)
  • OpenSSL 3.1.1 ECC 加密、解密、签名、验签(国密 sm2、sm3)

    openssl 3 默认废弃了 旧版本 (opessl 1.x) 的部分api 导致部分旧ecc 代码无法使用(可以通过配置编译选项打开) ,这里展示如何使用新接口用ECC 进行加密解密。 新接口是真的方便,基本上你都不需要懂啥密码学知识,对我们这种密码白痴来说太好了 头文件 生成密钥对 导出公

    2024年02月05日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包