Java SM2加密相关实现与简单原理

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

        首先我们应该了解SM2加密的主要用途:数字签名、密钥交换和公钥加密等应用。以下为SM2加密的简单原理:

  1. 密钥生成:首先,生成一对公钥和私钥。公钥用于加密和验证签名,私钥用于解密和生成签名。

  2. 加密过程:

    • 随机选择一个临时的非零整数k,计算椭圆曲线点C = k * G,其中G是曲线上的基点。
    • 将明文数据转换为椭圆曲线上的点M。
    • 计算椭圆曲线点C1 = k * G。
    • 计算椭圆曲线点S = (h + x) * C1,其中h是哈希值,x是私钥。
    • 将明文数据与S进行异或运算,得到密文C2。
    • 将C1和C2组合在一起作为最终的加密结果。
  3. 解密过程:

    • 使用私钥x计算椭圆曲线点C1' = x * C1。
    • 从C1'中提取出明文数据M'。
    • 将M'与密文C2进行异或运算,得到原始的明文数据。

        我们要了解对于同一个公钥和同一个数据,多次加密的密文是不相同的!!!文章来源地址https://www.toymoban.com/news/detail-816827.html

import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
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.crypto.signers.SM2Signer;
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.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;

/**
 * SM2工具类
 * @author van
 */
public class SM2Util {

    /**
     * 生成 SM2 公私钥对
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     */
    public static KeyPair geneSM2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
        // 获取一个椭圆曲线类型的密钥对生成器
        final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
        // 产生随机数
        SecureRandom secureRandom = new SecureRandom();
        // 使用SM2参数初始化生成器
        kpg.initialize(sm2Spec, secureRandom);
        // 获取密钥对
        KeyPair keyPair = kpg.generateKeyPair();
        return keyPair;
    }

    /**
     * 生产hex秘钥对
     */
    public static void geneSM2HexKeyPair(){
        try {
            KeyPair keyPair = geneSM2KeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            System.out.println("========  EC X Y keyPair    ========");
            System.out.println(privateKey);
            System.out.println(publicKey);
            System.out.println("========  hex keyPair       ========");
            System.out.println("hex priKey: " + getPriKeyHexString(privateKey));
            System.out.println("hex pubKey: " + getPubKeyHexString(publicKey));
            System.out.println("========  base64 keyPair    ========");
            System.out.println("base64 priKey: " + new String(Base64.getEncoder().encode(privateKey.getEncoded())));
            System.out.println("base64 pubKey: " + new String(Base64.getEncoder().encode(publicKey.getEncoded())));
            System.out.println("========  generate finished ========");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取私钥(16进制字符串,头部不带00长度共64)
     *
     * @param privateKey 私钥PrivateKey型
     * @return
     */
    public static String getPriKeyHexString(PrivateKey privateKey) {
        // OK
//        BCECPrivateKey s=(BCECPrivateKey)privateKey;
//        String priKeyHexString = Hex.toHexString(s.getD().toByteArray());
//        if(null!= priKeyHexString && priKeyHexString.length()==66 && "00".equals(priKeyHexString.substring(0,2))){
//            return priKeyHexString.substring(2);
//        }
        // OK
        BCECPrivateKey key = (BCECPrivateKey) privateKey;
        BigInteger intPrivateKey = key.getD();
        String priKeyHexString = intPrivateKey.toString(16);
        return priKeyHexString;
    }
    /**
     * 获取私钥 base64字符串
     *
     * @param privateKey 私钥PrivateKey型
     * @return
     */
    public static String getPriKeyBase64String(PrivateKey privateKey) {
        return new String(Base64.getEncoder().encode(privateKey.getEncoded()));
    }

    /**
     * 获取公钥(16进制字符串,头部带04长度共130)
     *
     * @param publicKey 公钥PublicKey型
     * @return
     */
    public static String getPubKeyHexString(PublicKey publicKey) {
        BCECPublicKey key = (BCECPublicKey) publicKey;
        return Hex.toHexString(key.getQ().getEncoded(false));
    }
    /**
     * 获取公钥 base64字符串
     *
     * @param publicKey 公钥PublicKey型
     * @return
     */
    public static String getPubKeyBase64String(PublicKey publicKey) {
        return new String(Base64.getEncoder().encode(publicKey.getEncoded()));
    }

    /**
     * SM2加密算法
     *
     * @param publicKey 公钥
     * @param data      明文数据
     * @return
     */
    public static String encrypt(String data, PublicKey publicKey) {
        return encrypt(data.getBytes(StandardCharsets.UTF_8), publicKey);
    }

    public static String encrypt(byte[] data, PublicKey publicKey) {
        BCECPublicKey key = (BCECPublicKey) publicKey;
        return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));
    }

    public static String encrypt(String data, String pubKeyHexString) {
        return encrypt(data.getBytes(StandardCharsets.UTF_8), pubKeyHexString);
    }

    /**
     * SM2加密算法
     *
     * @param pubKeyHexString 公钥(16进制字符串)
     * @param data            明文数据
     * @return hex字符串
     */
    public static String encrypt(byte[] data, String pubKeyHexString) {
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        // 设置sm2为加密模式
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes = null;
        try {
            arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);
        } catch (Exception e) {
            System.out.println("SM2加密时出现异常:" + e.getMessage());
        }
        return Hex.toHexString(arrayOfBytes);

    }

    /**
     * SM2解密算法
     * @param cipherData    hex格式密文
     * @param privateKey    密钥PrivateKey型
     * @return              明文
     */
    public static String decrypt(String cipherData, PrivateKey privateKey) {
        return decrypt(Hex.decode(cipherData), privateKey);
    }

    public static String decrypt(byte[] cipherData, PrivateKey privateKey) {
        BCECPrivateKey key = (BCECPrivateKey) privateKey;
        return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));
    }

    public static String decrypt(String cipherData, String priKeyHexString) {
        // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
        if (!cipherData.startsWith("04")) {
            cipherData = "04" + cipherData;
        }
        return decrypt(Hex.decode(cipherData), priKeyHexString);
    }

    /**
     * SM2解密算法
     *
     * @param cipherData      密文数据
     * @param priKeyHexString 私钥(16进制字符串)
     * @return
     */
    public static String decrypt(byte[] cipherData, String priKeyHexString) {
        //获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        // 设置sm2为解密模式
        sm2Engine.init(false, privateKeyParameters);

        String result = "";
        try {
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);
            return new String(arrayOfBytes);
        } catch (Exception e) {
            System.out.println("SM2解密时出现异常:" + e.getMessage());
        }
        return result;
    }

    /**
     * @param data
     * @param priKeyHexString hex私钥,长度64
     * @return hex格式签名值
     * @throws Exception
     */
    public static String sign(String data, String priKeyHexString) throws Exception {
        return sign(data.getBytes(StandardCharsets.UTF_8), priKeyHexString);
    }

    /**
     * 签名
     * @param data              原始数据,字节数组
     * @param priKeyHexString   hex私钥,64长度
     * @return                  Hex字符串
     * @throws Exception
     */
    public static String sign(byte[] data, String priKeyHexString) throws Exception {
        String signValue = null;
        SM2Signer signer = new SM2Signer();
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        CipherParameters param = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(priKeyHexString, 16), domainParameters));
        signer.init(true, param);
        signer.update(data, 0, data.length);
        signValue = Hex.toHexString(signer.generateSignature());
        return signValue;
    }

    /**
     * 验签
     * @param data                  原始数据
     * @param signValue             原始签名值(hex型)
     * @param publicKeyHexString    hex130长度公钥
     * @return                      ture or false
     * @throws Exception
     */
    public static boolean verify(String data, String signValue, String publicKeyHexString) throws Exception {
        return verify(data.getBytes(StandardCharsets.UTF_8), Hex.decode(signValue), publicKeyHexString);
    }

    /**
     * 验签
     * @param data                  原始数据字节数组
     * @param sign                  字节数组()
     * @param publicKeyHexString    hex130长度公钥
     * @return                      true or false
     * @throws Exception
     */
    public static boolean verify(byte[] data, byte[] sign, String publicKeyHexString) throws Exception {
        SM2Signer signer = new SM2Signer();
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        if (publicKeyHexString.length() == 128) {
            publicKeyHexString = "04" + publicKeyHexString;
        }
        ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKeyHexString));
        CipherParameters param = new ECPublicKeyParameters(ecPoint, domainParameters);
        signer.init(false, param);
        signer.update(data, 0, data.length);
        return signer.verifySignature(sign);
    }

    /**
     * 私钥生成公钥
     * @param priKeyHexString 私钥Hex格式,必须64位
     * @return 公钥Hex格式,04开头,130位
     * @throws Exception 例如:
     *                   04181db7fe400641115c0dec08e23d8ddb94c5999f2fb6efd03030780142e077a63eb4d47947ef5baee7f40fec2c29181d2a714d9c6cba87b582f252a4e3e9a9f8
     *                   11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a
     */
    public static String getPubKeyByPriKey(String priKeyHexString) throws Exception {
        if (priKeyHexString == null || priKeyHexString.length() != 64) {
            System.err.println("priKey 必须是Hex 64位格式,例如:11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a");
            return "";
        }
        String pubKeyHexString = null;
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);

        ECParameterSpec ecParameterSpec = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(privateKeyD, ecParameterSpec);
        PrivateKey privateKey = null;
        privateKey = KeyFactory.getInstance("EC", new BouncyCastleProvider()).generatePrivate(ecPrivateKeySpec);

        // 临时解决办法
        String pointString = privateKey.toString();
//        System.out.println(pointString);
        String pointString_X = pointString.substring(pointString.indexOf("X: ") + "X: ".length(), pointString.indexOf("Y: ")).trim();
        String pointString_Y = pointString.substring(pointString.indexOf("Y: ") + "Y: ".length()).trim();
//        System.out.println(pointString_X);
//        System.out.println(pointString_Y);

        pubKeyHexString = "04" + pointString_X + pointString_Y;
        return pubKeyHexString;

    }

    // 将字符串转换为十六进制字符串
    public static String stringToHex(String str) {
        StringBuilder hexString = new StringBuilder();

        for (char ch : str.toCharArray()) {
            hexString.append(Integer.toHexString((int) ch));
        }

        return hexString.toString();
    }

    public static void main(String[] args) throws Exception {
        System.out.println("======  sm2utils test  ======");

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username","jsdxzx");
        jsonObject.put("password","1");
        jsonObject.put("clientId","jsdxzx");
        jsonObject.put("grantType","password_credentials");
        jsonObject.put("passwordEncode",true);
        String jsonString = JSON.toJSONString(jsonObject);

        String M = jsonString;
        System.out.println("mingwen\t" + M);

        System.out.println("begin 开始生成密钥对>>>");
        KeyPair keyPair = geneSM2KeyPair();

        PublicKey publicKey = keyPair.getPublic();
        String pubKeyHexString = getPubKeyHexString(publicKey);
        System.out.println("publicKey\t" + pubKeyHexString);

        PrivateKey privateKey = keyPair.getPrivate();
        String priKeyHexString = getPriKeyHexString(privateKey);
        System.out.println("privateKey\t" + priKeyHexString);
        System.out.println("end   结束生成密钥对>>>");

        priKeyHexString="4f0341e5977b175e0ec0e1fdefd2799b13dd25c51716133ef42ba76c0edd973d"; //1
        pubKeyHexString="04e523210b3407464839723558e0d82765b9e2cac9491bd86c99c89b9fc43fbe9a94395ee3138dbc4ae43daa8fe01fd512de8568102e34c66989eb2b306611b518"; //1
        System.out.println("publicKey\t" + pubKeyHexString);

        String cipherData = encrypt(M, pubKeyHexString);
        System.out.println("miwen\t" + cipherData);

        String text = decrypt(cipherData, priKeyHexString);
        System.out.println("jiemi\t" + text);

        String sign = sign(M, priKeyHexString);
        System.out.println("signvalue\t" + sign);
        sign="304402204bbd4b026f58f1e072c2ab1f736a730ed5c2f6773ef4855df5e87f9ea54f14be02205e9b6146b5273e6f37fe6d9d8f059bc46f7042a10da224130a4e0ba8619d967c";

        boolean verifyResult = verify(M, sign, pubKeyHexString);
        System.out.println("verifyResult\t" + verifyResult);

    }
}

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

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

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

相关文章

  • 使用 Java Bouncy Castle实现国密算法SM4、SM3以及SM2的加密

    国密算法的实现借助了Java库函数 Bouncy Castle,加密库安装使用教程请参考链接 SM4,又称为商密算法,是一种分组密码算法,于2012年由中国密码技术研究中心(中国密码学会成员)发布,目前已成为我国国家密码算法,并在多个领域得到了广泛的应用。SM4算法采用了32轮迭代结

    2024年02月16日
    浏览(30)
  • 国密算法SM2,SM3,SM4-java实现原理

    SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法,基于ECC。其签名速度与秘钥生成速度都快于RSA,非对称加密,该算法已公开 SM3是中华人民共和国政府采用的一种密码散列函数标准,由国家密码管理局于2010年12月17日发布。SM3主要用数字签名及验证、消息认

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

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

    2024年02月02日
    浏览(43)
  • 4.Java开源RSA/SM2非对称加密算法对比介绍

    前期内容导读: Java开源RSA/AES/SHA1/PGP/SM2/SM3/SM4加密算法介绍 Java开源AES/SM4/3DES对称加密算法介绍及其实现 Java开源AES/SM4/3DES对称加密算法的验证说明 非对称加密 主要是指 秘钥对 是非对称的(相对于 对称加密 而言),简单理解就是加密秘钥和解密秘钥不同,一般叫做公钥和私

    2024年02月14日
    浏览(34)
  • 适用于前后端公用的SM2国密加密传输, JAVA + VUE

    由于等保和多个系统间的数据传输加密, 写了一个共有的前端与后端, 后端与后端,的通用算法SM2简单加密,  不需要验签, 几行代码搞定.  引包, 测试好几遍, 这个包适合jdk1.8使用 引包, 没有意外就应该直接能用下面代码了     publicKey:04aa909915f87880507e3de515220cc8f82b1c5693f56a0475b3

    2024年02月16日
    浏览(34)
  • 国密算法SM2/3/4简单比较,以及基于Java的SM4(ECB模式,CBC模式)对称加解密实现

    常用的国密算法包含SM2,SM3,SM4。以下针对每个算法使用场景进行说明以比较其差异 SM2:非对称加密算法,可以替代RSA 数字签名,SM2为非对称加密,加解密使用一对私钥和公钥,只有签名发行者拥有私钥,可用于加密,其他需要验证解密或验签者使用公钥进行。如果使用公

    2024年04月13日
    浏览(30)
  • 国密算法(SM2)java语言的实现:利用bcprov库来实现SM2算法,非对称算法

    随着密码技术和计算机技术的发展,目前常用的1024位RSA算法面临严重的安全威胁,我们国家密码管理部门经过研究,决定采用SM2椭圆曲线算法替换RSA算法。 SM2是非对称加密算法; SM2是基于椭圆曲线密码的公钥密码算法标准; SM2是国密算法,用于替换RSA/DH/ECDSA/ECDH等国际算法

    2024年02月03日
    浏览(32)
  • 小程序 实现 国密 sm2 加密

    第一步:进入微信小程序项目目录下,输入cmd    第二步:加载插件包 第三步:加载成功之后,会参生这个文件夹     第四步:然后在对应的js文件中引入miniprogram-sm-crypto/index.js   第五步: 加密方式,加密方法  

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

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

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

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

    2024年02月14日
    浏览(56)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包