JAVA实现国密算法SM2/SM3/SM4签名与验签(基于 BouncyCastle)
密码学算法分类指南[
国密算法全解析
Java实现国密算法 SM2 /SM3 /SM4加解密(基于 BouncyCastle)
- 环境要求:
- JDK 17
- Maven 3
- BouncyCastle(1.78 )
先上个结论, SM2 是三种算法中唯一真正适用于数字签名的算法。
一、加解密与签名验签的区别
贴代码之前,先说一下加解密与签名验签的区别:
- 加解密:用于保密通信,使用公钥加密,私钥解密。
- 签名验签:用于身份验证和数据完整性校验, 强调真实性、完整性以及不可否认性。使用私钥签名,公钥验签。
举个栗子:
1. 加解密示例
A 要给 B 寄一份重要的文件快递,但怕被别人偷看,便使用了一个只能用 B 钥匙打开的快递箱。 如图:

- B 提前公开了开箱钥匙(公钥);
- A 把文件放进快递箱里,并用这把钥匙锁住(公钥加密);
- A 寄出这个快递箱(密文);
- B 收到后,用私钥打开;
- 只有 B 能看到内容,别人即使拦截也无法打开。
2. 签名验签示例
A 寄文件快递时,在包裹外贴上了专属的封条(签名)。B 收到包裹后,使用 A 的公开印章验签,判断这个包括是否是 A 寄出的,内容是否被修改过。

- A 写好信件后,用自己专属的签名封条(私钥)封住包裹;
- B 接收包裹, 从公开平台获取A 的印章样式(公钥);
- B 比对签名是否一致;
- 如果验证通过,B 可以确定包裹是 A 发出的,内容也没被改过。
二、 Maven 依赖配置
在 pom.xml 中添加如下依赖:
<!-- 1.78 -->
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk18on</artifactId><version>1.78</version>
</dependency>
三. 签名工具类
工具类说明:
- 工具类基于 Bouncy Castle 实现;
- SM2 用于非对称签名验签;
- SM3 用于哈希校验(非数字签名);
- SM4 用于对称加密,可用于实现“加密认证”。
Bouncy Castle 中,SM4Engine 默认使用 ECB 模式 。类 SmBaseUtils、 SmKeyGenUtils 和密钥对生成在上一篇文章中,这里就不贴了。
代码如下:
import org.bouncycastle.crypto.digests.SM3Digest;import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;/**** 国密算法签名工具类.** @author shijiangyong* @date 2025/11/5 10:23**/
public class SmAlgorithmSignUtils extends SmBaseUtils{/*** SM2 签名(非对称,基于公钥/私钥).** @param privateKey key* @param valueToDigest 数据* @return 结果*/public static String sm2Sign(final String privateKey, final String valueToDigest) {try {byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey);PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");PrivateKey key = keyFactory.generatePrivate(keySpec);Signature signature = Signature.getInstance("SM3withSM2", "BC");signature.initSign(key);signature.update(valueToDigest.getBytes(StandardCharsets.UTF_8));byte[] signed = signature.sign();return Base64.getEncoder().encodeToString(signed);} catch (Exception e) {throw new RuntimeException("SM2 sign error", e);}}/*** SM2 验签.** @param publicKey publicKey* @param value 验签数据* @param signatureStr 签名值* @return 结果*/public static boolean sm2Verify(final String publicKey, final String value, final String signatureStr) {try {byte[] publicKeyBytes = Base64.getDecoder().decode(publicKey);X509EncodedKeySpec keySpec 