Java国密SM2算法实战:从Bouncy Castle集成到加解密签名完整实现

发布时间:2026/7/3 4:35:46

Java国密SM2算法实战:从Bouncy Castle集成到加解密签名完整实现 1. 项目概述为什么要在Java里折腾SM2如果你正在处理涉及金融、政务或者对数据安全有高要求的业务那么“国密算法”这个词你肯定不陌生。SM2作为国密算法体系中的非对称加密核心正逐渐成为这些领域的标配。最近我在一个数据交换平台的项目里就遇到了必须集成SM2进行数据加解密和签名验签的需求。甲方明确要求所有敏感数据的传输和存储都必须使用国密算法RSAAES对不起这次不灵了。网上找了一圈发现关于Java实现SM2的资料要么是零零散散的代码片段语焉不详要么就是直接丢给你一个庞大的、文档稀少的第三方库让人不知从何下手。更头疼的是很多文章只讲“怎么调用”却不讲“为什么这么调”遇到生成密钥对格式不对、加密后数据长度异常、签名验签总失败这些坑根本找不到解决方案。所以我决定把这次从零开始在Java环境中完整实现SM2加解密和加签验签的实战过程记录下来。这不是一个简单的API调用教程而是一个包含了密钥管理、算法原理理解、踩坑实录和性能考量的全流程复盘。无论你是正在对接国密需求的开发还是对密码学感兴趣想深入理解SM2这篇文章都能给你一套可直接复现、且知道每一步背后逻辑的实操方案。2. 核心思路与方案选型不走弯路的起点在动手写代码之前理清思路和选对工具至关重要。SM2的实现核心在于找到一个可靠、易用且符合标准的密码学库。2.1 为什么选择Bouncy CastleJava标准库JCE本身并不包含SM2算法。因此我们必须引入第三方库。主流的选项有Bouncy Castle和国内的GMSSLJava版。经过对比我选择了Bouncy CastleBC原因如下生态成熟资料相对丰富BC是Java密码学领域事实上的标准扩展库历史悠久社区活跃。虽然其对国密的官方支持也是后来加入的但其整体的稳定性和代码质量经过了长期考验。遇到问题时在Stack Overflow、GitHub等平台更容易找到相关讨论或线索。API设计统一易于集成BC的API设计与JCE标准兼容性很好。一旦引入你可以用类似Cipher、Signature这样的标准JCE类来操作SM2学习成本和集成成本较低。功能全面BC不仅提供了SM2还提供了SM3、SM4等国密算法以及完整的椭圆曲线ECC底层支持这对于理解SM2的密钥生成和参数配置非常有帮助。当然GMSSL是国密算法的“原厂”实现理论上更“正宗”。但在实际Java项目集成中其文档、Maven依赖的易用性以及与其他框架的兼容性有时会带来额外的挑战。对于大多数需要快速、稳定上线的业务场景BC是更稳妥的起点。2.2 项目依赖与基础环境准备这里我使用Maven进行依赖管理。BC提供了两个主要的JAR包bcprov-jdk15on核心密码学提供者和bcpkix-jdk15on处理证书和CRL等。对于基础的SM2操作我们只需要核心包。在你的pom.xml中添加依赖dependency groupIdorg.bouncycastle/groupId artifactIdbcprov-jdk15on/artifactId version1.70/version !-- 请使用最新稳定版 -- /dependency注意版本号请务必检查BC官网或Maven中央仓库使用最新稳定版。不同版本间API可能有细微差别本文代码基于1.70版本编写。接下来我们需要在代码中动态注册BC提供者或者通过JVM参数静态注册。我推荐在应用启动时动态注册这样更灵活不影响其他模块。import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.Security; public class Sm2Demo { static { // 如果尚未注册则添加BouncyCastleProvider if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) null) { Security.addProvider(new BouncyCastleProvider()); } } // ... 后续代码 }完成这一步你的Java环境就具备了操作SM2等算法的基础能力。3. SM2密钥对生成与管理非对称加密的基石就是密钥对。SM2使用的是椭圆曲线密码学ECC其密钥对包括一个私钥自己保管用于解密和签名和一个公钥可以公开用于加密和验签。3.1 生成SM2密钥对在BC中我们可以通过指定椭圆曲线参数来生成SM2的密钥对。国密局推荐的SM2曲线参数是固定的。import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom; public class KeyPairGeneratorDemo { public static KeyPair generateSm2KeyPair() throws Exception { // 获取SM2的椭圆曲线参数规范 ECNamedCurveParameterSpec sm2Spec ECNamedCurveTable.getParameterSpec(sm2p256v1); // 实例化密钥对生成器使用ECC算法并指定提供者为BC KeyPairGenerator kpg KeyPairGenerator.getInstance(EC, BouncyCastleProvider.PROVIDER_NAME); // 使用SM2的参数初始化生成器 kpg.initialize(sm2Spec, new SecureRandom()); // 生成密钥对 return kpg.generateKeyPair(); } public static void main(String[] args) throws Exception { KeyPair keyPair generateSm2KeyPair(); System.out.println(私钥格式: keyPair.getPrivate().getFormat()); // 通常是PKCS#8 System.out.println(公钥格式: keyPair.getPublic().getFormat()); // 通常是X.509 // 获取Base64编码的字符串便于存储和传输 String privateKeyBase64 java.util.Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); String publicKeyBase64 java.util.Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); System.out.println(\n--- Base64编码 ---); System.out.println(私钥: privateKeyBase64); System.out.println(公钥: publicKeyBase64); } }关键点解析sm2p256v1这是BC中定义的SM2曲线名称。它对应国密标准《GMT 0003-2012》中定义的椭圆曲线参数。KeyPairGenerator.getInstance(EC, ...)虽然算法是SM2但在JCE体系下它被归类为椭圆曲线EC算法。指定提供者为BC才能使用其特有的sm2p256v1参数。getEncoded()这个方法返回密钥的标准编码字节流。私钥通常是PKCS#8格式公钥是X.509格式。这两种格式是业界通用标准便于与其他系统交换密钥。3.2 从字节或字符串加载密钥实际项目中密钥对通常不是每次运行时生成而是预先生成并保存在配置文件、数据库或硬件加密机中。因此从Base64字符串或字节数组加载密钥是必备技能。import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; public class KeyLoader { /** * 从Base64字符串加载SM2私钥 */ public static PrivateKey loadPrivateKey(String base64PrivateKey) throws Exception { byte[] keyBytes java.util.Base64.getDecoder().decode(base64PrivateKey); // PKCS#8编码规范 PKCS8EncodedKeySpec keySpec new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory KeyFactory.getInstance(EC, BouncyCastleProvider.PROVIDER_NAME); return keyFactory.generatePrivate(keySpec); } /** * 从Base64字符串加载SM2公钥 */ public static PublicKey loadPublicKey(String base64PublicKey) throws Exception { byte[] keyBytes java.util.Base64.getDecoder().decode(base64PublicKey); // X.509编码规范 X509EncodedKeySpec keySpec new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory KeyFactory.getInstance(EC, BouncyCastleProvider.PROVIDER_NAME); return keyFactory.generatePublic(keySpec); } }实操心得这里最容易出问题的地方是密钥格式。务必确保你存储的私钥是PKCS#8格式公钥是X.509格式。如果你从其他系统如OpenSSL生成的密钥可能需要先进行格式转换。一个常见的错误是将PEM格式-----BEGIN PRIVATE KEY-----的密钥直接去掉头尾行进行Base64解码这是正确的因为PEM本质上就是Base64编码的DER内容加上头尾标识。只要解码后的字节是标准的PKCS#8或X.509 DER编码上述加载方法就有效。4. SM2非对称加解密实现SM2加密和解密是非对称加密的典型应用用公钥加密用私钥解密。4.1 加密过程详解与实现SM2加密过程比RSA复杂一些它本质上是一种基于椭圆曲线的集成加密方案ECIES输出包含一个椭圆曲线点C1、密钥派生产生的对称密钥加密的密文C2和消息认证码C3。但幸运的是BC帮我们封装了这些细节。import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import java.security.PublicKey; public class Sm2Encryptor { /** * SM2公钥加密 * param publicKey SM2公钥 * param data 待加密的明文数据 * return 加密后的密文字节数组 */ public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception { // 获取SM2加密的Cipher实例使用BC提供者 // 算法名称为 SM2 Cipher cipher Cipher.getInstance(SM2, BouncyCastleProvider.PROVIDER_NAME); // 初始化为加密模式传入公钥 cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 执行加密 return cipher.doFinal(data); } /** * 一个更实用的方法加密文本并返回Base64字符串 */ public static String encryptToBase64(PublicKey publicKey, String plainText, String charsetName) throws Exception { byte[] data plainText.getBytes(charsetName); // 如 UTF-8 byte[] encrypted encrypt(publicKey, data); return java.util.Base64.getEncoder().encodeToString(encrypted); } }为什么加密后的数据长度不固定与RSA加密后长度固定为密钥长度不同SM2加密后的数据长度是64字节C1点 明文长度 32字节C3即SM3杂凑值。对于256位曲线C1点是两个256位整数xy的压缩或未压缩表示通常为65字节未压缩格式04xy或33字节压缩格式。BC默认可能使用混合格式或未压缩格式。因此加密一个很短的字符串得到的密文也会长达100多字节。这是正常的是SM2算法机制决定的。4.2 解密过程详解与实现解密是加密的逆过程需要使用配对的私钥。import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import java.security.PrivateKey; public class Sm2Decryptor { /** * SM2私钥解密 * param privateKey SM2私钥 * param encryptedData 密文数据 * return 解密后的明文字节数组 */ public static byte[] decrypt(PrivateKey privateKey, byte[] encryptedData) throws Exception { Cipher cipher Cipher.getInstance(SM2, BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(encryptedData); } /** * 解密Base64编码的密文字符串 */ public static String decryptFromBase64(PrivateKey privateKey, String base64Encrypted, String charsetName) throws Exception { byte[] encryptedData java.util.Base64.getDecoder().decode(base64Encrypted); byte[] decrypted decrypt(privateKey, encryptedData); return new String(decrypted, charsetName); } }4.3 加解密完整测试用例让我们把生成、加密、解密串起来写一个完整的测试。public class Sm2EncryptionDemo { public static void main(String[] args) { try { // 1. 生成密钥对 KeyPair keyPair KeyPairGeneratorDemo.generateSm2KeyPair(); PublicKey publicKey keyPair.getPublic(); PrivateKey privateKey keyPair.getPrivate(); String originalText 这是一段需要加密的敏感数据比如身份证号或合同金额。; System.out.println(原始明文: originalText); // 2. 加密 String encryptedBase64 Sm2Encryptor.encryptToBase64(publicKey, originalText, UTF-8); System.out.println(加密后(Base64): encryptedBase64); System.out.println(密文长度(字节): java.util.Base64.getDecoder().decode(encryptedBase64).length); // 3. 解密 String decryptedText Sm2Decryptor.decryptFromBase64(privateKey, encryptedBase64, UTF-8); System.out.println(解密后明文: decryptedText); // 4. 验证 System.out.println(解密是否成功: originalText.equals(decryptedText)); } catch (Exception e) { e.printStackTrace(); } } }运行这个Demo你应该能看到加密解密成功的输出。注意观察密文的长度体会SM2加密的特点。5. SM2数字签名与验签实现数字签名用于验证数据的完整性和来源真实性。发送方用私钥对数据或其摘要签名接收方用公钥验签。5.1 签名过程详解与实现SM2的签名算法与ECDSA类似但使用了SM3作为杂凑函数。在BC中我们使用Signature类。import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.PrivateKey; import java.security.Signature; public class Sm2Signer { /** * 使用SM2私钥对数据进行签名 * param privateKey 私钥 * param data 原始数据 * return 签名字节数组 (通常是DER编码的(r,s)序列) */ public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception { // 获取Signature实例算法指定为 SM3withSM2 Signature signature Signature.getInstance(SM3withSM2, BouncyCastleProvider.PROVIDER_NAME); // 初始化签名对象进入签名模式 signature.initSign(privateKey); // 传入要签名的数据 signature.update(data); // 执行签名 return signature.sign(); } /** * 对数据进行签名并返回Base64字符串 */ public static String signToBase64(PrivateKey privateKey, String data, String charsetName) throws Exception { byte[] dataBytes data.getBytes(charsetName); byte[] signBytes sign(privateKey, dataBytes); return java.util.Base64.getEncoder().encodeToString(signBytes); } }关键点SM3withSM2这个算法名称是BC特有的它指明了签名算法是SM2并且使用的消息摘要算法是国密的SM3。这是与SHA256withECDSA等ECDSA算法的关键区别。5.2 验签过程详解与实现验签使用公钥验证签名是否由对应的私钥生成且数据未被篡改。import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.PublicKey; import java.security.Signature; public class Sm2Verifier { /** * 使用SM2公钥验证签名 * param publicKey 公钥 * param data 原始数据 * param sign 签名字节数组 * return true-验签成功 false-验签失败 */ public static boolean verify(PublicKey publicKey, byte[] data, byte[] sign) throws Exception { Signature signature Signature.getInstance(SM3withSM2, BouncyCastleProvider.PROVIDER_NAME); signature.initVerify(publicKey); signature.update(data); return signature.verify(sign); } /** * 验证Base64编码的签名 */ public static boolean verifyFromBase64(PublicKey publicKey, String data, String base64Sign, String charsetName) throws Exception { byte[] dataBytes data.getBytes(charsetName); byte[] signBytes java.util.Base64.getDecoder().decode(base64Sign); return verify(publicKey, dataBytes, signBytes); } }5.3 签名验签完整测试与“坑点”实录public class Sm2SignatureDemo { public static void main(String[] args) { try { KeyPair keyPair KeyPairGeneratorDemo.generateSm2KeyPair(); PublicKey publicKey keyPair.getPublic(); PrivateKey privateKey keyPair.getPrivate(); String message 这是一份重要的电子合同金额为100万元。; System.out.println(原始消息: message); // 1. 签名 String signatureBase64 Sm2Signer.signToBase64(privateKey, message, UTF-8); System.out.println(数字签名(Base64): signatureBase64); // 2. 验签 (正常情况) boolean verifyResult1 Sm2Verifier.verifyFromBase64(publicKey, message, signatureBase64, UTF-8); System.out.println(对原始消息验签结果: verifyResult1); // 应为 true // 3. 验签 (消息被篡改) String tamperedMessage message 已被恶意修改; boolean verifyResult2 Sm2Verifier.verifyFromBase64(publicKey, tamperedMessage, signatureBase64, UTF-8); System.out.println(对篡改消息验签结果: verifyResult2); // 应为 false // 4. 验签 (签名错误) String wrongSignature ThisIsNotAValidSignatureAtAll; // 这里会抛出异常因为Base64解码失败或签名格式错误 try { boolean verifyResult3 Sm2Verifier.verifyFromBase64(publicKey, message, wrongSignature, UTF-8); System.out.println(对错误签名验签结果: verifyResult3); // 应为 false } catch (IllegalArgumentException e) { System.out.println(错误签名导致异常: e.getMessage()); } } catch (Exception e) { e.printStackTrace(); } } }踩坑实录与核心注意事项签名数据的编码一致性这是最常见的坑签名时对字符串message.getBytes(UTF-8)验签时也必须用完全相同的字符集message.getBytes(UTF-8)。如果一端用UTF-8另一端用GBK即使字符串看起来一样字节数组也不同验签必然失败。最佳实践是对于要签名的业务数据先明确约定并统一使用一种编码如UTF-8或者直接对数据的字节数组进行签名和验签。签名值的格式signature.sign()返回的通常是DER编码的ASN.1序列包含了r和s两个大整数。有些其他平台如某些C语言实现可能返回的是r和s的简单拼接各32字节共64字节。如果你需要与这类平台交互可能需要对签名值进行编解码转换。BC的Signature类在验签时期望的也是DER格式。如果遇到验签失败先检查签名值的格式是否匹配。“SM3withSM2”与“SM2”在Signature.getInstance时务必使用SM3withSM2。如果错误地使用了SM2BC可能会找不到该算法而抛出异常。6. 实战进阶解决典型问题与性能优化在实际项目集成中你可能会遇到比Demo更复杂的情况。下面分享几个实战中的关键问题和优化思路。6.1 问题一与OpenSSL或其他语言生成的密钥/签名互通场景后端用JavaBC前端或另一个服务用OpenSSL命令生成的SM2密钥和签名无法互相加解密或验签。排查思路密钥格式确认双方使用的密钥格式。OpenSSL默认生成的PEM私钥可能是PKCS#1格式-----BEGIN EC PRIVATE KEY-----而Java BC通常期望PKCS#8格式。你需要用OpenSSL命令进行转换# 将PKCS#1转换为PKCS#8 openssl pkcs8 -topk8 -nocrypt -in sm2_private_key.pem -out sm2_private_key_pkcs8.pem公钥通常问题不大X.509格式是通用的。曲线参数确保双方使用的是同一条椭圆曲线即sm2p256v1。在OpenSSL生成密钥时需要指定参数openssl ecparam -genkey -name sm2p256v1 -out sm2_private_key.pem签名格式如前所述关注签名值是DER编码还是裸的r||s拼接。BC默认使用DER编码。如果对方是裸拼接你需要在验签前将其转换为DER格式。BC提供了ASN1Primitive等类来帮助构建DER序列。6.2 问题二加密数据长度限制与分段处理场景SM2作为非对称加密虽然不像RSA有严格的明文长度限制例如2048位密钥最多加密245字节但由于其加密过程中包含对称加密步骤生成C2理论上可以加密较长的数据。然而在实际使用中尤其是与某些硬件加密机或特定平台交互时可能会有自己的限制。建议对于非常长的数据如超过数MB的文件非对称加密在性能上是不合适的。标准的做法是采用混合加密随机生成一个对称密钥如SM4密钥。用SM4对称加密算法加密原始大文件。用SM2公钥加密这个临时的SM4密钥。将SM2加密后的密钥SM4加密后的文件数据一起发送或存储。解密时先用SM2私钥解密出SM4密钥再用SM4密钥解密文件数据。6.3 性能考量与最佳实践密钥对象复用KeyPair、PublicKey、PrivateKey对象一旦创建可以安全地重复用于多次加密、解密、签名、验签操作。避免在每次操作前都从Base64字符串解析密钥。使用线程安全的类Cipher和Signature实例不是线程安全的。对于高并发场景应该为每个线程创建新的实例或者使用ThreadLocal进行缓存。更简单的方式是在方法内部每次创建新实例因为创建它们的开销在单次加解密/签名操作中占比很小。异常处理加解密和签名验签操作可能抛出多种异常如BadPaddingException解密时密钥不对或数据被篡改、InvalidKeyException密钥格式错误、SignatureException验签失败等。在生产代码中务必进行细致的异常捕获和日志记录这有助于快速定位是密钥问题、数据问题还是算法配置问题。日志脱敏绝对不要在日志中直接打印明文、密钥或完整的密文。可以打印其长度、哈希值如SM3或前几位后几位用于调试。7. 完整工具类封装与使用示例将上述功能封装成一个工具类便于在项目中调用。import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import javax.crypto.Cipher; import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; /** * SM2 加解密及签名工具类 * 依赖Bouncy Castle 1.70 */ public class Sm2Util { static { if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) null) { Security.addProvider(new BouncyCastleProvider()); } } private static final String ALGORITHM EC; private static final String SM2_CURVE_NAME sm2p256v1; private static final String SM2_CIPHER_ALG SM2; private static final String SM2_SIGN_ALG SM3withSM2; private static final String CHARSET UTF-8; // 密钥对生成 public static KeyPair generateKeyPair() throws Exception { ECNamedCurveParameterSpec sm2Spec ECNamedCurveTable.getParameterSpec(SM2_CURVE_NAME); KeyPairGenerator kpg KeyPairGenerator.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME); kpg.initialize(sm2Spec, new SecureRandom()); return kpg.generateKeyPair(); } public static String getPublicKeyBase64(PublicKey publicKey) { return Base64.getEncoder().encodeToString(publicKey.getEncoded()); } public static String getPrivateKeyBase64(PrivateKey privateKey) { return Base64.getEncoder().encodeToString(privateKey.getEncoded()); } // 密钥加载 public static PublicKey loadPublicKey(String base64PublicKey) throws Exception { byte[] keyBytes Base64.getDecoder().decode(base64PublicKey); X509EncodedKeySpec keySpec new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory KeyFactory.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME); return keyFactory.generatePublic(keySpec); } public static PrivateKey loadPrivateKey(String base64PrivateKey) throws Exception { byte[] keyBytes Base64.getDecoder().decode(base64PrivateKey); PKCS8EncodedKeySpec keySpec new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory KeyFactory.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME); return keyFactory.generatePrivate(keySpec); } // 加解密 public static String encrypt(String plainText, String base64PublicKey) throws Exception { PublicKey publicKey loadPublicKey(base64PublicKey); Cipher cipher Cipher.getInstance(SM2_CIPHER_ALG, BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] encrypted cipher.doFinal(plainText.getBytes(CHARSET)); return Base64.getEncoder().encodeToString(encrypted); } public static String decrypt(String base64CipherText, String base64PrivateKey) throws Exception { PrivateKey privateKey loadPrivateKey(base64PrivateKey); Cipher cipher Cipher.getInstance(SM2_CIPHER_ALG, BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decrypted cipher.doFinal(Base64.getDecoder().decode(base64CipherText)); return new String(decrypted, CHARSET); } // 签名验签 public static String sign(String data, String base64PrivateKey) throws Exception { PrivateKey privateKey loadPrivateKey(base64PrivateKey); Signature signature Signature.getInstance(SM2_SIGN_ALG, BouncyCastleProvider.PROVIDER_NAME); signature.initSign(privateKey); signature.update(data.getBytes(CHARSET)); byte[] signBytes signature.sign(); return Base64.getEncoder().encodeToString(signBytes); } public static boolean verify(String data, String base64Sign, String base64PublicKey) throws Exception { PublicKey publicKey loadPublicKey(base64PublicKey); Signature signature Signature.getInstance(SM2_SIGN_ALG, BouncyCastleProvider.PROVIDER_NAME); signature.initVerify(publicKey); signature.update(data.getBytes(CHARSET)); return signature.verify(Base64.getDecoder().decode(base64Sign)); } // 使用示例 public static void main(String[] args) throws Exception { // 1. 生成并保存密钥对 KeyPair keyPair generateKeyPair(); String publicKeyStr getPublicKeyBase64(keyPair.getPublic()); String privateKeyStr getPrivateKeyBase64(keyPair.getPrivate()); System.out.println(公钥: publicKeyStr.substring(0, 50) ...); System.out.println(私钥: privateKeyStr.substring(0, 50) ...); String originalData 测试数据123ABC; System.out.println(\n原始数据: originalData); // 2. 加密解密 String encrypted encrypt(originalData, publicKeyStr); System.out.println(加密后: encrypted); String decrypted decrypt(encrypted, privateKeyStr); System.out.println(解密后: decrypted); System.out.println(加解密是否一致: originalData.equals(decrypted)); // 3. 签名验签 String signature sign(originalData, privateKeyStr); System.out.println(\n签名值: signature); boolean isValid verify(originalData, signature, publicKeyStr); System.out.println(验签结果: isValid); // 4. 篡改后验签 boolean isTamperedValid verify(originalData x, signature, publicKeyStr); System.out.println(数据篡改后验签结果: isTamperedValid); } }这个工具类Sm2Util提供了从生成密钥到加解密、签名验签的完整闭环方法并且所有输入输出都使用Base64字符串方便在JSON、数据库或配置文件中存储和传输。你可以直接将其复制到你的工具模块中使用。最后再强调一个至关重要的安全实践私钥的安全管理。在生产环境中私钥绝不能像示例中这样硬编码在代码或配置文件中。应该使用安全的密钥管理系统KMS、硬件安全模块HSM或至少在部署时从高度安全的环境变量、加密的密钥文件中读取。保护好私钥是整个非对称加密体系安全的根本。

相关新闻