Spring Boot实战5分钟集成BouncyCastle实现国密SM2加密在金融、政务等对数据安全要求严格的领域国密算法正逐步成为标配。作为Java开发者如何在Spring Boot项目中快速集成SM2加密功能本文将带你从零开始5分钟内完成BouncyCastle的配置、密钥生成到业务层封装的完整流程。1. 环境准备与基础配置首先创建一个标准的Spring Boot项目在pom.xml中添加BouncyCastle依赖dependency groupIdorg.bouncycastle/groupId artifactIdbcprov-jdk15on/artifactId version1.70/version /dependency接下来需要在应用启动时注册安全提供者。推荐在配置类中完成这一操作Configuration public class CryptoConfig { PostConstruct public void init() { if (Security.getProvider(BC) null) { Security.addProvider(new BouncyCastleProvider()); } } }常见问题排查如果遇到NoSuchProviderException检查依赖版本是否冲突多模块项目中需确保BouncyCastle只在主模块加载一次2. SM2密钥管理与存储方案SM2算法采用非对称加密体系密钥管理是首要考虑的问题。我们封装一个密钥生成服务Service public class SM2KeyService { private static final X9ECParameters CURVE_PARAMS GMNamedCurves.getByOID(GMObjectIdentifiers.sm2p256v1); public KeyPair generateKeyPair() { KeyPairGenerator generator KeyPairGenerator.getInstance(EC, BC); generator.initialize(new ECParameterSpec( CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN())); return generator.generateKeyPair(); } public String serializePublicKey(PublicKey publicKey) { BCECPublicKey ecPublicKey (BCECPublicKey) publicKey; return Base64.getEncoder().encodeToString(ecPublicKey.getQ().getEncoded(false)); } }密钥存储建议开发环境可使用application.yml临时存储生产环境推荐使用HashiCorp Vault或阿里云KMS公私钥分离存储私钥必须加密保存3. 核心加密服务实现创建SM2CryptoService封装加解密操作Service RequiredArgsConstructor public class SM2CryptoService { private final SM2KeyService keyService; public String encrypt(String plainText, String publicKeyBase64) { byte[] publicKeyBytes Base64.getDecoder().decode(publicKeyBase64); ECPoint publicKeyPoint CURVE_PARAMS.getCurve().decodePoint(publicKeyBytes); ECPublicKeyParameters pubKeyParams new ECPublicKeyParameters( publicKeyPoint, new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN())); SM2Engine engine new SM2Engine(SM2Engine.Mode.C1C3C2); engine.init(true, new ParametersWithRandom(pubKeyParams, new SecureRandom())); byte[] cipherText engine.processBlock( plainText.getBytes(StandardCharsets.UTF_8), 0, plainText.getBytes().length); return Base64.getEncoder().encodeToString(cipherText); } public String decrypt(String cipherText, PrivateKey privateKey) { BCECPrivateKey ecPrivateKey (BCECPrivateKey) privateKey; ECPrivateKeyParameters privKeyParams new ECPrivateKeyParameters( ecPrivateKey.getD(), new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN())); SM2Engine engine new SM2Engine(SM2Engine.Mode.C1C3C2); engine.init(false, privKeyParams); byte[] plainText engine.processBlock( Base64.getDecoder().decode(cipherText), 0, Base64.getDecoder().decode(cipherText).length); return new String(plainText, StandardCharsets.UTF_8); } }性能优化点重用SM2Engine实例减少对象创建开销对大文件采用分段加密策略使用线程本地变量存储频繁使用的密钥参数4. 业务层集成实战最后在Controller层提供加密接口RestController RequestMapping(/api/crypto) RequiredArgsConstructor public class CryptoController { private final SM2CryptoService cryptoService; private final SM2KeyService keyService; PostMapping(/encrypt) public ResponseEntityString encryptData(RequestBody String plainText) { KeyPair keyPair keyService.generateKeyPair(); String cipherText cryptoService.encrypt(plainText, keyService.serializePublicKey(keyPair.getPublic())); MapString, String response new HashMap(); response.put(cipherText, cipherText); response.put(keyId, keyPair.getPublic().hashCode() ); return ResponseEntity.ok(response); } PostMapping(/decrypt) public ResponseEntityString decryptData( RequestParam String cipherText, RequestParam String keyId) { // 实际项目中应从密钥管理系统获取私钥 PrivateKey privateKey getPrivateKeyFromVault(keyId); return ResponseEntity.ok( cryptoService.decrypt(cipherText, privateKey)); } }生产环境建议添加请求参数校验实现密钥轮换机制记录加密操作审计日志对敏感数据返回结果进行脱敏处理5. 测试验证与异常处理编写集成测试确保功能正确性SpringBootTest class SM2IntegrationTest { Autowired private SM2CryptoService cryptoService; Autowired private SM2KeyService keyService; Test void testEncryptDecrypt() { String originalText 国密算法测试123; KeyPair keyPair keyService.generateKeyPair(); String cipherText cryptoService.encrypt( originalText, keyService.serializePublicKey(keyPair.getPublic())); String decryptedText cryptoService.decrypt( cipherText, keyPair.getPrivate()); assertEquals(originalText, decryptedText); } }异常处理规范定义统一的CryptoException区分密钥无效、数据篡改等不同错误类型对BC库原生异常进行适当封装避免在异常信息中泄露密钥细节ControllerAdvice public class CryptoExceptionHandler { ExceptionHandler(SM2EngineException.class) public ResponseEntityErrorResponse handleCryptoError(CryptoException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse(CRYPTO_ERROR, 加密处理失败)); } }在实际项目中我们还将SM2加密与Spring Security的过滤器链结合实现了请求参数的自动加解密。当系统需要对接第三方国密认证平台时这套方案只需稍作适配即可快速投入使用。
Spring Boot项目实战:5分钟搞定BouncyCastle集成国密SM2加密
发布时间:2026/5/30 8:08:36
Spring Boot实战5分钟集成BouncyCastle实现国密SM2加密在金融、政务等对数据安全要求严格的领域国密算法正逐步成为标配。作为Java开发者如何在Spring Boot项目中快速集成SM2加密功能本文将带你从零开始5分钟内完成BouncyCastle的配置、密钥生成到业务层封装的完整流程。1. 环境准备与基础配置首先创建一个标准的Spring Boot项目在pom.xml中添加BouncyCastle依赖dependency groupIdorg.bouncycastle/groupId artifactIdbcprov-jdk15on/artifactId version1.70/version /dependency接下来需要在应用启动时注册安全提供者。推荐在配置类中完成这一操作Configuration public class CryptoConfig { PostConstruct public void init() { if (Security.getProvider(BC) null) { Security.addProvider(new BouncyCastleProvider()); } } }常见问题排查如果遇到NoSuchProviderException检查依赖版本是否冲突多模块项目中需确保BouncyCastle只在主模块加载一次2. SM2密钥管理与存储方案SM2算法采用非对称加密体系密钥管理是首要考虑的问题。我们封装一个密钥生成服务Service public class SM2KeyService { private static final X9ECParameters CURVE_PARAMS GMNamedCurves.getByOID(GMObjectIdentifiers.sm2p256v1); public KeyPair generateKeyPair() { KeyPairGenerator generator KeyPairGenerator.getInstance(EC, BC); generator.initialize(new ECParameterSpec( CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN())); return generator.generateKeyPair(); } public String serializePublicKey(PublicKey publicKey) { BCECPublicKey ecPublicKey (BCECPublicKey) publicKey; return Base64.getEncoder().encodeToString(ecPublicKey.getQ().getEncoded(false)); } }密钥存储建议开发环境可使用application.yml临时存储生产环境推荐使用HashiCorp Vault或阿里云KMS公私钥分离存储私钥必须加密保存3. 核心加密服务实现创建SM2CryptoService封装加解密操作Service RequiredArgsConstructor public class SM2CryptoService { private final SM2KeyService keyService; public String encrypt(String plainText, String publicKeyBase64) { byte[] publicKeyBytes Base64.getDecoder().decode(publicKeyBase64); ECPoint publicKeyPoint CURVE_PARAMS.getCurve().decodePoint(publicKeyBytes); ECPublicKeyParameters pubKeyParams new ECPublicKeyParameters( publicKeyPoint, new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN())); SM2Engine engine new SM2Engine(SM2Engine.Mode.C1C3C2); engine.init(true, new ParametersWithRandom(pubKeyParams, new SecureRandom())); byte[] cipherText engine.processBlock( plainText.getBytes(StandardCharsets.UTF_8), 0, plainText.getBytes().length); return Base64.getEncoder().encodeToString(cipherText); } public String decrypt(String cipherText, PrivateKey privateKey) { BCECPrivateKey ecPrivateKey (BCECPrivateKey) privateKey; ECPrivateKeyParameters privKeyParams new ECPrivateKeyParameters( ecPrivateKey.getD(), new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN())); SM2Engine engine new SM2Engine(SM2Engine.Mode.C1C3C2); engine.init(false, privKeyParams); byte[] plainText engine.processBlock( Base64.getDecoder().decode(cipherText), 0, Base64.getDecoder().decode(cipherText).length); return new String(plainText, StandardCharsets.UTF_8); } }性能优化点重用SM2Engine实例减少对象创建开销对大文件采用分段加密策略使用线程本地变量存储频繁使用的密钥参数4. 业务层集成实战最后在Controller层提供加密接口RestController RequestMapping(/api/crypto) RequiredArgsConstructor public class CryptoController { private final SM2CryptoService cryptoService; private final SM2KeyService keyService; PostMapping(/encrypt) public ResponseEntityString encryptData(RequestBody String plainText) { KeyPair keyPair keyService.generateKeyPair(); String cipherText cryptoService.encrypt(plainText, keyService.serializePublicKey(keyPair.getPublic())); MapString, String response new HashMap(); response.put(cipherText, cipherText); response.put(keyId, keyPair.getPublic().hashCode() ); return ResponseEntity.ok(response); } PostMapping(/decrypt) public ResponseEntityString decryptData( RequestParam String cipherText, RequestParam String keyId) { // 实际项目中应从密钥管理系统获取私钥 PrivateKey privateKey getPrivateKeyFromVault(keyId); return ResponseEntity.ok( cryptoService.decrypt(cipherText, privateKey)); } }生产环境建议添加请求参数校验实现密钥轮换机制记录加密操作审计日志对敏感数据返回结果进行脱敏处理5. 测试验证与异常处理编写集成测试确保功能正确性SpringBootTest class SM2IntegrationTest { Autowired private SM2CryptoService cryptoService; Autowired private SM2KeyService keyService; Test void testEncryptDecrypt() { String originalText 国密算法测试123; KeyPair keyPair keyService.generateKeyPair(); String cipherText cryptoService.encrypt( originalText, keyService.serializePublicKey(keyPair.getPublic())); String decryptedText cryptoService.decrypt( cipherText, keyPair.getPrivate()); assertEquals(originalText, decryptedText); } }异常处理规范定义统一的CryptoException区分密钥无效、数据篡改等不同错误类型对BC库原生异常进行适当封装避免在异常信息中泄露密钥细节ControllerAdvice public class CryptoExceptionHandler { ExceptionHandler(SM2EngineException.class) public ResponseEntityErrorResponse handleCryptoError(CryptoException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse(CRYPTO_ERROR, 加密处理失败)); } }在实际项目中我们还将SM2加密与Spring Security的过滤器链结合实现了请求参数的自动加解密。当系统需要对接第三方国密认证平台时这套方案只需稍作适配即可快速投入使用。