Spring Boot项目实战:5分钟搞定BouncyCastle集成国密SM2加密
Spring Boot实战:5分钟集成BouncyCastle实现国密SM2加密
在金融、政务等对数据安全要求严格的领域,国密算法正逐步成为标配。作为Java开发者,如何在Spring Boot项目中快速集成SM2加密功能?本文将带你从零开始,5分钟内完成BouncyCastle的配置、密钥生成到业务层封装的完整流程。
1. 环境准备与基础配置
首先创建一个标准的Spring Boot项目,在pom.xml中添加BouncyCastle依赖:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.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 ResponseEntity<String> encryptData(@RequestBody String plainText) { KeyPair keyPair = keyService.generateKeyPair(); String cipherText = cryptoService.encrypt(plainText, keyService.serializePublicKey(keyPair.getPublic())); Map<String, String> response = new HashMap<>(); response.put("cipherText", cipherText); response.put("keyId", keyPair.getPublic().hashCode() + ""); return ResponseEntity.ok(response); } @PostMapping("/decrypt") public ResponseEntity<String> 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 ResponseEntity<ErrorResponse> handleCryptoError(CryptoException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse("CRYPTO_ERROR", "加密处理失败")); } }在实际项目中,我们还将SM2加密与Spring Security的过滤器链结合,实现了请求参数的自动加解密。当系统需要对接第三方国密认证平台时,这套方案只需稍作适配即可快速投入使用。
