java 实现SM2(国密2)加解密以及加签处理

前言

国家密码管理局于2010年12月17日发布了SM2算法,并要求现有的基于RSA算法的电子认证系统、密钥管理系统、应用系统进升级改造,使用支持国密SM2算法的证书。

基于ECC椭圆曲线算法的SM2算法,则普遍采用256位密钥长度,它的单位安全强度相对较高,在工程应用中比较难以实现,破译或求解难度基本上是指数级的。因此,SM2算法可以用较少的计算能力提供比RSA算法更高的安全强度,而所需的密钥长度却远比RSA算法低。

同时,现在好多软件公司对于软件的安全性要求越来越高,大部分软件出于对用户隐私的保护都会使用加密算法对用户信息相关接口进行后端加密,前端解密处理。这里就不多说废话了 直接上干活

引入jar包

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

生成公钥和私钥

    /**
     * 生成SM2密钥对
     * @return org.bouncycastle.crypto.AsymmetricCipherKeyPair
     * @author fyh
     **/
    public static AsymmetricCipherKeyPair generateKeyPair() {
        ECKeyPairGenerator generator = new ECKeyPairGenerator();
        generator.init(new ECKeyGenerationParameters(DOMAIN_PARAMS, new SecureRandom()));
        return generator.generateKeyPair();
    }

    /**
     * 获取公钥字符串
     */
    /**
     * 获取公钥字符串。
     *
     * @param keyPair 密钥对
     * @return 公钥字符串
     */
    public static String getPublicKeyString(AsymmetricCipherKeyPair keyPair) {
        ECPublicKeyParameters ecPublicKeyParameters = (ECPublicKeyParameters) keyPair.getPublic();
        return Hex.toHexString(ecPublicKeyParameters.getQ().getEncoded(false));
    }

    /**
     * 获取私钥字符串。
     *
     * @param keyPair 密钥对
     * @return 私钥字符串
     */
    public static String getPrivateKeyString(AsymmetricCipherKeyPair keyPair) {
        ECPrivateKeyParameters ecPrivateKeyParameters = (ECPrivateKeyParameters) keyPair.getPrivate();
        return Hex.toHexString(ecPrivateKeyParameters.getD().toByteArray());
    }

//直接使用main方法生成公钥和私钥
  public static void main(String[] args) {

        AsymmetricCipherKeyPair keyPair = Sm2Util.generateKeyPair();
        String publicKey = Sm2Util.getPublicKeyString(keyPair);
        String privateKey = Sm2Util.getPrivateKeyString(keyPair);
        System.out.println("#blade配置 \n" +
                "blade:\n" +
                "  auth:\n" +
                "    public-key: " + publicKey + "\n" +
                "    private-key: " + privateKey);
    }

加密、解密以及加签和验签工具类实现

   private static final ECDomainParameters DOMAIN_PARAMS;

    static {
        Security.addProvider(new BouncyCastleProvider());
        X9ECParameters spec = GMNamedCurves.getByName("sm2p256v1");
        DOMAIN_PARAMS = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH());
    }
 /**
     * 获取私钥字符串
     * @param data
     * @return
     */
    public static ECPrivateKeyParameters stringToPrivateKey(String data) {
        return new ECPrivateKeyParameters(new BigInteger(Hex.decode(data)), DOMAIN_PARAMS);
    }

    /**
     * 从字符串恢复公钥
     * @param data
     * @return
     */
    public static ECPublicKeyParameters stringToPublicKey(String data) {
        return new ECPublicKeyParameters(DOMAIN_PARAMS.getCurve().decodePoint(Hex.decode(data)), DOMAIN_PARAMS);
    }


    public static byte[] encrypt3(String input, String publicKey) {
        return encrypt3(input, stringToPublicKey(publicKey));
    }

    public static byte[] encrypt3(String input, ECPublicKeyParameters publicKey) {
        try {
            SM2Engine engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);
            engine.init(true, new ParametersWithRandom(publicKey, new SecureRandom()));
            byte[] inputBytes = input.getBytes();
            return engine.processBlock(inputBytes, 0, inputBytes.length);
        } catch (Exception e) {
            return null;
        }
    }

    public static String decrypt3(byte[] encrypted, String privateKey) {
        return decrypt3(encrypted, stringToPrivateKey(privateKey));
    }

    public static String decrypt3(byte[] encrypted, ECPrivateKeyParameters privateKey) {
        try {
            SM2Engine engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);
            engine.init(false, privateKey);
            byte[] decryptedBytes = engine.processBlock(encrypted, 0, encrypted.length);
            return new String(decryptedBytes);
        } catch (Exception e) {
            return null;
        }
    }



    /**
     * 生成签名
     * @param input 数据
     @param privateKey 私钥
      * @return byte[]
     * @date 2025/7/31 15:35
     * @author fyh
     **/
    public static byte[] sign2(String input, String privateKey) {
        return sign2(input, stringToPrivateKey(privateKey));
    }

    /**
     * 使用SM2算法进行数据签名。
     *
     * @param input      需要签名的数据
     * @param privateKey 私钥
     * @return 签名
     */
    public static byte[] sign2(String input, ECPrivateKeyParameters privateKey) {
        try {
            SM2Signer signer = new SM2Signer();
            signer.init(true, new ParametersWithRandom(privateKey, new SecureRandom()));
            byte[] inputBytes = input.getBytes();
            signer.update(inputBytes, 0, inputBytes.length);
            return signer.generateSignature();
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 验证签名
     * @param input 数据
     @param signature 签名
     @param publicKey 公钥
      * @return boolean
     * @date 2025/7/31 15:36
     * @author fyh
     **/
    public static boolean verify2(String input, byte[] signature, String publicKey) {
        return verify2(input, signature, stringToPublicKey(publicKey));
    }

    /**
     * 使用SM2算法验证签名。
     *
     * @param input     数据
     * @param signature 签名
     * @param publicKey 公钥
     * @return 是否验证成功
     */
    public static boolean verify2(String input, byte[] signature, ECPublicKeyParameters publicKey) {
        try {
            SM2Signer signer = new SM2Signer();
            signer.init(false, publicKey);
            byte[] inputBytes = input.getBytes();
            signer.update(inputBytes, 0, inputBytes.length);
            return signer.verifySignature(signature);
        } catch (Exception e) {
            return false;
        }
    }

     //使用main测试加解密是否正常
    public static void main(String[] args) {
        String jobNumber ="90006790";
        byte[] encryptedData = Sm2Util.encrypt3(jobNumber, publicKey);
        //将数组转换为字符串
        String hexString = Hex.toHexString(encryptedData);
        byte[] bytes = Sm2Util.sign2(jobNumber, privateKey);
        System.out.println("前段加密数据:"+hexString);
        System.out.println("前段加签:"+bytes);
        String s1 = Sm2Util.decrypt3(encryptedData, privateKey);
        boolean b = Sm2Util.verify2(s1, bytes, publicKey);
        System.out.println("解密数据:"+s1);
        System.out.println("验签:"+b);
    }

具体使用方式

我们可以在需要加密或者解密的地方调用公共方法然后实现对字符串的加密或者解密操作 就比如一个前后端分离的项目,在登录的时候 需要前端实现加密,加签操作后端实现解密,验签操作的过程

protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
        Map<String, String> parameters = tokenRequest.getRequestParameters();
        String username = MapUtils.getString(parameters, "username");
        String password = MapUtils.getString(parameters, "password");
        //SM2Key.PRIVATE_KEY_ANDROID 是解密的私钥
        username = SM2Util.decrypt3(HexUtil.decodeHex(username), SM2Key.PRIVATE_KEY_ANDROID);
        password = SM2Util.decrypt3(HexUtil.decodeHex(password), SM2Key.PRIVATE_KEY_ANDROID);
        String jsonStr = "{\"username\":\"{}\",\"password\":\"{}\"}";
        jsonStr = StrUtil.format(jsonStr,
                username,
                password
        );
        //HexUtil.decodeHex 方法是将字符串转换为二进制,SM2Key.PUBLIC_KEY_SIGN 是验签的公钥
        boolean b = SM2Util.verify2(jsonStr, HexUtil.decodeHex(parameters.get("dataS")),SM2Key.PUBLIC_KEY_SIGN);
        if (!b) {
            //验签失败
            throw new DataFailException(ResultEnum.USER_WECHAT_LOGIN_FAIL.getCode() + "", "请检查数据完整性");
        }
        //此处省去逻辑代码实现部分
}

备注:上述代码中的SM2Key类是专门用户存放公钥和私钥的公共类,可以将生成的公钥和私钥,验签的数据放在SM2Key中方便后期使用时直接调用即可

Java中的SM2(Secure Multiparty Computation 2)是一种中国国家密码管理局推荐的非对称密算法,类似于国际上常用的RSA算法。如果你想要在Java中编写一个SM2加解密工具类,首先你需要添相应的密库支持,如BCrypt-JCE(Bouncy Castle Crypto),它提供了一套丰富的密码学API。 一个基本的SM2工具类可能会包含以下几个部分: 1. 导入必要的库和类: ```java import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.spec.SecretKeySpec; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.Security; ``` 2. 初始化BouncyCastle安全提供者(如果尚未初始化): ```java Security.addProvider(new BouncyCastleProvider()); ``` 3. 创建密钥对: ```java KeyPairGenerator keyGen = KeyPairGenerator.getInstance("SM2"); keyGen.initialize(256, new SecureRandom()); KeyPair keyPair = keyGen.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); ``` 4. 密函数(公钥密): ```java byte[] ciphertext = SM2Util.encrypt(publicKey, plaintext); ``` 5. 解密函数(私钥解密): ```java byte[] decryptedText = SM2Util.decrypt(privateKey, ciphertext); ``` 其中,`SM2Util`是一个自定义的工具类,包含SM2相关的加解密方法。实际操作中,你需要根据BouncyCastle的API文档编写具体的密和解密实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值