springboot集成国密算法SM2

一、简介

  SM2国密算法是国产椭圆曲线非对称公钥算法,国家商用密码标准,2010 年国密局发布,替代 RSA,分三大能力:数字签名、公钥加解密、密钥协商,必须搭配国密哈希 SM3 使用。

  标准使用GM/T 0003-2012,国标 GB/T 32918-2016,已纳入 ISO 国际密码标准。椭圆曲线离散对数 ECDLP 难题,固定国密标准素域曲线sm2p256v1(CURVE_SM2),曲线参数由国家密码管理局固定,不可自定义。256 位 SM2 ≈ RSA3072 安全强度,远优于 RSA2048;密钥更短、运算更快、CPU 开销低。

二、主要功能模块

1.数字签名算法

用于身份认证和数据完整性验证基于椭圆曲线离散对数问题(ECDLP)签名长度:64字节(r和s各32字节)

javascript 复制代码
签名流程:
预处理:用用户 ID + 公钥 + SM3生成 Z 值,H=SM3(Z||原文M)(区别于 ECDSA:带用户 ID 防公钥替换攻击)
随机生成 k,计算k*G=(x1,y1)(G 为国密曲线基点)
r=(H+x1) mod n、s=(k-r.d).(k^{-1})mod n,输出Sign(r,s)

验签流程:
公钥 Q=d*G,还原 r、s;通过公钥反向计算校验 r 是否合法,一致则验签通过
业务场景:接口签名、电子签章、证书签名、区块链签名

2.密钥交换协议

双方通过协商生成共享会话密钥

支持双向身份认证

防止中间人攻击

3.公钥加密算法

加密:接收方公钥加密明文 M → C1+C2+C3

解密:接收方私钥解密 C1,派生密钥解密 C2,校验 C3 哈希防篡改

限制:非对称只能加密短数据(小于曲线阶长≈32 字节),长数据必须 SM2 加密 SM4 密钥、SM4 加密明文(混合加密)

三、核心身份与说明

1.核心身份

标准 :GM/T 0003、GB/T 32918

类型 :非对称加密(公钥 + 私钥)

哈希 :必须搭配 SM3

安全 :SM2 256 位 ≈ RSA 3072

地位:国密必用、金融 / 政务 / 等保强制标准

2.核心说明

密钥格式

私钥:32 字节

公钥:64 字节(非压缩)/33 字节(压缩)

签名规则

算法:SM3withSM2

签名长度:64 字节

加密规则

密文结构:C1 (65 字节) + C3 (32 字节) + C2 (密文)

限制:只能加密 < 32 字节数据,长数据用 SM2 加密 SM4 密钥 + SM4 加密数据

四、SM2 vs RSA / 国际 ECC (ECDSA) 区别

1.对比 RSA

RSA:大数分解难题,密钥长、加解密慢、量子安全性差;SM2 密钥短、性能高、抗量子更强、国产合规

2.对比 ECDSA (国际 ECC)

ECDSA:曲线自由定义、无用户 ID;SM2 固定国密曲线、签名带用户 ID 预处理、强制 SM3 哈希、KDF 密钥派生规则国标化,安全性更强、规范统一

五、应用场景

金融:网银签名、支付验签、电子票据

政务:国密 SSL 证书、电子签章、公文签名

物联网 / 嵌入式:智能卡、设备密钥(小密钥省存储)

系统改造:等保合规项目,替换原有 RSA 证书、接口验签

六、代码实战

1.引入依赖

java 复制代码
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.78.5</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk18on</artifactId>
    <version>1.78.5</version>
</dependency>

2.SM2Util工具类

java 复制代码
public class SM2Util {

    // 注册BouncyCastle提供者
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    // SM2标准曲线参数 sm2p256v1
    private static final X9ECParameters SM2_EC_PARAMS = GMNamedCurves.getByName("sm2p256v1");
    private static final ECDomainParameters SM2_DOMAIN_PARAMS = new ECDomainParameters(
            SM2_EC_PARAMS.getCurve(),
            SM2_EC_PARAMS.getG(),
            SM2_EC_PARAMS.getN(),
            SM2_EC_PARAMS.getH()
    );

    // 1. 生成 SM2 密钥对(公钥+私钥)
    public static AsymmetricCipherKeyPair generateKeyPair() {
        ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(
                SM2_DOMAIN_PARAMS, new SecureRandom()
        );
        ECKeyPairGenerator generator = new ECKeyPairGenerator();
        generator.init(keyGenParams);
        return generator.generateKeyPair();
    }

    // 2. SM2 签名(私钥签名)
    public static byte[] sign(byte[] privateKeyBytes, byte[] data) throws Exception {
        ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) PrivateKeyFactory.createKey(privateKeyBytes);
        SM2Signer signer = new SM2Signer();
        signer.init(true, privateKey);
        signer.update(data, 0, data.length);
        return signer.generateSignature();
    }

    // 3. SM2 验签(公钥验签)
    public static boolean verify(byte[] publicKeyBytes, byte[] data, byte[] signature) throws Exception {
        ECPublicKeyParameters publicKey = (ECPublicKeyParameters) PublicKeyFactory.createKey(publicKeyBytes);
        SM2Signer signer = new SM2Signer();
        signer.init(false, publicKey);
        signer.update(data, 0, data.length);
        return signer.verifySignature(signature);
    }

    // 4. SM2 加密(公钥加密)
    public static byte[] encrypt(byte[] publicKeyBytes, byte[] data) throws Exception {
        ECPublicKeyParameters publicKey = (ECPublicKeyParameters) PublicKeyFactory.createKey(publicKeyBytes);
        SM2Engine engine = new SM2Engine(new SM3Digest());
        engine.init(true, publicKey);
        return engine.processBlock(data, 0, data.length);
    }

    // 5. SM2 解密(私钥解密)
    public static byte[] decrypt(byte[] privateKeyBytes, byte[] encryptedData) throws Exception {
        ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) PrivateKeyFactory.createKey(privateKeyBytes);
        SM2Engine engine = new SM2Engine(new SM3Digest());
        engine.init(false, privateKey);
        return engine.processBlock(encryptedData, 0, encryptedData.length);
    }
}

3.签名证书制作

java 复制代码
 public static String generateSelfSignedCertificate(KeyPair keyPair, String commonName, boolean pemFormat) throws Exception {
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        X500Name issuer = new X500Name("CN=" + commonName + ",O=AniCert,C=CN");
        X500Name subject = new X500Name("CN=" + commonName + ",O=AniCert,C=CN");

        BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());

        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calendar.set(2026, Calendar.MAY, 1, 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date notBefore = calendar.getTime();

        Calendar calendar1 = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calendar1.set(2046, Calendar.MAY, 1, 0, 0, 0);
        calendar1.set(Calendar.MILLISECOND, 0);
        Date notAfter = calendar1.getTime();

        SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());

        X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
                issuer,
                serialNumber,
                notBefore,
                notAfter,
                subject,
                publicKeyInfo
        );

        //证书签名算法
        ContentSigner signer = new JcaContentSignerBuilder("SM3withSM2")
                .setProvider(bcProvider)
                .build(privateKey);

        org.bouncycastle.cert.X509CertificateHolder certHolder = certBuilder.build(signer);

        //生成证书
        java.security.cert.X509Certificate certificate = new JcaX509CertificateConverter()
                .setProvider(bcProvider)
                .getCertificate(certHolder);

        byte[] certBytes = certificate.getEncoded();

        if (pemFormat) {
            String base64Cert = Base64.encode(certBytes);
            return "-----BEGIN CERTIFICATE-----\n" +
                    formatBase64(base64Cert) +
                    "\n-----END CERTIFICATE-----";
        } else {
            return Base64.encode(certBytes);
        }
    }

4.证书加解密验证

java 复制代码
 // 解密需要 32位
        BCECPrivateKey privateKeyObj = SM2KeyUtil.decodeWithPKCS8(privateKey);
        String privateKeyRaw = SM2KeyUtil.encodeStrWithBigInteger(privateKeyObj);

        StaticLog.info("原始D值私钥:{}", privateKeyRaw);

        String certDerBase64 = SM2KeyUtil.generateSelfSignedCertificate(smKeyPair, "xxxx", false);

        StaticLog.info("私钥(PKCS8 Base64):{}", privateKey);
        StaticLog.info("证书(DER Base64):{}", certDerBase64);
      //  StaticLog.info("证书(PEM格式):\n{}", certPem);

        StaticLog.info("--------证书信息验证--------");
        org.bouncycastle.asn1.x509.Certificate cert = CertUtils.parseSM2Certificate(certDerBase64);

        String subjectDN = CertUtils.getCertInfo(cert, 21);
        String issuerDN = CertUtils.getCertInfo(cert, 20);
        String publicKeyFromCert = CertUtils.getCertInfo(cert, 30);
        String expiryDate = CertUtils.getCertInfo(cert, 32);

        StaticLog.info("证书主体:{}", subjectDN);
        StaticLog.info("证书颁发者:{}", issuerDN);
        StaticLog.info("证书公钥:{}", publicKeyFromCert);
        StaticLog.info("证书有效期截止:{}", expiryDate);

        String originalData = "我是需要加密的数据";
        StaticLog.info("原始数据:{}", originalData);

        ISoftCryptoService iSoftCryptoService = new SoftCryptoServiceImpl();
        String encryptedData = iSoftCryptoService.encrypt(publicKeyFromCert, originalData);
        StaticLog.info("加密后数据:{}", encryptedData);

        String decryptedData = iSoftCryptoService.decrypt(privateKeyRaw, encryptedData);
        StaticLog.info("解密后数据:{}", decryptedData);
		

七、总结

SM2 = 国密非对称,签名 + 加密,配 SM3,替代 RSA

SM2(非对称)签名、密钥加密

SM3(哈希)消息摘要(对标 SHA256)

SM4(对称 128 位)大数据加解密(对标 AES)

相关推荐
我登哥MVP1 小时前
Spring Boot 从“会用”到“精通”:静态资源原理
java·spring boot·后端·spring·tomcat·maven·intellij-idea
caibixyy1 小时前
Springboot + flowable6.8.0
spring boot·flowable6.8.0
JAVA面经实录9171 小时前
SpringBoot 全套完整版学习文档(零基础+实战+面试+源码)
java·spring boot·spring·架构
jasnet_u1 小时前
SpringCloud中Feign透传traceId及日志切面配置
java·spring cloud·feign·日志系统
nvd111 小时前
从 Spring 到 Quarkus:为什么依赖注入正在从“运行时”退回“编译期”?
java·后端·spring
孬甭_1 小时前
二叉树(Binary Tree)
数据结构·算法
JAVA面经实录9171 小时前
SpringCloud 完整体系学习文档
java·spring cloud
爱吃羊的老虎1 小时前
【JAVA】Java微服务—Spring Cloud 里用来做服务调用的工具OpenFeign
java·微服务·开源
开源推荐官1 小时前
2026 年主流优质 B2B2C 多商户商城系统推荐
java·架构·开源