当前位置: 首页 > news >正文

SM2加签、验签,加密、解密

SM2加签、验签,加密、解密

一、前言

简介
SM2加密方式‌是一种基于椭圆曲线密码学(ECC)的非对称加密算法,由中国国家密码管理局发布,主要用于保障数据在传输和存储过程中的安全。SM2算法的核心是椭圆曲线方程y^2
= x^3 + ax + b,通过点的倍增操作生成公钥和私钥。私钥是一个秘密整数,公钥由私钥乘以基点得到。SM2算法的安全性基于椭圆曲线离散对数问题,即使知道公钥也无法轻易推导出私钥。‌
工作原理
SM2算法的工作原理基于椭圆曲线的数学特性。首先,选择一个合适的椭圆曲线和一个基点,然后用户随机生成一个私钥,使用私钥与基点进行标量乘法运算得到公钥。公钥和私钥一一对应,用于加密、解密和数字签名验证。
加密和解密过程
SM2算法本身主要用于数字签名和密钥交换,但它可以与其他对称加密算法结合使用进行数据的加密和解密。在加密过程中,使用共享密钥对数据进行对称加密;在解密过程中,使用相同的共享密钥对加密后的数据进行解密。
安全性
SM2算法的安全性基于椭圆曲线离散对数问题,其破译或求解难度基本上是指数级的,因此安全性较高。SM2推荐使用256位密钥长度,其加密强度等同于3072位RSA证书,而业界普遍采用的RSA证书通常为2048位。此外,SM2在签名和密钥交换方面采取了更为安全的机制,不同于国际标准的ECDSA和ECDH。
应用场景
SM2算法广泛应用于数据加密、数字签名、密钥交换等场景,特别适用于需要高安全性和高效性的场合,如金融、政府和物联网等领域。在实际应用中,SM2可以用于加密敏感数据,确保只有拥有相应私钥的接收方可以解密和访问原始数据。

二、业务流程

在这里插入图片描述

三、使用示例

2.1 引入包
  <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk18on</artifactId>
            <version>1.78.1</version>
        </dependency>
2.2 java工具类
public class EnAndDecrypt {

    private static final Logger log = LoggerFactory.getLogger(EnAndDecrypt.class);
	//生成的私钥
    static String privateKey = "1e58de0096c2fbcf7d251d63eb3432d2648b21489fd53d12d8878d55653d62fe";
   //生成的公钥
    static String publicKey = "046fbfab13134b3360493dfcbc090bf9d1094af8d95692331b3a60adda116ead29f323285e556083aae490a2d58130daf12ae7ca2a39732ada84f2c100ac47d4fa";
    
	//固定写法
    private static final X9ECParameters curveParams = GMNamedCurves.getByName("sm2p256v1");
	
    private static final ECDomainParameters domainParams = new ECDomainParameters(curveParams
            .getCurve(), curveParams.getG(), curveParams.getN(), curveParams.getH());

    public static void main(String[] args) throws Exception {
   		 //生成密钥对(私钥、公钥)
        generateKeyPair();
		//私钥加签
        privateSign();
        //验签
        verifyData();
    	//加密
        encrypt();
		//解密
        decryptData();
    }

    public static void encrypt()
            throws Exception
    {
        LineDTO line = LineDTO.builder()
                .type(Integer.valueOf(1))
                .startRegionCode("444444")
                .startRegionName("成都")
                .endRegionCode("454444")
                .endRegionName("巴中")
                .lineId(UUID.randomUUID().toString().replaceAll("-", ""))
                .lineName("成巴")
                .lineType(Integer.valueOf(2))
                .lineLevel(Integer.valueOf(2))
                .firstStationId(UUID.randomUUID().toString().replaceAll("-", ""))
                .firstStationName("成都南站")
                .lastStationId(UUID.randomUUID().toString().replaceAll("-", ""))
                .lastStationName("巴中北站")
                .lineStatus(Integer.valueOf(1))
                .build();
        String bizContent = encrypt(JSON.toJSONString(Arrays.asList(new LineDTO[] { line })));
        log.info("加密后密文:{}", bizContent);
    }

    public static String encrypt(String str) throws Exception {
        return encrypt(publicKey, str);
    }
    public static String encrypt(String publicKeyHex, String plainText) throws Exception {
        byte[] publicKeyBytes = Hex.decode(publicKeyHex);
        byte[] textBytes = plainText.getBytes(StandardCharsets.UTF_8);
        ECPoint point = domainParams.getCurve().decodePoint(publicKeyBytes);
        ECPublicKeyParameters publicKeyParams = new ECPublicKeyParameters(point, domainParams);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParams, new SecureRandom()));
        byte[] encryptedData = sm2Engine.processBlock(textBytes, 0, textBytes.length);
        return Hex.toHexString(encryptedData);
    }

    public static void privateSign() throws Exception {
        String bizContent = "";

        CommonDTO dto = CommonDTO.builder()
                .Interface("dzky_line")
                .encryType("SM2")
                .timestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                        .format(new Date()))
                .bizContent(bizContent)
                .build();

        String sign = privateSign(dto.toSignText());
        System.out.println("私钥签名后的结果:" + sign);
    }
    public static String privateSign(String str) throws Exception {
        return sign(str, privateKey);
    }

    public static String sign(String plainText, String privateKeyHex) throws Exception {
        System.out.println("签名的原字符串:" + plainText);
        byte[] textBytes = plainText.getBytes(StandardCharsets.UTF_8);
        BigInteger d = new BigInteger(privateKeyHex, 16);
        ECPrivateKeyParameters privateKeyParams = new ECPrivateKeyParameters(d, domainParams);
        SM2Signer signer = new SM2Signer();
        signer.init(true, privateKeyParams);
        signer.update(textBytes, 0, textBytes.length);
        byte[] sign = signer.generateSignature();
        return Hex.toHexString(sign);
    }

    public static Map<String, String> generateKeyPair() {
        ECKeyPairGenerator generator = new ECKeyPairGenerator();
        ECKeyGenerationParameters params = new ECKeyGenerationParameters(domainParams, new SecureRandom());
        generator.init(params);
        AsymmetricCipherKeyPair keyPair = generator.generateKeyPair();

        ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters)keyPair.getPrivate();
        ECPublicKeyParameters publicKey = (ECPublicKeyParameters)keyPair.getPublic();
        String privateKeyHex = privateKey.getD().toString(16);
        String publicKeyHex = Hex.toHexString(publicKey.getQ().getEncoded(false));

        Map map = new HashMap();
        map.put("publicKey", publicKeyHex);
        map.put("privateKey", privateKeyHex);
        System.out.println("私钥:" + privateKeyHex);
        System.out.println("公钥:" + publicKeyHex);
        return map;
    }

    public static void decryptData() throws Exception
    {
        String decrypt = decryptData("0473b120ad1a22b99a4169c1515eba4e6648880a0c72f667b0177ddb2ffe6e9a3b8388782c944bb7b5360ea659cb383ced3c3704de8f834644211a7e829420d5de2474895eea30d7e254d20d6072d04133cd522a568c9e4430bf7c605343d376ffc095aa15570eedcc7b68ec3abb70c9f90b04ce53452abe7e407a9425b94a04c4ec2eb743d5e06691581b4e9f97f3b74753cfc714db349ae8b6df2f3c13aa10052c8e84e987324e499c60c5e0ab49dccc219d8e62a84199f8e8fd3f1a51d812f8440f934ffa83ef9d1dc0f82d7572f5d2f95a1e68c6777870c7fba088df767195a4d37e1956b78407cc4869b9c40ea5b354e284bfd1c52f6e377f5ca680184f9c4149bef18a840c0223401d3183ec6632f27d8030c5cac8fec44df39b598948116384d11c22340efda462f82ff4b52976766fb98362fa31023d6fef7c913c6dff09bd4f882dbb8607f651d5a18fa7256e6ae37f17d095e3ee733cf4cf7f3d5b1206d2a0f93f0f6417d4b921d5ee9ceb772c4e60bee74a1d624536f8a467bf1d3e4fcd93c5b6bd1980ce8a0f932bd6d63f3aec4eade7fdd6af5214ea62c0af95acaa54bc5115020ad8ff7cc09a68fc60a590fd0bbb510ec022b2844bef03f93e9138a1a9ccf581f2f6796935d6a9931620b9e5828f9c8b4f6102ccfdd3d53488ae4f84245e365d1395");
        System.out.println("解密:" + decrypt);
    }

    public static String decryptData(String str) throws Exception {
        String decrypt = decrypt(privateKey, str);
        return decrypt;
    }

    public static String decrypt(String privateKeyHex, String encryptedHex) throws Exception {
        byte[] encryptedData = Hex.decode(encryptedHex);
        BigInteger d = new BigInteger(privateKeyHex, 16);
        ECPrivateKeyParameters privateKeyParams = new ECPrivateKeyParameters(d, domainParams);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2Engine.init(false, privateKeyParams);
        byte[] decryptedData = sm2Engine.processBlock(encryptedData, 0, encryptedData.length);
        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    public static boolean verifyData() throws Exception
    {
        boolean verify = verifyData("ak=e511ff01-4737-4edf-b439-568c1b8c538e&encryType=SM2&interface=dzky_line&timestamp=2025-01-24 13:48:34", "3045022100d0c62b30c5da211989da8b2a4eb7b7f2d08aee895fd01e1751e0fe1fd9b4fb1702202f5e1f11fd7cae454f21e59382223744485f75152c9ce77eea9a18f7015bb80b");

        System.out.println("验签结果:" + verify);
        return verify;
    }

    public static boolean verifyData(String str, String enSign) throws Exception
    {
        return verify(str, enSign, publicKey);
    }

    public static boolean verify(String plainText, String signHex, String publicKeyHex)
            throws Exception
    {
        byte[] signature = Hex.decode(signHex);
        byte[] publicKeyBytes = Hex.decode(publicKeyHex);
        byte[] textBytes = plainText.getBytes(StandardCharsets.UTF_8);
        ECPoint point = domainParams.getCurve().decodePoint(publicKeyBytes);
        ECPublicKeyParameters publicKeyParams = new ECPublicKeyParameters(point, domainParams);
        SM2Signer signer = new SM2Signer();
        signer.init(false, publicKeyParams);
        signer.update(textBytes, 0, textBytes.length);
        return signer.verifySignature(signature);
    }
}
2.3 工具dto
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CommonDTO {

    private String ak;
    private String Interface;
    private String encryType;
    private String sign;
    private String timestamp;
    private String bizContent;

    public String toSignText()
    {
        StringBuilder sb = new StringBuilder();
        boolean firstParam = true;

        if (StringUtils.isNotBlank(this.ak)) {
            sb.append("ak=").append(this.ak);
            firstParam = false;
        }
        if (StringUtils.isNotBlank(this.bizContent)) {
            if (!firstParam) {
                sb.append("&");
            }
            sb.append("bizContent=").append(this.bizContent);
            firstParam = false;
        }
        if (StringUtils.isNotBlank(this.encryType)) {
            if (!firstParam) {
                sb.append("&");
            }
            sb.append("encryType=").append(this.encryType);
            firstParam = false;
        }
        if (StringUtils.isNotBlank(this.Interface)) {
            if (!firstParam) {
                sb.append("&");
            }
            sb.append("interface=").append(this.Interface);
            firstParam = false;
        }
        if (StringUtils.isNotBlank(this.timestamp)) {
            if (!firstParam) {
                sb.append("&");
            }
            sb.append("timestamp=").append(this.timestamp);
        }
        return sb.toString();
    }
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LineDTO {
    private Integer type;
    private String orgcode;
    private String orgname;
    private String startRegionCode;
    private String startRegionName;
    private String endRegionCode;
    private String endRegionName;
    private String lineId;
    private String lineName;
    private Integer lineType;
    private Integer lineLevel;
    private String firstStationId;
    private String firstStationName;
    private String lastStationId;
    private String lastStationName;
    private Integer lineStatus;
    private Double mileage;
    private Double hghwaymile;
    private String lineDescription;
    private List<Integer> otaNames;
    private Integer sendSMS;
    private Object reserved;
}

相关文章:

  • 【洛谷】B3849 [GESP样题 三级] 进制转换
  • DeepSeek与ChatGPT的全面对比
  • electron 学习
  • 【virtiofs】ubuntu24.04+qemu7.0调试virtiofs
  • 洗牌加速!车规MCU“冷热交加”
  • rust学习一、入门之搭建简单开发环境
  • 【人工智能】通过python练习机器学习中的8大算法
  • SpringMVC详解
  • 使用爬虫获取1688商品分类:实战案例指南
  • 【技术解析】MultiPatchFormer:多尺度时间序列预测的全新突破
  • 固高控制卡的几种运动模式
  • 洛谷 P3660 USACO17FEB Why Did the Cow Cross the Road III 题解
  • 云点SEO:外贸独立站谷歌SEO优化的五大优势
  • Python----PyQt开发(PyQt高级:界面切换,信号与槽功能pyqtSignal)
  • Java基础概念
  • 国产编辑器EverEdit - 上下翻滚不迷路(历史编辑位置、历史光标位置回溯功能)
  • Typora“使用”教程
  • SpringBoot开发——初步了解SpringBoot
  • UE_C++ —— UObject Instance Creation
  • AcWing——1571. 完美序列
  • 刘元春在《光明日报》撰文:以法治护航民营经济高质量发展
  • 图集︱“中国排面”威武亮相
  • 被取消总统候选人资格,金文洙:将采取政治法律措施讨回公道
  • 上海证监局规范辖区私募经营运作,6月15日前完成自评自纠
  • 山东14家城商行中,仅剩枣庄银行年营业收入不足10亿
  • 欧盟公布关税反制清单,瞄准美国飞机、汽车等产品