Java 国密算法 SM2 加密 加签,SM3 摘要加密,SM4 加密 解密 工具类 (附完整代码)

目录

介绍

开始

引入 Bouncy Castle 依赖

SM2 算法

完整代码 (SM2Util.java)

测试调用

1. 生成公钥私钥

2. 加密解密

3. 加签验签

SM3 算法

1. 摘要加密

完整代码(SM3Util.java)

SM4 算法

1. 生成随机密钥

2. 加密解密

完整代码(SM4Util.java)

下载代码(Gitee代码参考)


介绍

针对 Bouncy Castle 做了封装工具类,用于实现国密算法中的 SM2、SM3、SM4。

国密算法工具封装包括:

  • 非对称加密和签名:SM2
  • 摘要签名算法:SM3
  • 对称加密:SM4

国密算法需要引入 Bouncy Castle 库的依赖。

开始

引入 Bouncy Castle 依赖

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

SM2 算法

非对称加密 SM2

包含:生成公钥私钥、加密、解密、加签、验签

完整代码 (SM2Util.java)

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.*;

/**
 * SM2Util 类用于生成、加密、解密、签名和验签 SM2 密钥对及数据。
 * 该类使用 BouncyCastle 提供的安全库来实现各种 SM2 操作。
 */
public class SM2Util {

  // 静态代码块,在类加载时自动添加 BouncyCastle 提供者
  static {
    Security.addProvider(new BouncyCastleProvider());
  }

  // 定义 SM2 曲线名称常量
  private static final String CURVE_NAME = "sm2p256v1";
  private static final SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;


  /**
   * 打印生成的 SM2 公钥和私钥,以十六进制格式显示
   */
  public static void printGenSM2Key() {
    // 获取 SM2 曲线参数
    X9ECParameters ecParameters = GMNamedCurves.getByName(CURVE_NAME);
    ECDomainParameters domainParameters = new ECDomainParameters(
            ecParameters.getCurve(),
            ecParameters.getG(),
            ecParameters.getN(),
            ecParameters.getH());

    // 使用 SM2 算法生成密钥对
    ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
    ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(domainParameters, new SecureRandom());
    keyPairGenerator.init(keyGenerationParameters);

    // 生成密钥对
    AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();
    ECPoint publicKeyPoint = ((org.bouncycastle.crypto.params.ECPublicKeyParameters) keyPair.getPublic()).getQ();
    ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();

    // 获取公钥和私钥的字节数组
    byte[] publicKeyBytes = publicKeyPoint.getEncoded(false);  // false表示未压缩格式
    byte[] privateKeyBytes = privateKey.getD().toByteArray();

    // 将字节数组转换为 Hex String
    String publicKeyHex = Hex.toHexString(publicKeyBytes);
    String privateKeyHex = Hex.toHexString(privateKeyBytes);

    // 输出公钥和私钥的 Hex String
    System.out.println("Public Key (Hex): " + publicKeyHex);
    System.out.println("Private Key (Hex): " + privateKeyHex);
  }

  /**
   * SM2 加密
   *
   * @param data 要加密的数据
   * @param publicKeyStr 公钥的字符串表示形式
   * @return 加密后的数据,以十六进制表示
   * @throws InvalidCipherTextException 加密失败
   */
  public static String encrypt(String data, String publicKeyStr) throws InvalidCipherTextException {
    byte[] dataBytes = data.getBytes();
    ECPublicKeyParameters publicKey = convertStringToPublicKey(publicKeyStr);
    // 使用 SM3 和 C1C3C2 模式
    SM2Engine engine = new SM2Engine(new SM3Digest(), mode);
    engine.init(true, new ParametersWithRandom(publicKey));
    byte[] bytes = engine.processBlock(dataBytes, 0, dataBytes.length);
    return Hex.toHexString(bytes);
  }

  /**
   * SM2 解密
   *
   * @param encryptedData 加密后的数据,以十六进制表示
   * @param privateKeyStr 私钥的字符串表示形式
   * @return 解密后的原始数据
   * @throws InvalidCipherTextException 解密失败
   */
  public static String decrypt(String encryptedData, String privateKeyStr) throws InvalidCipherTextException {
    byte[] dataBytes = Hex.decodeStrict(encryptedData);
    ECPrivateKeyParameters privateKey = convertStringToPrivateKey(privateKeyStr);
    // 使用 SM3 和 C1C3C2 模式
    SM2Engine engine = new SM2Engine(new SM3Digest(), mode);
    engine.init(false, privateKey);
    byte[] bytes = engine.processBlock(dataBytes, 0, dataBytes.length);
    return new String(bytes);
  }

  /**
   * 将字符串形式的公钥转换为 PublicKey 对象
   *
   * @param publicKeyStr 公钥的字符串表示形式
   * @return 转换后的 PublicKey 对象
   */
  public static ECPublicKeyParameters convertStringToPublicKey(String publicKeyStr) {
    // 获取 SM2 曲线参数
    X9ECParameters x9ECParameters = GMNamedCurves.getByName(CURVE_NAME);
    ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH());
    ECCurve curve = ecDomainParameters.getCurve();
    ECPoint ecPoint = curve.decodePoint(Hex.decode(publicKeyStr));
    return new ECPublicKeyParameters(ecPoint, ecDomainParameters);

  }

  /**
   * 将字符串形式的私钥转换为 PrivateKey 对象
   *
   * @param privateKeyStr 私钥的字符串表示形式
   * @return 转换后的 PrivateKey 对象
   */
  public static ECPrivateKeyParameters convertStringToPrivateKey(String privateKeyStr) {
    // 添加 BouncyCastle 作为安全提供者
    Security.addProvider(new BouncyCastleProvider());
    // 将 Hex 私钥转换为 BigInteger
    BigInteger privateKeyD = new BigInteger(1, Hex.decode(privateKeyStr));
    // 获取 SM2 曲线参数
    X9ECParameters x9ECParameters = GMNamedCurves.getByName(CURVE_NAME);
    ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH());
    return new ECPrivateKeyParameters(privateKeyD, ecDomainParameters);
  }

  /**
   * SM2 签名
   *
   * @param data 要签名的数据
   * @param privateKeyStr 私钥的字符串表示形式
   * @return 签名后的数据,以十六进制表示
   * @throws CryptoException 签名失败
   */
  public static String sign(String data, String privateKeyStr) throws CryptoException {
    byte[] dataBytes = data.getBytes();
    ECPrivateKeyParameters privateKey = convertStringToPrivateKey(privateKeyStr);

    // 创建 SM2Signer 对象
    SM2Signer signer = new SM2Signer();
    signer.init(true, new ParametersWithRandom(privateKey));
    // 更新签名器的数据
    signer.update(dataBytes, 0, dataBytes.length);
    // 生成签名
    byte[] bytes = signer.generateSignature();
    return Hex.toHexString(bytes);
  }

  /**
   * SM2 验签
   *
   * @param data 原始数据
   * @param signature 签名数据,以十六进制表示
   * @param publicKeyStr 公钥的字符串表示形式
   * @return 验签结果,true 表示签名有效,false 表示无效
   */
  public static boolean verify(String data, String signature, String publicKeyStr) {
    byte[] dataBytes = data.getBytes();
    byte[] signatureBytes = Hex.decodeStrict(signature);
    ECPublicKeyParameters publicKey = convertStringToPublicKey(publicKeyStr);

    // 创建 SM2Signer 对象
    SM2Signer signer = new SM2Signer();
    signer.init(false, publicKey);
    // 更新签名器的数据
    signer.update(dataBytes, 0, dataBytes.length);
    // 验证签名
    return signer.verifySignature(signatureBytes);
  }

  public static void main(String[] args) throws Exception {
    // 打印生成的 SM2 密钥对
    printGenSM2Key();

    String data = "Hello SM";
    String privateKey = "009b2efe43ebc2e9dc9c3c3ef69d382aac9dcbddebf5bcc9a96a9a3fc3f9a09cf3";
    String publicKey = "04173d57a1df1ea8c0d22c8fbb0ea8abdc88b3c994851cbad1c28cfe06ebdb54ecbfbb38a984d41f5d2218b2381a2e53cc7d13dd0d10a315c76f331aa57d5cda01";

    String encryptData = encrypt(data, publicKey);
    System.out.println("SM2 加密" + encryptData);
    String decryptData = decrypt(encryptData, privateKey);
    System.out.println("SM2 解密" + decryptData);

    String sign = sign(data, privateKey);
    System.out.println("SM2 加签" + sign);
    System.out.println("SM2 验签" + verify(data, sign, publicKey));
  }

}

测试调用

public static void main(String[] args) throws Exception {
  // 打印生成的 SM2 密钥对
  printGenSM2Key();

  String data = "Hello SM";
  String privateKey = "009b2efe43ebc2e9dc9c3c3ef69d382aac9dcbddebf5bcc9a96a9a3fc3f9a09cf3";
  String publicKey = "04173d57a1df1ea8c0d22c8fbb0ea8abdc88b3c994851cbad1c28cfe06ebdb54ecbfbb38a984d41f5d2218b2381a2e53cc7d13dd0d10a315c76f331aa57d5cda01";

  String encryptData = encrypt(data, publicKey);
  System.out.println("SM2 加密" + encryptData);
  String decryptData = decrypt(encryptData, privateKey);
  System.out.println("SM2 解密" + decryptData);

  String sign = sign(data, privateKey);
  System.out.println("SM2 加签" + sign);
  System.out.println("SM2 验签" + verify(data, sign, publicKey));
}

1. 生成公钥私钥

// 打印生成的 SM2 密钥对
printGenSM2Key();

2. 加密解密

String data = "Hello SM";
String privateKey = "009b2efe43ebc2e9dc9c3c3ef69d382aac9dcbddebf5bcc9a96a9a3fc3f9a09cf3";
String publicKey = "04173d57a1df1ea8c0d22c8fbb0ea8abdc88b3c994851cbad1c28cfe06ebdb54ecbfbb38a984d41f5d2218b2381a2e53cc7d13dd0d10a315c76f331aa57d5cda01";

String encryptData = encrypt(data, publicKey);
System.out.println("SM2 加密" + encryptData);
String decryptData = decrypt(encryptData, privateKey);
System.out.println("SM2 解密" + decryptData);

3. 加签验签

String sign = sign(data, privateKey);
System.out.println("SM2 加签" + sign);
System.out.println("SM2 验签" + verify(data, sign, publicKey));

SM3 算法

摘要加密 SM3

1. 摘要加密

String digest = digest("Hello SM");
System.out.println("SM3 摘要加密" + digest);

完整代码(SM3Util.java)

import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.util.encoders.Hex;

/**
 * SM3Util 类用于执行 SM3 摘要加密操作。
 * SM3 是中国国家密码管理局定义的一种哈希算法,类似于 SHA-256。
 */
public class SM3Util {

  /**
   * 生成数据的 SM3 摘要
   *
   * @param data 数据
   * @return 生成的摘要,以十六进制表示
   */
  public static String digest(String data) {
    byte[] bytes = data.getBytes();
    SM3Digest digest = new SM3Digest();
    digest.update(bytes, 0, bytes.length);
    byte[] hash = new byte[digest.getDigestSize()];
    digest.doFinal(hash, 0);
    return Hex.toHexString(hash);
  }

  public static void main(String[] args) {
    String digest = digest("Hello SM");
    System.out.println("SM3 摘要加密" + digest);
  }

}

SM4 算法

对称加密 SM4

包含 生成随机密钥、加密、解密

1. 生成随机密钥

String key = generateKey();
System.out.println("SM4 随机密钥" + key);

控制台输出:

SM4 随机密钥86ac19e32caff89b4c0555751347c308

2. 加密解密

String key = generateKey();
System.out.println("SM4 随机密钥" + key);

String data = "Hello SM";
String encrypt = encrypt(data, key);
System.out.println("SM4 加密" + encrypt);
String decryptData = decrypt(encrypt, key);
System.out.println("SM4 解密" + decryptData);

控制台输出:

SM4 随机密钥27976d9e26e6c84fbdf04ac54b312287
SM4 加密cb822754fb3cbaa4a7a856caa77add09
SM4 解密Hello SM

完整代码(SM4Util.java)

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

public class SM4Util {

  static {
    Security.addProvider(new BouncyCastleProvider());
  }

  private static final int BLOCK_SIZE = 16;

  /**
   * 生成一个随机的 SM4 密钥
   *
   * @return 生成长度 32 的密钥,返回的密钥以十六进制字符串形式表示
   */
  public static String generateKey() {
    byte[] key = new byte[BLOCK_SIZE];
    SecureRandom random = new SecureRandom();
    random.nextBytes(key);
    return Hex.toHexString(key);
  }

  /**
   * SM4加密
   *
   * @param data 需要加密的数据
   * @param key  密钥 32 位十六进制字符串表示的(16 字节)
   *
   * @return 加密后的数据
   * @throws InvalidCipherTextException 加密失败时抛出异常
   */
  public static String encrypt(String data, String key) throws InvalidCipherTextException {
    return encrypt(data, key, null);
  }

  /**
   * SM4解密
   *
   * @param encryptedData 加密的数据
   * @param key  密钥 32 位十六进制字符串表示的(16 字节)

   * @return 解密后的数据
   * @throws InvalidCipherTextException 解密失败时抛出异常
   */
  public static String decrypt(String encryptedData, String key) throws InvalidCipherTextException {
    return decrypt(encryptedData, key, null);
  }

  /**
   * SM4加密
   *
   * @param data 需要加密的数据
   * @param key 密钥 32 位十六进制字符串表示的(16 字节)
   * @param iv  初始化向量 32 位十六进制字符串表示的(16 字节)或为空,若为空则使用全零 IV
   *
   * @return 加密后的数据
   * @throws InvalidCipherTextException 加密失败时抛出异常
   */
  public static String encrypt(String data, String key, String iv) throws InvalidCipherTextException {
    byte[] keyBytes = Hex.decodeStrict(key);
    if (keyBytes.length != BLOCK_SIZE) {
      throw new IllegalArgumentException("Key 长度必须为 32");
    }

    byte[] ivBytes;
    if (iv == null || iv.isEmpty()) {
      ivBytes = new byte[BLOCK_SIZE]; // 默认使用全零 IV
    } else {
      ivBytes = Hex.decodeStrict(iv);
      if (ivBytes.length != BLOCK_SIZE) {
        throw new IllegalArgumentException("IV 如果不为空,长度必须为 32");
      }
    }

    SM4Engine sm4Engine = new SM4Engine();
    CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(sm4Engine);
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcBlockCipher);

    CipherParameters params = new ParametersWithIV(new KeyParameter(keyBytes), ivBytes);
    cipher.init(true, params);
    byte[] dataBytes = data.getBytes();
    byte[] process = process(cipher, dataBytes);
    return Hex.toHexString(process);
  }

  /**
   * SM4解密
   *
   * @param encryptedData 加密的数据
   * @param key 密钥 32 位十六进制字符串表示的(16 字节)
   * @param iv  初始化向量 32 位十六进制字符串表示的(16 字节)或为空,若为空则使用全零 IV

   * @return 解密后的数据
   * @throws InvalidCipherTextException 加密失败时抛出异常
   */
  public static String decrypt(String encryptedData, String key, String iv) throws InvalidCipherTextException {
    byte[] keyBytes = Hex.decodeStrict(key);
    if (keyBytes.length != BLOCK_SIZE) {
      throw new IllegalArgumentException("Key 长度必须为 32");
    }

    byte[] ivBytes;
    if (iv == null || iv.isEmpty()) {
      ivBytes = new byte[BLOCK_SIZE]; // 默认使用全零 IV
    } else {
      ivBytes = Hex.decodeStrict(iv);
      if (ivBytes.length != BLOCK_SIZE) {
        throw new IllegalArgumentException("IV 如果不为空,长度必须为 32");
      }
    }

    SM4Engine sm4Engine = new SM4Engine();
    CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(sm4Engine);
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbcBlockCipher);

    CipherParameters params = new ParametersWithIV(new KeyParameter(keyBytes), ivBytes);
    cipher.init(false, params);
    byte[] encryptedDataBytes = Hex.decodeStrict(encryptedData);
    byte[] process = process(cipher, encryptedDataBytes);
    return new String(process);
  }

  /**
   * 处理加密或解密的数据
   *
   * @param cipher 加密器或解密器
   * @param data 数据
   *
   * @return 处理后的数据
   * @throws InvalidCipherTextException 处理失败时抛出异常
   */
  private static byte[] process(PaddedBufferedBlockCipher cipher, byte[] data) throws InvalidCipherTextException {
    byte[] outputBuffer = new byte[cipher.getOutputSize(data.length)];
    int length = cipher.processBytes(data, 0, data.length, outputBuffer, 0);
    length += cipher.doFinal(outputBuffer, length);
    return Arrays.copyOf(outputBuffer, length);
  }



  public static void main(String[] args) throws Exception {
    String key = generateKey();
    System.out.println("SM4 随机密钥" + key);

    String data = "Hello SM";
    String encrypt = encrypt(data, key);
    System.out.println("SM4 加密" + encrypt);
    String decryptData = decrypt(encrypt, key);
    System.out.println("SM4 解密" + decryptData);
  }

}

下载代码(Gitee代码参考)

https://gitee.com/fhsy80/sm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值