Browse Source

密码平台接口开发

dev-V1.0.9
vincent 1 month ago
parent
commit
934c112976
  1. 19
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/config/PasswordPartyConfig.java
  2. 4
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/passwordparty/KeyPairVo.java
  3. 18
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/passwordparty/SymmetryEncryptVo.java
  4. 96
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/IPasswordPartyService.java
  5. 179
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/PasswordPartyDemo.java
  6. 264
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/impl/PasswordPartyServiceImpl.java
  7. 3
      zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml
  8. 6
      zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-prod.yml

19
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/config/PasswordPartyConfig.java

@ -0,0 +1,19 @@
package com.zdxt.enforcementcode.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "password-party")
public class PasswordPartyConfig {
private String baseUrl;
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
}

4
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/passwordparty/KeyPairVo.java

@ -0,0 +1,4 @@
package com.zdxt.enforcementcode.domain.vo.passwordparty;
public class KeyPairVo {
}

18
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/passwordparty/SymmetryEncryptVo.java

@ -0,0 +1,18 @@
package com.zdxt.enforcementcode.domain.vo.passwordparty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class SymmetryEncryptVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String outData;
private String ivData;
}

96
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/IPasswordPartyService.java

@ -0,0 +1,96 @@
package com.zdxt.enforcementcode.service.thirdparty;
import com.zdxt.enforcementcode.domain.vo.passwordparty.SymmetryEncryptVo;
/**
* 密码服务平台
*
* @author vincent
* @date 2026-09-22
*/
public interface IPasswordPartyService {
/**
* HSM - 生成随机数
*
* @param length 随机数长度
* @return 随机数(Hex 16进制类型)
*/
String generateRandom(int length);
/**
* HSM - 对称加密
*
* @param keyIndex 用户密钥号,填0时使用keyData作为密钥
* @param algType 对称算法类型(SM1或SM4)
* @param key 外部密钥(keyIndex为0时生效,Hex 16进制)
* @param iv IV值,CBC/CFB/OFB模式时必传(Hex 16进制)
* @param inputData 加密原文(Hex 16进制)
* @return 对称加密结果
*/
SymmetryEncryptVo symmetryEncrypt(Integer keyIndex, int algType, String key, String iv, String inputData);
/**
* HSM - 对称解密
*
* @param keyIndex 用户密钥号,填0时使用keyData作为密钥
* @param algType 对称算法类型(SM1或SM4)
* @param key 外部密钥(keyIndex为0时生效,Hex 16进制)
* @param iv IV值,CBC/CFB/OFB模式时必传(Hex 16进制)
* @param inputData 解密密文(Hex 16进制)
* @return 对称解密结果
*/
SymmetryEncryptVo symmetryDecrypt(Integer keyIndex, int algType, String key, String iv, String inputData);
/**
* HSM - SM2签名
*
* @param keyIndex 密钥索引
* @param plainData 明文摘要(先使用杂凑接口计算摘要,Hex 16进制类型)
* @return 签名值(Hex 16进制类型)
*/
String sm2SignData(Integer keyIndex, String plainData);
/**
* HSM - SM2验签
*
* @param algType 算法类型
* @param publicKey 公钥(使用导出公钥接口获取,Hex 16进制类型)
* @param signValue 签名值(Hex 16进制类型)
* @param plainData 明文摘要(先使用杂凑接口计算摘要,Hex 16进制类型)
* @return true=验证通过, false=验证失败
*/
boolean sm2Verify(Integer algType, String publicKey, String signValue, String plainData);
/**
* HSM - 非对称加密
*
* @param algType 算法ID(请查阅算法字典)
* @param publcKey 公钥(Hex 16进制类型)
* @param data 明文(Hex 16进制类型)
* @return 加密密文(Hex 16进制类型)
*/
String externalEncrypt(Integer algType, String publcKey, String data);
/**
* HSM - 非对称解密
*
* @param keyIndex 密钥索引
* @param algId 算法ID(请查阅算法字典)
* @param enc 密文(Hex 16进制类型)
* @return 解密明文(Hex 16进制类型)
*/
String internalDecrypt(Integer keyIndex, Integer algId, String enc);
/**
* HSM - HMAC摘要
*
* @param keyIndex 密钥索引(必填)
* @param key 密钥,非必传(Hex 16进制类型)
* @param algType 算法ID标签,请使用SM3(查阅算法字典)
* @param inputData 明文摘要(Hex 16进制类型,必填)
* @return HMAC值(Hex 16进制类型)
*/
String hmacWithIndex(Integer keyIndex, String key, Integer algType, String inputData);
}

179
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/PasswordPartyDemo.java

@ -0,0 +1,179 @@
package com.zdxt.enforcementcode.service.thirdparty;
import com.zdxt.enforcementcode.config.PasswordPartyConfig;
import com.zdxt.enforcementcode.domain.vo.passwordparty.SymmetryEncryptVo;
import com.zdxt.enforcementcode.service.thirdparty.impl.PasswordPartyServiceImpl;
/**
* IPasswordPartyService 接口方法正向流程演示
* <p>
* 运行前请确保:
* 1. HSM 密码服务平台已启动且网络可达
* 2. 下方 BASE_URL 配置为正确的 HSM 服务地址
* 3. HSM 中已配置好所需的密钥(密钥索引、算法类型等)
* </p>
*
* @author vincent
* @date 2026-09-22
*/
public class PasswordPartyDemo {
/** HSM 密码服务平台地址,按实际环境修改 */
private static final String BASE_URL = "http://localhost:8080";
/** 测试用密钥索引 */
private static final int KEY_INDEX = 1;
/** SM4 算法ID(按实际算法字典修改) */
private static final int ALG_SM4 = 1;
/** SM2 算法ID */
private static final int ALG_SM2 = 2;
/** SM3 算法ID */
private static final int ALG_SM3 = 4;
private static IPasswordPartyService service;
/**
* 主入口:手动构造 PasswordPartyServiceImpl 实例,依次调用各接口方法验证正向流程
* <p>
* 执行顺序:
* 1. 生成随机数
* 2. 对称加密 → 对称解密(验证互为逆操作)
* 3. SM2签名
* 4. 非对称加密 → 非对称解密(验证互为逆操作)
* 5. HMAC摘要
* </p>
*/
public static void main(String[] args) {
// 构造配置并初始化 Service 实例(不依赖 Spring 容器)
PasswordPartyConfig config = new PasswordPartyConfig();
config.setBaseUrl(BASE_URL);
service = new PasswordPartyServiceImpl(config);
System.out.println("========== IPasswordPartyService 正向流程演示 ==========\n");
// 1. 生成随机数:验证 HSM 基础连通性
testGenerateRandom();
// 2. 对称加密 + 对称解密:使用 SM4 算法,验证加解密互为逆操作
testSymmetryEncryptAndDecrypt();
// 3. SM2签名:对摘要数据进行签名,验证签名值非空
testSM2SignAndVerify();
// 4. 非对称加密 + 非对称解密:使用 SM2 算法,验证加解密互为逆操作
testAsymmetricEncryptAndDecrypt();
// 5. HMAC摘要:使用 SM3 算法计算消息认证码
testHmacWithIndex();
System.out.println("\n========== 全部演示完成 ==========");
}
/**
* 1. 生成随机数
*/
private static void testGenerateRandom() {
System.out.println("----- 1. 生成随机数 -----");
String random = service.generateRandom(10);
System.out.println("随机数(10字节): " + random);
assertNotBlank("random", random);
String random32 = service.generateRandom(32);
System.out.println("随机数(32字节): " + random32);
assertNotBlank("random32", random32);
System.out.println();
}
/**
* 2. 对称加密 + 对称解密(互为逆操作)
*/
private static void testSymmetryEncryptAndDecrypt() {
System.out.println("----- 2. 对称加密 + 对称解密 -----");
String plainHex = "31323334353637383132333435363738";
SymmetryEncryptVo encryptResult = service.symmetryEncrypt(KEY_INDEX, ALG_SM4, null, null, plainHex);
System.out.println("对称加密原文: " + plainHex);
System.out.println("对称加密密文: " + encryptResult.getOutData());
assertNotBlank("encryptOutData", encryptResult.getOutData());
SymmetryEncryptVo decryptResult = service.symmetryDecrypt(KEY_INDEX, ALG_SM4, null, null, encryptResult.getOutData());
System.out.println("对称解密结果: " + decryptResult.getOutData());
assertNotBlank("decryptOutData", decryptResult.getOutData());
if (plainHex.equalsIgnoreCase(decryptResult.getOutData())) {
System.out.println("[PASS] 对称加密/解密互为逆操作,解密结果与原文一致");
} else {
System.out.println("[WARN] 解密结果与原文不一致,请检查密钥配置");
}
System.out.println();
}
/**
* 3. SM2签名 + SM2验签
*/
private static void testSM2SignAndVerify() {
System.out.println("----- 3. SM2签名 + SM2验签 -----");
String plainDataHex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
String signValue = service.sm2SignData(KEY_INDEX, plainDataHex);
System.out.println("SM2签名原文(摘要): " + plainDataHex);
System.out.println("SM2签名值: " + signValue);
assertNotBlank("signData", signValue);
System.out.println("[PASS] SM2签名成功,签名值非空");
System.out.println();
}
/**
* 4. 非对称加密 + 非对称解密
*/
private static void testAsymmetricEncryptAndDecrypt() {
System.out.println("----- 4. 非对称加密 + 非对称解密 -----");
String plainHex = "31323334";
String publicKey = getTestPublicKey();
String encryptData = service.externalEncrypt(ALG_SM2, publicKey, plainHex);
System.out.println("非对称加密原文: " + plainHex);
System.out.println("非对称加密密文: " + encryptData);
assertNotBlank("encryptData", encryptData);
String decrypted = service.internalDecrypt(KEY_INDEX, ALG_SM2, encryptData);
System.out.println("非对称解密结果: " + decrypted);
assertNotBlank("decrypted", decrypted);
if (plainHex.equalsIgnoreCase(decrypted)) {
System.out.println("[PASS] 非对称加密/解密互为逆操作,解密结果与原文一致");
} else {
System.out.println("[WARN] 解密结果与原文不一致,请检查密钥配置");
}
System.out.println();
}
/**
* 5. HMAC摘要
*/
private static void testHmacWithIndex() {
System.out.println("----- 5. HMAC摘要 -----");
String inputDataHex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
String hmac = service.hmacWithIndex(KEY_INDEX, null, ALG_SM3, inputDataHex);
System.out.println("HMAC输入摘要: " + inputDataHex);
System.out.println("HMAC输出值: " + hmac);
assertNotBlank("hmac", hmac);
System.out.println("[PASS] HMAC摘要成功,结果非空");
System.out.println();
}
// ======================== 辅助方法 ========================
private static String getTestPublicKey() {
// TODO: 替换为 HSM 中实际导出的公钥(Hex 16进制)
return "a746f6b47e9261adbf79e2af77d4b40919c01a85d9b11722c4bdd7cb36fa8ddad68c8abc96d73c986c6de1544c147e0b8208d98d9976facd8f00f312c7c5a90e";
}
private static void assertNotBlank(String fieldName, String value) {
if (value == null || value.isBlank()) {
throw new AssertionError("[FAIL] " + fieldName + " 返回值为空");
}
}
}

264
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/impl/PasswordPartyServiceImpl.java

@ -0,0 +1,264 @@
package com.zdxt.enforcementcode.service.thirdparty.impl;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import com.zdxt.enforcementcode.config.PasswordPartyConfig;
import com.zdxt.enforcementcode.domain.vo.passwordparty.SymmetryEncryptVo;
import com.zdxt.enforcementcode.service.thirdparty.IPasswordPartyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.base.BaseException;
import org.springframework.stereotype.Service;
/**
* 密码服务平台
*
* @author vincent
* @date 2026-09-22
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class PasswordPartyServiceImpl implements IPasswordPartyService {
private final PasswordPartyConfig passwordPartyConfig;
/**
* 生成随机数
*
* @param length 随机数长度
* @return 随机数(Hex 16进制类型)
*/
@Override
public String generateRandom(int length) {
JSONObject params = new JSONObject();
params.put("length", length);
JSONObject data = callHsmApi("/hsm/api/GenerateRandom", params, "生成随机数");
return data.getString("random");
}
/**
* 对称加密
*
* @param keyIndex 用户密钥号,填0时使用keyData做密钥
* @param algType 对称算法SM1或SM4
* @param key 外部密钥,keyIndex为0时生效(Hex 16进制类型)
* @param iv IV值,CBC/CFB/OFB模式时必传(Hex 16进制类型)
* @param inputData 加密原文(Hex 16进制类型)
* @return 对称加密结果
*/
@Override
public SymmetryEncryptVo symmetryEncrypt(Integer keyIndex, int algType, String key, String iv, String inputData) {
JSONObject params = buildSymmetryParams(keyIndex, algType, key, iv, inputData);
JSONObject data = callHsmApi("/hsm/api/symmetryEncrypt", params, "对称加密");
return buildSymmetryEncryptVo(data);
}
/**
* 对称解密
*
* @param keyIndex 用户密钥号,填0时使用keyData做密钥
* @param algType 对称算法SM1或SM4
* @param key 外部密钥,keyIndex为0时生效(Hex 16进制类型)
* @param iv IV值,CBC/CFB/OFB模式时必传(Hex 16进制类型)
* @param inputData 解密原文(Hex 16进制类型)
* @return 对称解密结果
*/
@Override
public SymmetryEncryptVo symmetryDecrypt(Integer keyIndex, int algType, String key, String iv, String inputData) {
JSONObject params = buildSymmetryParams(keyIndex, algType, key, iv, inputData);
JSONObject data = callHsmApi("/hsm/api/symmetryDecrypt", params, "对称解密");
return buildSymmetryEncryptVo(data);
}
/**
* SM2签名
*
* @param keyIndex 密钥索引
* @param plainData 明文摘要,先使用杂凑接口计算摘要(Hex 16进制类型)
* @return 签名值(Hex 16进制类型)
*/
@Override
public String sm2SignData(Integer keyIndex, String plainData) {
JSONObject params = new JSONObject();
params.put("keyIndex", keyIndex);
params.put("plainData", plainData);
JSONObject data = callHsmApi("/hsm/api/SM2SignData", params, "SM2签名");
return data.getString("signData");
}
/**
* SM2验签
*
* @param algType 算法类型
* @param publicKey 公钥,使用导出公钥接口获取(Hex 16进制类型)
* @param signValue 签名值(Hex 16进制类型)
* @param plainData 明文摘要,先使用杂凑接口计算摘要(Hex 16进制类型)
* @return true=验证通过
*/
@Override
public boolean sm2Verify(Integer algType, String publicKey, String signValue, String plainData) {
JSONObject params = new JSONObject();
params.put("algType", algType);
params.put("publicKey", publicKey);
params.put("signValue", signValue);
params.put("plainData", plainData);
doPost("/hsm/api/sm2Verify", params, "SM2验签");
return true;
}
/**
* 非对称加密
*
* @param algType 算法ID
* @param publcKey 公钥(Hex 16进制类型)
* @param data 明文(Hex 16进制类型)
* @return 加密密文(Hex 16进制类型)
*/
@Override
public String externalEncrypt(Integer algType, String publcKey, String data) {
JSONObject params = new JSONObject();
params.put("algType", algType);
params.put("publcKey", publcKey);
params.put("data", data);
JSONObject result = callHsmApi("/hsm/api/externalEncrypt", params, "非对称加密");
return result.getString("encryptData");
}
/**
* 非对称解密
*
* @param keyIndex 密钥索引
* @param algId 算法ID
* @param enc 密文(Hex 16进制类型)
* @return 解密明文(Hex 16进制类型)
*/
@Override
public String internalDecrypt(Integer keyIndex, Integer algId, String enc) {
JSONObject params = new JSONObject();
params.put("keyIndex", keyIndex);
params.put("algId", algId);
params.put("enc", enc);
JSONObject data = callHsmApi("/hsm/api/internalDecrypt", params, "非对称解密");
return data.getString("plainData");
}
/**
* HMAC摘要
*
* @param keyIndex 密钥索引(必填)
* @param key 密钥,非必传(Hex 16进制类型)
* @param algType 算法ID标签,请使用SM3
* @param inputData 明文摘要(Hex 16进制类型,必填)
* @return HMAC值(Hex 16进制类型)
*/
@Override
public String hmacWithIndex(Integer keyIndex, String key, Integer algType, String inputData) {
JSONObject params = new JSONObject();
params.put("keyIndex", keyIndex);
if (key != null) {
params.put("key", key);
}
params.put("algType", algType);
params.put("inputData", inputData);
JSONObject data = callHsmApi("/hsm/api/HmacWithIndex", params, "HMAC");
return data.getString("hmac");
}
/**
* 构建对称加密/解密的公共请求参数
*/
private JSONObject buildSymmetryParams(Integer keyIndex, int algType, String key, String iv, String inputData) {
JSONObject params = new JSONObject();
params.put("keyIndex", keyIndex);
params.put("algType", algType);
if (key != null) {
params.put("key", key);
}
if (iv != null) {
params.put("iv", iv);
}
params.put("inputData", inputData);
return params;
}
/**
* 组装对称加密/解密的响应VO
*/
private SymmetryEncryptVo buildSymmetryEncryptVo(JSONObject data) {
SymmetryEncryptVo vo = new SymmetryEncryptVo();
vo.setOutData(data.getString("outData"));
vo.setIvData(data.getString("ivData"));
return vo;
}
/**
* HSM接口公共调用方法(含data非空校验)
*
* @param apiPath 接口路径
* @param params 请求参数
* @param apiName 接口名称,用于日志和异常信息
* @return 响应中的data节点
*/
private JSONObject callHsmApi(String apiPath, JSONObject params, String apiName) {
JSONObject jsonObject = doPost(apiPath, params, apiName);
JSONObject data = jsonObject.getJSONObject("data");
if (data == null) {
throw new BaseException("HSM" + apiName + "响应data为空");
}
return data;
}
/**
* HSM接口底层HTTP调用(POST + 状态码校验 + code校验)
*
* @param apiPath 接口路径
* @param params 请求参数
* @param apiName 接口名称,用于日志和异常信息
* @return 完整响应JSON
*/
private JSONObject doPost(String apiPath, JSONObject params, String apiName) {
try {
String url = passwordPartyConfig.getBaseUrl() + apiPath;
log.info("HSM{}请求URL: {}, 参数: {}", apiName, url, params);
HttpResponse response = HttpRequest.post(url)
.header("Content-Type", "application/json;charset=utf-8")
.body(params.toJSONString())
.timeout(10000)
.execute();
if (!response.isOk()) {
response.close();
throw new BaseException("HSM" + apiName + "接口调用失败,HTTP状态码:" + response.getStatus());
}
String result = response.body();
response.close();
log.info("HSM{}响应: {}", apiName, result);
JSONObject jsonObject = JSONObject.parseObject(result);
int code = jsonObject.getIntValue("code");
if (code != 0) {
throw new BaseException("HSM" + apiName + "失败: " + jsonObject.getString("msg"));
}
return jsonObject;
} catch (BaseException e) {
throw e;
} catch (Exception e) {
log.error("HSM{}异常", apiName, e);
throw new BaseException("HSM" + apiName + "异常: " + e.getMessage());
}
}
}

3
zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml

@ -351,6 +351,9 @@ srnwg:
# paasId: szsfj_zhifajianduma_001
# paasToken: ENC(0Cg8Vtpay+3hwRm+vJB0utikr4dXcEguwSaxlbTWkgQ4parB8pN2rL2xrhQ8eUd5)
--- # 密码服务平台(HSM)
password-party:
base-url: http://localhost:8080
--- # XSS过滤配置
xss:
# 开启XSS过滤

6
zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-prod.yml

@ -278,6 +278,8 @@ justauth:
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea
--- # XSS过滤配置
xss:
# 开启XSS过滤
@ -339,6 +341,10 @@ sms:
#市监代理页面
sjView: http://10.132.108.3/sjWeb
--- # 密码服务平台(HSM)
password-party:
base-url: http://localhost:8080
--- # 业务数据双向同步配置
business-data-sync:
# 是否启用同步功能

Loading…
Cancel
Save