SM2算法

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

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

 

依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.60</version>
</dependency>
 

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
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.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;

public class SM2Utils {

    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
    private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());

    protected static BigInteger n = new BigInteger(
            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
    protected static BigInteger p = new BigInteger(
            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
    protected static BigInteger a = new BigInteger(
            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
    protected static BigInteger b = new BigInteger(
            "28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
    protected static BigInteger gx = new BigInteger(
            "32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
    protected static BigInteger gy = new BigInteger(
            "BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);
    protected static ECDomainParameters ecc_bc_spec;
    protected static int w = (int) Math.ceil(n.bitLength() * 1.0 / 2) - 1;
    protected static BigInteger _2w = new BigInteger("2").pow(w);
    protected static final int DIGEST_LENGTH = 32;

    private static SecureRandom random = new SecureRandom();
    protected static ECCurve.Fp curve;
    protected static ECPoint G;
    protected static boolean debug = false;

    static  {
        curve = new ECCurve.Fp(p, // q
                a, // a
                b); // b
        G = curve.createPoint(gx, gy);
        ecc_bc_spec = new ECDomainParameters(curve, G, n);
    }
    /**
     * 生成公私钥对
     * @param compressedPubKey  是否压缩公钥
     * @return
     */
    public static String[] genKeyPair(boolean compressedPubKey) throws NoSuchAlgorithmException,InvalidAlgorithmParameterException{
        // 获取SM2 椭圆曲线推荐参数
        X9ECParameters ecParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造EC 算法参数
        ECNamedCurveParameterSpec sm2Spec = new ECNamedCurveParameterSpec(
                // 设置SM2 算法的 OID
                GMObjectIdentifiers.sm2p256v1.toString()
                // 设置曲线方程
                , ecParameters.getCurve()
                // 椭圆曲线G点
                , ecParameters.getG()
                // 大整数N
                , ecParameters.getN());
        // 创建 密钥对生成器
        KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
        // 使用SM2的算法区域初始化密钥生成器
        gen.initialize(sm2Spec, new SecureRandom());
        // 获取密钥对
        KeyPair keyPair = gen.generateKeyPair();
        byte buffer[] = ((BCECPublicKey)keyPair.getPublic()).getQ().getEncoded(false);
        String publicKeyStr = Base64.encodeBase64String(buffer);
        String privateKeyStr = ((BCECPrivateKey)keyPair.getPrivate()).getD().toString(16);
        return new String[]{publicKeyStr, privateKeyStr};
    }

    /**
     * 私钥签名
     * @param privateKeyStr    私钥
     * @param plainText       待签名内容
     * @return
     */
    public static String sign(String privateKeyStr, String plainText) throws SignatureException,NoSuchAlgorithmException,InvalidKeyException{
        // 生成SM2sign with sm3 签名验签算法实例
        Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), new BouncyCastleProvider());
        BCECPrivateKey privateKey = getPrivatekeyFromD(new BigInteger(privateKeyStr,16));
        // 签名需要使用私钥,使用私钥 初始化签名实例
        signature.initSign(privateKey);
        // 签名原文
        // 写入签名原文到算法中
        signature.update(plainText.getBytes(StandardCharsets.UTF_8));
        // 计算签名值
        byte[] signatureValue = signature.sign();
        return Base64.encodeBase64String(signatureValue);
    }

    /**
     * 验证签名
     * @param publicKeyStr     公钥
     * @param plainText       待签名内容
     * @param sign          签名值
     * @return
     */
    public static boolean verify(String publicKeyStr, String plainText, String sign) throws SignatureException, NoSuchAlgorithmException,InvalidKeyException{
        Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), new BouncyCastleProvider());
        // 签名需要使用公钥,使用公钥 初始化签名实例
        byte buffer[] = Base64.decodeBase64(publicKeyStr);
        ECPoint ecPoint = curve.decodePoint(buffer);
        signature.initVerify(getPublickeyFromXY(ecPoint.getXCoord().toBigInteger(), ecPoint.getYCoord().toBigInteger()));
        // 写入待验签的签名原文到算法中
        signature.update(plainText.getBytes(StandardCharsets.UTF_8));
        // 验签
        return signature.verify(Base64.decodeBase64(sign));
    }

    public static BCECPrivateKey getPrivatekeyFromD(BigInteger d){
        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecParameterSpec);
        return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    public static BCECPublicKey getPublickeyFromXY(BigInteger x, BigInteger y){
        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecParameterSpec);
        return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    public static void main(String[] args) throws Exception{
        String[] keyPairArray = genKeyPair(true);
        String publicKeyStr = keyPairArray[0];
        String privateKeyStr = keyPairArray[1];
        String plainText = "HelloWorld";
        String signStr = sign(privateKeyStr, plainText);
        boolean verify = verify(publicKeyStr, plainText, signStr);
        System.out.println(verify);
    }
————————————————
版权声明:本文为优快云博主「xiaozhang51」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/xiaozhang51/article/details/102894439

<think>好的,我现在需要帮助用户了解SM2算法的实现、教程或代码示例。首先,我需要整理已有的引用信息,看看有哪些内容可以利用。引用[1]提到Python实现,引用[2]是单片机上的C语言示例,引用[3]和[5]是Java的实现,引用[4]是PHP的。用户可能对不同的编程语言感兴趣,所以应该涵盖多种实现示例。 接下来,用户的问题是关于SM2的实现、教程和代码示例,所以需要分步骤讲解。首先应该介绍SM2的基本原理,比如它是基于椭圆曲线密码学的非对称算法,然后讲解密钥生成、加密解密、签名验证的流程。之后,再提供不同语言的代码示例,这样用户可以对照学习。 需要注意的是,每个引用中的代码可能只是部分实现,比如引用[2]提到需要SM3哈希函数,但示例中可能没有完整代码,需要提醒用户注意。另外,引用[3]和[5]都是Java的实现,可能需要整合这两个的信息,说明Java的实现步骤和结果。 用户可能还关心实际应用中的注意事项,比如性能优化、安全处理等,这些在引用[2]和[4]中都有提到,需要指出。此外,各个引用中的代码示例可能只是核心逻辑,实际应用需要更完整的处理,比如错误处理、标准遵循等,这也是需要强调的地方。 最后,生成相关问题时要覆盖算法原理、实现细节、应用场景和优化等方面,帮助用户进一步深入学习。要确保问题具有针对性,能够引导用户探索更多相关内容。</think>### SM2算法实现与代码示例 SM2是国家密码管理局发布的椭圆曲线公钥密码算法,基于$E(F_p)$域上的椭圆曲线方程$y^2 = x^3 + ax + b$,广泛应用于数字签名、密钥交换和加密[^3]。其核心流程包含**密钥生成**、**加密/解密**和**签名/验证**三部分。 --- #### 一、SM2算法核心流程 1. **密钥生成** - 生成随机数$d \in [1, n-1]$作为私钥 - 计算公钥$P = d \cdot G$,其中$G$为椭圆曲线基点 $$ P = (x_P, y_P) $$ 2. **加密流程** - 生成临时密钥对$(k, Q)$,$Q = k \cdot G$ - 计算密钥派生参数$t = KDF(x_Q || y_Q, \text{len}(明文))$ - 输出密文$C = Q || (明文 \oplus t) || \text{SM3}(明文)$ 3. **签名流程** - 计算哈希值$e = \text{SM3}(消息)$ - 生成随机数$k \in [1, n-1]$ - 计算$(r, s)$满足$r = (e + x_{kG}) \mod n$和$s = (1 + d)^{-1}(k - rd) \mod n$ --- #### 二、多语言实现示例 ##### 1. Python实现(面向对象) ```python # 引用[1]核心代码改编 from ecpy.curves import Curve class SM2: def __init__(self): self.cv = Curve.get_curve('SM2') self.G = self.cv.generator self.n = self.cv.order def keygen(self): priv_key = self.cv.order.random() pub_key = priv_key * self.G return priv_key, pub_key def sign(self, msg, priv_key): # 实现签名逻辑 pass ``` ##### 2. Java密钥生成(引用[5]) ```java import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.ECKeyGenerationParameters; public class SM2KeyGen { public static void main(String[] args) { ECKeyPairGenerator gen = new ECKeyPairGenerator(); gen.init(new ECKeyGenerationParameters(SM2Util.DOMAIN_PARAMS, new SecureRandom())); AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); // 公钥: keyPair.getPublic(), 私钥: keyPair.getPrivate() } } ``` ##### 3. 单片机C实现要点(引用[2]) ```c // 椭圆曲线参数定义 typedef struct { uint32_t p[8]; // 素数域 uint32_t a[8]; // 曲线系数a uint32_t b[8]; // 曲线系数b ECPoint G; // 基点 } ECCurve; // 大数比较示例 int BigNumCmp(uint32_t *a, uint32_t *b) { for(int i=7; i>=0; i--) { if(a[i] > b[i]) return 1; if(a[i] < b[i]) return -1; } return 0; } ``` --- #### 三、开发注意事项 1. **安全随机数**:密钥生成必须使用密码学安全随机数生成器(如Java的`SecureRandom`) 2. **常量时间实现**:防止侧信道攻击,避免分支泄露密钥信息[^4] 3. **参数标准化**:使用国标GB/T 32918.5-2017定义参数: $$ p=FFFFFFFE\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ FFFFFFFF\ 00000000\ FFFFFFFF\ FFFFFFFF $$ 4. **性能优化**:单片机实现需采用滑动窗口法优化点乘运算[^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值