fix:备份恢复密钥
This commit is contained in:
@@ -260,12 +260,13 @@ Open:
|
|||||||
- `POST /api/v1/resource-restores/{taskId}/apply`
|
- `POST /api/v1/resource-restores/{taskId}/apply`
|
||||||
- `GET /api/v1/resource-restores/latest`
|
- `GET /api/v1/resource-restores/latest`
|
||||||
|
|
||||||
恢复任务创建接口只完成验签、解密、展开和计划生成;前端只做一次“确认恢复”,确认后先调用创建接口,返回 `READY_TO_APPLY` 后立即调用 apply 接口进入停机恢复。TMS 重启后前端可调用 latest 接口展示最近一次恢复时间、结果和失败原因。
|
恢复任务创建接口只完成验签、解密、展开和计划生成;前端只做一次“确认恢复”,确认后先调用创建接口,返回 `READY_TO_APPLY` 后立即调用 apply 接口进入停机恢复。也就是用户确认后调用 apply 接口进入停机恢复,不再做第二次确认。TMS 重启后前端可调用 latest 接口展示最近一次恢复时间、结果和失败原因。
|
||||||
|
|
||||||
前后端字段、状态枚举和联调流程见 `docs/openapi/resource-backup-restore-frontend-integration.md`。
|
前后端字段、状态枚举和联调流程见 `docs/openapi/resource-backup-restore-frontend-integration.md`。
|
||||||
|
|
||||||
当前实现边界:
|
当前实现边界:
|
||||||
- 资源备份包输出为 `.tmsbak`,包内包含 `manifest.json`、`envelope.json`、`payload.enc`、`signature.sig`
|
- 资源备份包输出为 `.tmsbak`,包内包含 `manifest.json`、`envelope.json`、`payload.enc`、`signature.sig`
|
||||||
|
- `payload.enc` 加解密使用 PCIe 密码卡 SDF 会话密钥流程:备份时通过 `SDF_GenerateKeyWithIPK_ECC` 生成内部 ECC 公钥包裹的会话密钥并用 `SDF_Encrypt` 加密 payload;恢复时通过 `SDF_ImportKeyWithISK_ECC` 导入包裹会话密钥并用 `SDF_Decrypt` 解密 payload。`envelope.json` 保持 `version=1`,只记录 `payloadAlg`、`ivBase64` 和 `wrappedSessionKeyBase64`,`keyIndex/keyBits/algId` 由系统固定约定。
|
||||||
- 当前第一版备份/恢复只覆盖最小资源集:
|
- 当前第一版备份/恢复只覆盖最小资源集:
|
||||||
- `TMS_CONFIG`
|
- `TMS_CONFIG`
|
||||||
- `TMS_DB`
|
- `TMS_DB`
|
||||||
@@ -278,7 +279,7 @@ Open:
|
|||||||
- `USER_KEY`(从实体证书 `KeyEntity.keyIdx` 采集用户密钥备份)
|
- `USER_KEY`(从实体证书 `KeyEntity.keyIdx` 采集用户密钥备份)
|
||||||
- `MQ_REPLAY`
|
- `MQ_REPLAY`
|
||||||
- 预检阶段只做包头解析、指纹匹配和验签,不提前解密完整 payload
|
- 预检阶段只做包头解析、指纹匹配和验签,不提前解密完整 payload
|
||||||
- 恢复任务创建时 Java 完成验签、PCIe 解密、payload 展开、用户密钥恢复和恢复路径白名单校验
|
- 恢复任务创建时 Java 完成验签、PCIe/SDF 解密、payload 展开、用户密钥恢复和恢复路径白名单校验
|
||||||
- Java 会生成 shell 友好的 `restore-files.tsv`、`restore-databases.tsv`、`restore-mq.tsv`
|
- Java 会生成 shell 友好的 `restore-files.tsv`、`restore-databases.tsv`、`restore-mq.tsv`
|
||||||
- 创建恢复任务后启动 `apply-resource-backup.sh --work-dir <workDir>`,由 shell 执行停服务、文件覆盖、SQL 导入、MQ replay、起服务和健康检查
|
- 创建恢复任务后启动 `apply-resource-backup.sh --work-dir <workDir>`,由 shell 执行停服务、文件覆盖、SQL 导入、MQ replay、起服务和健康检查
|
||||||
- Java 启动恢复脚本前会在工作目录生成 `restore-env.sh`、`restore-stop-commands.tsv`、`restore-start-commands.tsv`;停启服务命令来自 `tms.backup.restore-stop-commands` / `tms.backup.restore-start-commands`
|
- Java 启动恢复脚本前会在工作目录生成 `restore-env.sh`、`restore-stop-commands.tsv`、`restore-start-commands.tsv`;停启服务命令来自 `tms.backup.restore-stop-commands` / `tms.backup.restore-start-commands`
|
||||||
@@ -298,7 +299,7 @@ Open:
|
|||||||
- `db/<orgCode>.sql`(标准/间参收发器包内存在时,导回同名机构库)
|
- `db/<orgCode>.sql`(标准/间参收发器包内存在时,导回同名机构库)
|
||||||
- `TMS_DB` 备份默认通过 `tms.backup.tms-db-excluded-tables` 排除角色、授权、会话和审计表,恢复时不会覆盖新机器安全状态
|
- `TMS_DB` 备份默认通过 `tms.backup.tms-db-excluded-tables` 排除角色、授权、会话和审计表,恢复时不会覆盖新机器安全状态
|
||||||
- `MQ_REPLAY` 通过 `replay-mq.sh` 消费 `mq/mq-restore-context.json`,也可用 `MQ_REPLAY_COMMAND` 委派给现场脚本
|
- `MQ_REPLAY` 通过 `replay-mq.sh` 消费 `mq/mq-restore-context.json`,也可用 `MQ_REPLAY_COMMAND` 委派给现场脚本
|
||||||
- Java 启动恢复脚本前会在工作目录生成 `restore-env.sh`,传递数据库连接、恢复脚本路径、MQ replay 脚本路径和健康检查配置
|
- Java 启动恢复脚本前会在工作目录生成 `restore-env.sh`,传递数据库连接、恢复脚本路径、MQ replay 脚本路径和健康检查配置;健康检查会按配置重试等待 TMS 真正启动完成
|
||||||
|
|
||||||
关键配置:
|
关键配置:
|
||||||
- `tms.backup.output-dir`
|
- `tms.backup.output-dir`
|
||||||
@@ -319,6 +320,8 @@ Open:
|
|||||||
- `tms.backup.cmep-database-name`
|
- `tms.backup.cmep-database-name`
|
||||||
- `tms.backup.health-check-url`
|
- `tms.backup.health-check-url`
|
||||||
- `tms.backup.health-check-timeout-seconds`
|
- `tms.backup.health-check-timeout-seconds`
|
||||||
|
- `tms.backup.health-check-max-wait-seconds`
|
||||||
|
- `tms.backup.health-check-retry-interval-seconds`
|
||||||
|
|
||||||
恢复前置条件:
|
恢复前置条件:
|
||||||
- LMK、IK、数据存储加密密钥对、数据真实性保护密钥对需先通过 UKey 恢复
|
- LMK、IK、数据存储加密密钥对、数据真实性保护密钥对需先通过 UKey 恢复
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ Content-Disposition: attachment; filename="RBKP-20260429-160000-ABCDEF.tmsbak"
|
|||||||
- `precheckPassed` 是后端聚合结果,当前等价于 `keysetMatched=true`、`signatureValid=true`、`compatible=true` 且 `blockingIssues` 为空
|
- `precheckPassed` 是后端聚合结果,当前等价于 `keysetMatched=true`、`signatureValid=true`、`compatible=true` 且 `blockingIssues` 为空
|
||||||
- `payloadDecrypted=false` 是正常值,预检阶段不解密完整 payload
|
- `payloadDecrypted=false` 是正常值,预检阶段不解密完整 payload
|
||||||
- 真正解密、展开 payload、路径白名单校验发生在创建恢复任务阶段
|
- 真正解密、展开 payload、路径白名单校验发生在创建恢复任务阶段
|
||||||
|
- `payload.enc` 的实际加解密由后端通过 PCIe 密码卡 SDF 会话密钥流程完成,前端不需要传递 `keyIndex/keyBits/algId`
|
||||||
|
|
||||||
### 4.8 创建恢复任务
|
### 4.8 创建恢复任务
|
||||||
|
|
||||||
@@ -380,7 +381,7 @@ Content-Disposition: attachment; filename="RBKP-20260429-160000-ABCDEF.tmsbak"
|
|||||||
- `status=READY_TO_APPLY`:立即调用 apply,不再弹确认框
|
- `status=READY_TO_APPLY`:立即调用 apply,不再弹确认框
|
||||||
- 其它状态:展示异常提示,不调用 apply
|
- 其它状态:展示异常提示,不调用 apply
|
||||||
|
|
||||||
后端在该阶段会完成验签、解密、payload 展开、用户密钥恢复、路径白名单校验和恢复计划文件生成,但不会停止 TMS。
|
后端在该阶段会完成验签、PCIe/SDF 解密、payload 展开、用户密钥恢复、路径白名单校验和恢复计划文件生成,但不会停止 TMS。
|
||||||
|
|
||||||
### 4.9 执行恢复任务
|
### 4.9 执行恢复任务
|
||||||
|
|
||||||
|
|||||||
@@ -161,12 +161,37 @@ health_check() {
|
|||||||
}
|
}
|
||||||
command -v curl >/dev/null 2>&1 || fail "curl command not found for health check" "HEALTH_CHECK"
|
command -v curl >/dev/null 2>&1 || fail "curl command not found for health check" "HEALTH_CHECK"
|
||||||
local timeout="${HEALTH_CHECK_TIMEOUT_SECONDS:-5}"
|
local timeout="${HEALTH_CHECK_TIMEOUT_SECONDS:-5}"
|
||||||
local response
|
local max_wait="${HEALTH_CHECK_MAX_WAIT_SECONDS:-180}"
|
||||||
response="$(curl -fsS --max-time "${timeout}" "${url}")" || fail "health check request failed" "HEALTH_CHECK"
|
local interval="${HEALTH_CHECK_RETRY_INTERVAL_SECONDS:-5}"
|
||||||
if [[ "${response}" != *"UP"* ]]; then
|
[[ "${max_wait}" =~ ^[0-9]+$ ]] || max_wait=180
|
||||||
fail "health check did not return UP" "HEALTH_CHECK"
|
[[ "${interval}" =~ ^[0-9]+$ ]] || interval=5
|
||||||
fi
|
local start_time
|
||||||
log "health check passed"
|
start_time="$(date +%s)"
|
||||||
|
local attempt=1
|
||||||
|
local response=""
|
||||||
|
local curl_output
|
||||||
|
while true; do
|
||||||
|
curl_output="$(curl -fsS --max-time "${timeout}" "${url}" 2>&1)" && response="${curl_output}" || {
|
||||||
|
log "[WARN] health check attempt ${attempt} failed: ${curl_output}"
|
||||||
|
response=""
|
||||||
|
}
|
||||||
|
if [[ "${response}" == *"UP"* ]]; then
|
||||||
|
log "health check passed after ${attempt} attempt(s)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -n "${response}" ]]; then
|
||||||
|
log "[WARN] health check attempt ${attempt} did not return UP: ${response}"
|
||||||
|
fi
|
||||||
|
local now
|
||||||
|
now="$(date +%s)"
|
||||||
|
if (( now - start_time >= max_wait )); then
|
||||||
|
fail "health check request failed after ${max_wait}s" "HEALTH_CHECK"
|
||||||
|
fi
|
||||||
|
if (( interval > 0 )); then
|
||||||
|
sleep "${interval}"
|
||||||
|
fi
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir -p "${WORK_DIR}"
|
mkdir -p "${WORK_DIR}"
|
||||||
|
|||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.cisd.tms.integration.crypto.pcie.model;
|
||||||
|
|
||||||
|
public class EccSessionKeyCryptoSelfTestRequest {
|
||||||
|
|
||||||
|
private int keyIndex = 1;
|
||||||
|
private int keyBits = 128;
|
||||||
|
private int algId;
|
||||||
|
private byte[] iv;
|
||||||
|
private byte[] data;
|
||||||
|
private int encOutBufferSize = 4096;
|
||||||
|
private int decOutBufferSize = 4096;
|
||||||
|
|
||||||
|
public int getKeyIndex() {
|
||||||
|
return keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyIndex(int keyIndex) {
|
||||||
|
this.keyIndex = keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyBits() {
|
||||||
|
return keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyBits(int keyBits) {
|
||||||
|
this.keyBits = keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlgId() {
|
||||||
|
return algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgId(int algId) {
|
||||||
|
this.algId = algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getIv() {
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIv(byte[] iv) {
|
||||||
|
this.iv = iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(byte[] data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEncOutBufferSize() {
|
||||||
|
return encOutBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncOutBufferSize(int encOutBufferSize) {
|
||||||
|
this.encOutBufferSize = encOutBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDecOutBufferSize() {
|
||||||
|
return decOutBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecOutBufferSize(int decOutBufferSize) {
|
||||||
|
this.decOutBufferSize = decOutBufferSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
package com.cisd.tms.integration.crypto.pcie.model;
|
||||||
|
|
||||||
|
public class EccSessionKeyCryptoSelfTestResult {
|
||||||
|
|
||||||
|
private int keyIndex;
|
||||||
|
private int keyBits;
|
||||||
|
private int algId;
|
||||||
|
private byte[] iv;
|
||||||
|
private byte[] wrappedSessionKey;
|
||||||
|
private byte[] encryptedData;
|
||||||
|
private byte[] decryptedData;
|
||||||
|
private boolean matched;
|
||||||
|
|
||||||
|
public int getKeyIndex() {
|
||||||
|
return keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyIndex(int keyIndex) {
|
||||||
|
this.keyIndex = keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyBits() {
|
||||||
|
return keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyBits(int keyBits) {
|
||||||
|
this.keyBits = keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlgId() {
|
||||||
|
return algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgId(int algId) {
|
||||||
|
this.algId = algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getIv() {
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIv(byte[] iv) {
|
||||||
|
this.iv = iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getWrappedSessionKey() {
|
||||||
|
return wrappedSessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWrappedSessionKey(byte[] wrappedSessionKey) {
|
||||||
|
this.wrappedSessionKey = wrappedSessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEncryptedData() {
|
||||||
|
return encryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncryptedData(byte[] encryptedData) {
|
||||||
|
this.encryptedData = encryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getDecryptedData() {
|
||||||
|
return decryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecryptedData(byte[] decryptedData) {
|
||||||
|
this.decryptedData = decryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMatched() {
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMatched(boolean matched) {
|
||||||
|
this.matched = matched;
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package com.cisd.tms.integration.crypto.pcie.model;
|
||||||
|
|
||||||
|
public class EccSessionKeyDecryptRequest {
|
||||||
|
|
||||||
|
private int keyIndex = 1;
|
||||||
|
private int algId;
|
||||||
|
private byte[] iv;
|
||||||
|
private byte[] wrappedSessionKey;
|
||||||
|
private byte[] encryptedData;
|
||||||
|
private int outBufferSize;
|
||||||
|
|
||||||
|
public int getKeyIndex() {
|
||||||
|
return keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyIndex(int keyIndex) {
|
||||||
|
this.keyIndex = keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlgId() {
|
||||||
|
return algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgId(int algId) {
|
||||||
|
this.algId = algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getIv() {
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIv(byte[] iv) {
|
||||||
|
this.iv = iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getWrappedSessionKey() {
|
||||||
|
return wrappedSessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWrappedSessionKey(byte[] wrappedSessionKey) {
|
||||||
|
this.wrappedSessionKey = wrappedSessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEncryptedData() {
|
||||||
|
return encryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncryptedData(byte[] encryptedData) {
|
||||||
|
this.encryptedData = encryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutBufferSize() {
|
||||||
|
return outBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutBufferSize(int outBufferSize) {
|
||||||
|
this.outBufferSize = outBufferSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.cisd.tms.integration.crypto.pcie.model;
|
||||||
|
|
||||||
|
public class EccSessionKeyDecryptResult {
|
||||||
|
|
||||||
|
private byte[] decryptedData;
|
||||||
|
|
||||||
|
public byte[] getDecryptedData() {
|
||||||
|
return decryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecryptedData(byte[] decryptedData) {
|
||||||
|
this.decryptedData = decryptedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package com.cisd.tms.integration.crypto.pcie.model;
|
||||||
|
|
||||||
|
public class EccSessionKeyEncryptRequest {
|
||||||
|
|
||||||
|
private int keyIndex = 1;
|
||||||
|
private int keyBits = 128;
|
||||||
|
private int algId;
|
||||||
|
private byte[] iv;
|
||||||
|
private byte[] data;
|
||||||
|
private int outBufferSize;
|
||||||
|
|
||||||
|
public int getKeyIndex() {
|
||||||
|
return keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyIndex(int keyIndex) {
|
||||||
|
this.keyIndex = keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyBits() {
|
||||||
|
return keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyBits(int keyBits) {
|
||||||
|
this.keyBits = keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlgId() {
|
||||||
|
return algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgId(int algId) {
|
||||||
|
this.algId = algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getIv() {
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIv(byte[] iv) {
|
||||||
|
this.iv = iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(byte[] data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutBufferSize() {
|
||||||
|
return outBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutBufferSize(int outBufferSize) {
|
||||||
|
this.outBufferSize = outBufferSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package com.cisd.tms.integration.crypto.pcie.model;
|
||||||
|
|
||||||
|
public class EccSessionKeyEncryptResult {
|
||||||
|
|
||||||
|
private int keyIndex;
|
||||||
|
private int keyBits;
|
||||||
|
private int algId;
|
||||||
|
private byte[] iv;
|
||||||
|
private byte[] wrappedSessionKey;
|
||||||
|
private byte[] encryptedData;
|
||||||
|
|
||||||
|
public int getKeyIndex() {
|
||||||
|
return keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyIndex(int keyIndex) {
|
||||||
|
this.keyIndex = keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyBits() {
|
||||||
|
return keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyBits(int keyBits) {
|
||||||
|
this.keyBits = keyBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlgId() {
|
||||||
|
return algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlgId(int algId) {
|
||||||
|
this.algId = algId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getIv() {
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIv(byte[] iv) {
|
||||||
|
this.iv = iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getWrappedSessionKey() {
|
||||||
|
return wrappedSessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWrappedSessionKey(byte[] wrappedSessionKey) {
|
||||||
|
this.wrappedSessionKey = wrappedSessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEncryptedData() {
|
||||||
|
return encryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncryptedData(byte[] encryptedData) {
|
||||||
|
this.encryptedData = encryptedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import com.sun.jna.Structure;
|
|||||||
import com.sun.jna.ptr.IntByReference;
|
import com.sun.jna.ptr.IntByReference;
|
||||||
import com.sun.jna.ptr.PointerByReference;
|
import com.sun.jna.ptr.PointerByReference;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@@ -22,6 +23,8 @@ public class JnaPcieCryptoService implements PcieCryptoService {
|
|||||||
private static final int IK_KEY_TYPE_AUTH = 2;
|
private static final int IK_KEY_TYPE_AUTH = 2;
|
||||||
private static final int HMAC_MAX_LENGTH = 64;
|
private static final int HMAC_MAX_LENGTH = 64;
|
||||||
private static final int ECC_CIPHER_PREFIX_LENGTH = (EccRefPublicKey.MAX_LEN * 2) + 32 + Integer.BYTES;
|
private static final int ECC_CIPHER_PREFIX_LENGTH = (EccRefPublicKey.MAX_LEN * 2) + 32 + Integer.BYTES;
|
||||||
|
private static final int SYMMETRIC_BLOCK_SIZE = 16;
|
||||||
|
private static final int SDF_SYMMETRIC_CHUNK_SIZE = 4096;
|
||||||
private static final String DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD = "88888888";
|
private static final String DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD = "88888888";
|
||||||
private static final byte[] DEFAULT_SM2_USER_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8);
|
private static final byte[] DEFAULT_SM2_USER_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
@@ -1010,6 +1013,166 @@ public class JnaPcieCryptoService implements PcieCryptoService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EccSessionKeyEncryptResult encryptWithInternalEccSessionKey(EccSessionKeyEncryptRequest request) {
|
||||||
|
EccSessionKeyEncryptRequest req = requireRequest("request", request);
|
||||||
|
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
|
||||||
|
int keyBits = requirePositive("keyBits", req.getKeyBits());
|
||||||
|
int algId = req.getAlgId() > 0 ? requireValidAlgId("algId", req.getAlgId()) : Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
byte[] data = requireBytes("data", req.getData());
|
||||||
|
int outBufferSize = requirePositive("outBufferSize", req.getOutBufferSize());
|
||||||
|
|
||||||
|
return sessionTemplate.withSession("SDF_GenerateKeyWithIPK_ECC+SDF_Encrypt", (lib, deviceHandle, sessionHandle) -> {
|
||||||
|
byte[] baseIv = resolveSymmetricIv(lib, sessionHandle, algId, req.getIv());
|
||||||
|
byte[] encryptInput = requiresPkcs7Padding(algId) ? applyPkcs7Padding(data, 16) : data;
|
||||||
|
EccCipher wrappedSessionKey = new EccCipher();
|
||||||
|
PointerByReference generatedHandleRef = new PointerByReference();
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_GenerateKeyWithIPK_ECC",
|
||||||
|
lib.SDF_GenerateKeyWithIPK_ECC(sessionHandle, keyIndex, keyBits, wrappedSessionKey, generatedHandleRef)
|
||||||
|
);
|
||||||
|
Pointer generatedHandle = requireKeyHandle("SDF_GenerateKeyWithIPK_ECC", generatedHandleRef.getValue());
|
||||||
|
try {
|
||||||
|
EccSessionKeyEncryptResult result = new EccSessionKeyEncryptResult();
|
||||||
|
result.setKeyIndex(keyIndex);
|
||||||
|
result.setKeyBits(keyBits);
|
||||||
|
result.setAlgId(algId);
|
||||||
|
result.setIv(copyNullable(baseIv));
|
||||||
|
result.setWrappedSessionKey(toEccCipherBytes("SDF_GenerateKeyWithIPK_ECC", wrappedSessionKey));
|
||||||
|
result.setEncryptedData(encryptSymmetricInChunks(lib, sessionHandle, generatedHandle,
|
||||||
|
algId, baseIv, encryptInput, Math.max(outBufferSize, encryptInput.length)));
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
destroyKeyHandle(lib, sessionHandle, generatedHandle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EccSessionKeyDecryptResult decryptWithInternalEccSessionKey(EccSessionKeyDecryptRequest request) {
|
||||||
|
EccSessionKeyDecryptRequest req = requireRequest("request", request);
|
||||||
|
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
|
||||||
|
int algId = req.getAlgId() > 0 ? requireValidAlgId("algId", req.getAlgId()) : Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
EccCipher wrappedSessionKey = toEccCipher("wrappedSessionKey", req.getWrappedSessionKey());
|
||||||
|
byte[] encryptedData = requireBytes("encryptedData", req.getEncryptedData());
|
||||||
|
int outBufferSize = requirePositive("outBufferSize", req.getOutBufferSize());
|
||||||
|
|
||||||
|
return sessionTemplate.withSession("SDF_ImportKeyWithISK_ECC+SDF_Decrypt", (lib, deviceHandle, sessionHandle) -> {
|
||||||
|
byte[] password = DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD.getBytes(StandardCharsets.UTF_8);
|
||||||
|
boolean privateAccessOpened = false;
|
||||||
|
Pointer importedHandle = null;
|
||||||
|
try {
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_GetPrivateKeyAccessRight",
|
||||||
|
lib.SDF_GetPrivateKeyAccessRight(sessionHandle, keyIndex, password, password.length)
|
||||||
|
);
|
||||||
|
privateAccessOpened = true;
|
||||||
|
|
||||||
|
PointerByReference importedHandleRef = new PointerByReference();
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_ImportKeyWithISK_ECC",
|
||||||
|
lib.SDF_ImportKeyWithISK_ECC(sessionHandle, keyIndex, wrappedSessionKey, importedHandleRef)
|
||||||
|
);
|
||||||
|
importedHandle = requireKeyHandle("SDF_ImportKeyWithISK_ECC", importedHandleRef.getValue());
|
||||||
|
|
||||||
|
byte[] decryptedOutput = decryptSymmetricInChunks(lib, sessionHandle, importedHandle,
|
||||||
|
algId, req.getIv(), encryptedData, Math.max(outBufferSize, encryptedData.length));
|
||||||
|
EccSessionKeyDecryptResult result = new EccSessionKeyDecryptResult();
|
||||||
|
result.setDecryptedData(requiresPkcs7Padding(algId) ? removePkcs7Padding(decryptedOutput, 16) : decryptedOutput);
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
destroyKeyHandle(lib, sessionHandle, importedHandle);
|
||||||
|
} finally {
|
||||||
|
if (privateAccessOpened) {
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_ReleasePrivateKeyAccessRight",
|
||||||
|
lib.SDF_ReleasePrivateKeyAccessRight(sessionHandle, keyIndex)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
wipe(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EccSessionKeyCryptoSelfTestResult eccSessionKeyEncryptDecryptSelfTest(EccSessionKeyCryptoSelfTestRequest request) {
|
||||||
|
EccSessionKeyCryptoSelfTestRequest req = requireRequest("request", request);
|
||||||
|
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
|
||||||
|
int keyBits = requirePositive("keyBits", req.getKeyBits());
|
||||||
|
int algId = req.getAlgId() > 0 ? requireValidAlgId("algId", req.getAlgId()) : Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
byte[] data = requireBytes("data", req.getData());
|
||||||
|
int encOutBufferSize = requirePositive("encOutBufferSize", req.getEncOutBufferSize());
|
||||||
|
int decOutBufferSize = requirePositive("decOutBufferSize", req.getDecOutBufferSize());
|
||||||
|
|
||||||
|
return sessionTemplate.withSession("SDF_GenerateKeyWithIPK_ECC+self-test", (lib, deviceHandle, sessionHandle) -> {
|
||||||
|
byte[] baseIv = resolveSymmetricIv(lib, sessionHandle, algId, req.getIv());
|
||||||
|
byte[] encryptInput = requiresPkcs7Padding(algId) ? applyPkcs7Padding(data, 16) : data;
|
||||||
|
EccCipher wrappedSessionKey = new EccCipher();
|
||||||
|
PointerByReference generatedHandleRef = new PointerByReference();
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_GenerateKeyWithIPK_ECC",
|
||||||
|
lib.SDF_GenerateKeyWithIPK_ECC(sessionHandle, keyIndex, keyBits, wrappedSessionKey, generatedHandleRef)
|
||||||
|
);
|
||||||
|
Pointer generatedHandle = requireKeyHandle("SDF_GenerateKeyWithIPK_ECC", generatedHandleRef.getValue());
|
||||||
|
byte[] wrappedSessionKeyBytes = toEccCipherBytes("SDF_GenerateKeyWithIPK_ECC", wrappedSessionKey);
|
||||||
|
|
||||||
|
byte[] encryptedData;
|
||||||
|
try {
|
||||||
|
encryptedData = encryptSymmetricInChunks(lib, sessionHandle, generatedHandle,
|
||||||
|
algId, baseIv, encryptInput, Math.max(encOutBufferSize, encryptInput.length));
|
||||||
|
} finally {
|
||||||
|
destroyKeyHandle(lib, sessionHandle, generatedHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] password = DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD.getBytes(StandardCharsets.UTF_8);
|
||||||
|
boolean privateAccessOpened = false;
|
||||||
|
Pointer importedHandle = null;
|
||||||
|
try {
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_GetPrivateKeyAccessRight",
|
||||||
|
lib.SDF_GetPrivateKeyAccessRight(sessionHandle, keyIndex, password, password.length)
|
||||||
|
);
|
||||||
|
privateAccessOpened = true;
|
||||||
|
|
||||||
|
PointerByReference importedHandleRef = new PointerByReference();
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_ImportKeyWithISK_ECC",
|
||||||
|
lib.SDF_ImportKeyWithISK_ECC(sessionHandle, keyIndex, wrappedSessionKey, importedHandleRef)
|
||||||
|
);
|
||||||
|
importedHandle = requireKeyHandle("SDF_ImportKeyWithISK_ECC", importedHandleRef.getValue());
|
||||||
|
|
||||||
|
byte[] decryptedOutput = decryptSymmetricInChunks(lib, sessionHandle, importedHandle,
|
||||||
|
algId, baseIv, encryptedData, Math.max(decOutBufferSize, encryptedData.length));
|
||||||
|
byte[] decryptedData = requiresPkcs7Padding(algId) ? removePkcs7Padding(decryptedOutput, 16) : decryptedOutput;
|
||||||
|
|
||||||
|
EccSessionKeyCryptoSelfTestResult result = new EccSessionKeyCryptoSelfTestResult();
|
||||||
|
result.setKeyIndex(keyIndex);
|
||||||
|
result.setKeyBits(keyBits);
|
||||||
|
result.setAlgId(algId);
|
||||||
|
result.setIv(copyNullable(baseIv));
|
||||||
|
result.setWrappedSessionKey(wrappedSessionKeyBytes);
|
||||||
|
result.setEncryptedData(encryptedData);
|
||||||
|
result.setDecryptedData(decryptedData);
|
||||||
|
result.setMatched(Arrays.equals(data, decryptedData));
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
destroyKeyHandle(lib, sessionHandle, importedHandle);
|
||||||
|
} finally {
|
||||||
|
if (privateAccessOpened) {
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_ReleasePrivateKeyAccessRight",
|
||||||
|
lib.SDF_ReleasePrivateKeyAccessRight(sessionHandle, keyIndex)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
wipe(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BackupDataResult exchangeDigitEnvelopeBaseOnRsa(ExchangeDigitEnvelopeRsaRequest request) {
|
public BackupDataResult exchangeDigitEnvelopeBaseOnRsa(ExchangeDigitEnvelopeRsaRequest request) {
|
||||||
ExchangeDigitEnvelopeRsaRequest req = requireRequest("request", request);
|
ExchangeDigitEnvelopeRsaRequest req = requireRequest("request", request);
|
||||||
@@ -1952,6 +2115,196 @@ public class JnaPcieCryptoService implements PcieCryptoService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] encryptSymmetricInChunks(
|
||||||
|
PcieNativeLibrary lib,
|
||||||
|
Pointer sessionHandle,
|
||||||
|
Pointer keyHandle,
|
||||||
|
int algId,
|
||||||
|
byte[] iv,
|
||||||
|
byte[] data,
|
||||||
|
int outBufferSize
|
||||||
|
) {
|
||||||
|
if (!shouldChunkSymmetricPayload(algId, data.length)) {
|
||||||
|
byte[] out = new byte[Math.max(outBufferSize, data.length)];
|
||||||
|
IntByReference outLen = new IntByReference(out.length);
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_Encrypt",
|
||||||
|
lib.SDF_Encrypt(sessionHandle, keyHandle, algId, copyNullable(iv),
|
||||||
|
data, data.length, out, outLen)
|
||||||
|
);
|
||||||
|
return toBackupResult("SDF_Encrypt", out, outLen.getValue()).getData();
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream(data.length);
|
||||||
|
byte[] currentIv = copyNullable(iv);
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < data.length) {
|
||||||
|
int chunkLength = nextSymmetricChunkLength(data.length - offset);
|
||||||
|
byte[] chunk = Arrays.copyOfRange(data, offset, offset + chunkLength);
|
||||||
|
byte[] out = new byte[chunkLength];
|
||||||
|
IntByReference outLen = new IntByReference(out.length);
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_Encrypt",
|
||||||
|
lib.SDF_Encrypt(sessionHandle, keyHandle, algId, copyNullable(currentIv),
|
||||||
|
chunk, chunk.length, out, outLen)
|
||||||
|
);
|
||||||
|
byte[] encryptedChunk = toBackupResult("SDF_Encrypt", out, outLen.getValue()).getData();
|
||||||
|
output.write(encryptedChunk, 0, encryptedChunk.length);
|
||||||
|
currentIv = nextSymmetricIv(algId, currentIv, encryptedChunk);
|
||||||
|
offset += chunkLength;
|
||||||
|
}
|
||||||
|
return output.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] decryptSymmetricInChunks(
|
||||||
|
PcieNativeLibrary lib,
|
||||||
|
Pointer sessionHandle,
|
||||||
|
Pointer keyHandle,
|
||||||
|
int algId,
|
||||||
|
byte[] iv,
|
||||||
|
byte[] data,
|
||||||
|
int outBufferSize
|
||||||
|
) {
|
||||||
|
if (!shouldChunkSymmetricPayload(algId, data.length)) {
|
||||||
|
byte[] out = new byte[Math.max(outBufferSize, data.length)];
|
||||||
|
IntByReference outLen = new IntByReference(out.length);
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_Decrypt",
|
||||||
|
lib.SDF_Decrypt(sessionHandle, keyHandle, algId, copyNullable(iv),
|
||||||
|
data, data.length, out, outLen)
|
||||||
|
);
|
||||||
|
return toBackupResult("SDF_Decrypt", out, outLen.getValue()).getData();
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream(data.length);
|
||||||
|
byte[] currentIv = copyNullable(iv);
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < data.length) {
|
||||||
|
int chunkLength = nextSymmetricChunkLength(data.length - offset);
|
||||||
|
byte[] chunk = Arrays.copyOfRange(data, offset, offset + chunkLength);
|
||||||
|
byte[] nextIv = nextSymmetricIv(algId, currentIv, chunk);
|
||||||
|
byte[] out = new byte[chunkLength];
|
||||||
|
IntByReference outLen = new IntByReference(out.length);
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_Decrypt",
|
||||||
|
lib.SDF_Decrypt(sessionHandle, keyHandle, algId, copyNullable(currentIv),
|
||||||
|
chunk, chunk.length, out, outLen)
|
||||||
|
);
|
||||||
|
byte[] decryptedChunk = toBackupResult("SDF_Decrypt", out, outLen.getValue()).getData();
|
||||||
|
output.write(decryptedChunk, 0, decryptedChunk.length);
|
||||||
|
currentIv = nextIv;
|
||||||
|
offset += chunkLength;
|
||||||
|
}
|
||||||
|
return output.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean shouldChunkSymmetricPayload(int algId, int length) {
|
||||||
|
return length > SDF_SYMMETRIC_CHUNK_SIZE && supportsBlockAlignedChunking(algId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean supportsBlockAlignedChunking(int algId) {
|
||||||
|
return algId == Gm0018AlgorithmIds.SM1_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM1_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int nextSymmetricChunkLength(int remaining) {
|
||||||
|
if (remaining <= SDF_SYMMETRIC_CHUNK_SIZE) {
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
return SDF_SYMMETRIC_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] nextSymmetricIv(int algId, byte[] currentIv, byte[] cipherChunk) {
|
||||||
|
if (!isCbcAlg(algId)) {
|
||||||
|
return copyNullable(currentIv);
|
||||||
|
}
|
||||||
|
if (cipherChunk == null || cipherChunk.length < SYMMETRIC_BLOCK_SIZE) {
|
||||||
|
throw new PcieCryptoException("SDF_Encrypt", -1, "CBC分块输出长度非法");
|
||||||
|
}
|
||||||
|
return Arrays.copyOfRange(cipherChunk, cipherChunk.length - SYMMETRIC_BLOCK_SIZE, cipherChunk.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCbcAlg(int algId) {
|
||||||
|
return algId == Gm0018AlgorithmIds.SM1_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pointer requireKeyHandle(String apiName, Pointer keyHandle) {
|
||||||
|
if (keyHandle == null) {
|
||||||
|
throw new PcieCryptoException(apiName, -1, "原生接口返回空会话密钥句柄");
|
||||||
|
}
|
||||||
|
return keyHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] resolveSymmetricIv(PcieNativeLibrary lib, Pointer sessionHandle, int algId, byte[] iv) {
|
||||||
|
if (iv != null && iv.length > 0) {
|
||||||
|
return Arrays.copyOf(iv, iv.length);
|
||||||
|
}
|
||||||
|
if (requiresSymmetricIv(algId)) {
|
||||||
|
byte[] randomIv = new byte[16];
|
||||||
|
sessionTemplate.ensureSuccess(
|
||||||
|
"SDF_GenerateRandom",
|
||||||
|
lib.SDF_GenerateRandom(sessionHandle, randomIv.length, randomIv)
|
||||||
|
);
|
||||||
|
return randomIv;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean requiresSymmetricIv(int algId) {
|
||||||
|
return algId == Gm0018AlgorithmIds.SM1_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM1_CFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM1_OFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_OFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_OFB;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean requiresPkcs7Padding(int algId) {
|
||||||
|
return algId == Gm0018AlgorithmIds.SM1_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM1_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] applyPkcs7Padding(byte[] data, int blockSize) {
|
||||||
|
int paddingLength = blockSize - (data.length % blockSize);
|
||||||
|
if (paddingLength == 0) {
|
||||||
|
paddingLength = blockSize;
|
||||||
|
}
|
||||||
|
byte[] padded = Arrays.copyOf(data, data.length + paddingLength);
|
||||||
|
Arrays.fill(padded, data.length, padded.length, (byte) paddingLength);
|
||||||
|
return padded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] removePkcs7Padding(byte[] data, int blockSize) {
|
||||||
|
if (data.length == 0 || data.length % blockSize != 0) {
|
||||||
|
throw new PcieCryptoException("SDF_Decrypt", -1, "解密后的填充数据长度非法");
|
||||||
|
}
|
||||||
|
int paddingLength = data[data.length - 1] & 0xFF;
|
||||||
|
if (paddingLength < 1 || paddingLength > blockSize || paddingLength > data.length) {
|
||||||
|
throw new PcieCryptoException("SDF_Decrypt", -1, "解密后的填充数据非法");
|
||||||
|
}
|
||||||
|
for (int i = data.length - paddingLength; i < data.length; i++) {
|
||||||
|
if ((data[i] & 0xFF) != paddingLength) {
|
||||||
|
throw new PcieCryptoException("SDF_Decrypt", -1, "解密后的填充数据非法");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Arrays.copyOf(data, data.length - paddingLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] copyNullable(byte[] value) {
|
||||||
|
return value == null ? null : Arrays.copyOf(value, value.length);
|
||||||
|
}
|
||||||
|
|
||||||
private static byte[] fileNameBytes(String fileName) {
|
private static byte[] fileNameBytes(String fileName) {
|
||||||
if (fileName == null || fileName.isBlank()) {
|
if (fileName == null || fileName.isBlank()) {
|
||||||
throw new IllegalArgumentException("fileName不能为空");
|
throw new IllegalArgumentException("fileName不能为空");
|
||||||
|
|||||||
+128
@@ -20,6 +20,12 @@ import com.cisd.tms.integration.crypto.pcie.model.DeviceStatusResult;
|
|||||||
import com.cisd.tms.integration.crypto.pcie.model.DigestRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.DigestRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccExternalDecryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccExternalDecryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccExternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccExternalEncryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccExternalSignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccExternalSignRequest;
|
||||||
@@ -466,6 +472,71 @@ public class MockPcieCryptoService implements PcieCryptoService {
|
|||||||
requireNonEmptyBytes("encryptedKeyBlob", req.getEncryptedKeyBlob());
|
requireNonEmptyBytes("encryptedKeyBlob", req.getEncryptedKeyBlob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EccSessionKeyEncryptResult encryptWithInternalEccSessionKey(EccSessionKeyEncryptRequest request) {
|
||||||
|
EccSessionKeyEncryptRequest req = requireRequest("request", request);
|
||||||
|
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
|
||||||
|
int keyBits = requirePositive("keyBits", req.getKeyBits());
|
||||||
|
int algId = req.getAlgId() > 0 ? requirePositive("algId", req.getAlgId()) : Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
byte[] data = requireBytes("data", req.getData());
|
||||||
|
byte[] iv = resolveMockIv(algId, req.getIv());
|
||||||
|
byte[] encrypted = requiresPkcs7Padding(algId) ? applyPkcs7Padding(data, 16) : Arrays.copyOf(data, data.length);
|
||||||
|
for (int i = 0; i < encrypted.length; i++) {
|
||||||
|
encrypted[i] = (byte) (encrypted[i] ^ 0x5A);
|
||||||
|
}
|
||||||
|
|
||||||
|
EccSessionKeyEncryptResult result = new EccSessionKeyEncryptResult();
|
||||||
|
result.setKeyIndex(keyIndex);
|
||||||
|
result.setKeyBits(keyBits);
|
||||||
|
result.setAlgId(algId);
|
||||||
|
result.setIv(iv);
|
||||||
|
result.setWrappedSessionKey(mockBackup(132, 0x50).getData());
|
||||||
|
result.setEncryptedData(encrypted);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EccSessionKeyDecryptResult decryptWithInternalEccSessionKey(EccSessionKeyDecryptRequest request) {
|
||||||
|
EccSessionKeyDecryptRequest req = requireRequest("request", request);
|
||||||
|
requireNonNegative("keyIndex", req.getKeyIndex());
|
||||||
|
int algId = req.getAlgId() > 0 ? requirePositive("algId", req.getAlgId()) : Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
requireNonEmptyBytes("wrappedSessionKey", req.getWrappedSessionKey());
|
||||||
|
byte[] encryptedData = requireBytes("encryptedData", req.getEncryptedData());
|
||||||
|
byte[] decrypted = Arrays.copyOf(encryptedData, encryptedData.length);
|
||||||
|
for (int i = 0; i < decrypted.length; i++) {
|
||||||
|
decrypted[i] = (byte) (decrypted[i] ^ 0x5A);
|
||||||
|
}
|
||||||
|
EccSessionKeyDecryptResult result = new EccSessionKeyDecryptResult();
|
||||||
|
result.setDecryptedData(requiresPkcs7Padding(algId) ? removePkcs7Padding(decrypted, 16) : decrypted);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EccSessionKeyCryptoSelfTestResult eccSessionKeyEncryptDecryptSelfTest(EccSessionKeyCryptoSelfTestRequest request) {
|
||||||
|
EccSessionKeyCryptoSelfTestRequest req = requireRequest("request", request);
|
||||||
|
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
|
||||||
|
int keyBits = requirePositive("keyBits", req.getKeyBits());
|
||||||
|
int algId = req.getAlgId() > 0 ? requirePositive("algId", req.getAlgId()) : Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
byte[] data = requireBytes("data", req.getData());
|
||||||
|
byte[] iv = resolveMockIv(algId, req.getIv());
|
||||||
|
byte[] encryptInput = requiresPkcs7Padding(algId) ? applyPkcs7Padding(data, 16) : data;
|
||||||
|
byte[] encrypted = Arrays.copyOf(encryptInput, encryptInput.length);
|
||||||
|
for (int i = 0; i < encrypted.length; i++) {
|
||||||
|
encrypted[i] = (byte) (encrypted[i] ^ 0x5A);
|
||||||
|
}
|
||||||
|
|
||||||
|
EccSessionKeyCryptoSelfTestResult result = new EccSessionKeyCryptoSelfTestResult();
|
||||||
|
result.setKeyIndex(keyIndex);
|
||||||
|
result.setKeyBits(keyBits);
|
||||||
|
result.setAlgId(algId);
|
||||||
|
result.setIv(iv);
|
||||||
|
result.setWrappedSessionKey(mockBackup(132, 0x50).getData());
|
||||||
|
result.setEncryptedData(encrypted);
|
||||||
|
result.setDecryptedData(Arrays.copyOf(data, data.length));
|
||||||
|
result.setMatched(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BackupDataResult exchangeDigitEnvelopeBaseOnRsa(ExchangeDigitEnvelopeRsaRequest request) {
|
public BackupDataResult exchangeDigitEnvelopeBaseOnRsa(ExchangeDigitEnvelopeRsaRequest request) {
|
||||||
ExchangeDigitEnvelopeRsaRequest req = requireRequest("request", request);
|
ExchangeDigitEnvelopeRsaRequest req = requireRequest("request", request);
|
||||||
@@ -938,6 +1009,63 @@ public class MockPcieCryptoService implements PcieCryptoService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] resolveMockIv(int algId, byte[] iv) {
|
||||||
|
if (iv != null && iv.length > 0) {
|
||||||
|
return Arrays.copyOf(iv, iv.length);
|
||||||
|
}
|
||||||
|
if (requiresSymmetricIv(algId)) {
|
||||||
|
return generateRandom(16);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean requiresSymmetricIv(int algId) {
|
||||||
|
return algId == Gm0018AlgorithmIds.SM1_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM1_CFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM1_OFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_OFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CFB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_OFB;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean requiresPkcs7Padding(int algId) {
|
||||||
|
return algId == Gm0018AlgorithmIds.SM1_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM1_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SSF33_CBC
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_ECB
|
||||||
|
|| algId == Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] applyPkcs7Padding(byte[] data, int blockSize) {
|
||||||
|
int paddingLength = blockSize - (data.length % blockSize);
|
||||||
|
if (paddingLength == 0) {
|
||||||
|
paddingLength = blockSize;
|
||||||
|
}
|
||||||
|
byte[] padded = Arrays.copyOf(data, data.length + paddingLength);
|
||||||
|
Arrays.fill(padded, data.length, padded.length, (byte) paddingLength);
|
||||||
|
return padded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] removePkcs7Padding(byte[] data, int blockSize) {
|
||||||
|
if (data.length == 0 || data.length % blockSize != 0) {
|
||||||
|
throw new IllegalArgumentException("解密后的填充数据长度非法");
|
||||||
|
}
|
||||||
|
int paddingLength = data[data.length - 1] & 0xFF;
|
||||||
|
if (paddingLength < 1 || paddingLength > blockSize || paddingLength > data.length) {
|
||||||
|
throw new IllegalArgumentException("解密后的填充数据非法");
|
||||||
|
}
|
||||||
|
for (int i = data.length - paddingLength; i < data.length; i++) {
|
||||||
|
if ((data[i] & 0xFF) != paddingLength) {
|
||||||
|
throw new IllegalArgumentException("解密后的填充数据非法");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Arrays.copyOf(data, data.length - paddingLength);
|
||||||
|
}
|
||||||
|
|
||||||
private static int requireUserKeyType(int keyType) {
|
private static int requireUserKeyType(int keyType) {
|
||||||
if (keyType != PcieUserKeyType.SIGN.getCode() && keyType != PcieUserKeyType.ENC.getCode()) {
|
if (keyType != PcieUserKeyType.SIGN.getCode() && keyType != PcieUserKeyType.ENC.getCode()) {
|
||||||
throw new IllegalArgumentException("keyType必须为0(SIGN)或1(ENC)");
|
throw new IllegalArgumentException("keyType必须为0(SIGN)或1(ENC)");
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ import com.cisd.tms.integration.crypto.pcie.model.DeviceStatusResult;
|
|||||||
import com.cisd.tms.integration.crypto.pcie.model.DigestRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.DigestRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccExternalDecryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccExternalDecryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccExternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccExternalEncryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccExternalSignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccExternalSignRequest;
|
||||||
@@ -341,6 +347,21 @@ public interface PcieCryptoService {
|
|||||||
*/
|
*/
|
||||||
void importKeyWithIskEcc(ImportKeyWithIskEccRequest request);
|
void importKeyWithIskEcc(ImportKeyWithIskEccRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成内部 ECC 公钥包裹的会话密钥,使用原会话密钥句柄加密数据,再导入包裹密钥并解密数据。
|
||||||
|
*/
|
||||||
|
EccSessionKeyCryptoSelfTestResult eccSessionKeyEncryptDecryptSelfTest(EccSessionKeyCryptoSelfTestRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内部 ECC 公钥生成会话密钥,并通过 SDF_Encrypt 加密数据。
|
||||||
|
*/
|
||||||
|
EccSessionKeyEncryptResult encryptWithInternalEccSessionKey(EccSessionKeyEncryptRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内部 ECC 私钥导入包裹会话密钥,并通过 SDF_Decrypt 解密数据。
|
||||||
|
*/
|
||||||
|
EccSessionKeyDecryptResult decryptWithInternalEccSessionKey(EccSessionKeyDecryptRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行基于 RSA 的数字信封交换。
|
* 执行基于 RSA 的数字信封交换。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public class ResourceBackupProperties {
|
|||||||
private String cmepDatabaseName = "CMEP";
|
private String cmepDatabaseName = "CMEP";
|
||||||
private String healthCheckUrl = "http://127.0.0.1:8080/actuator/health";
|
private String healthCheckUrl = "http://127.0.0.1:8080/actuator/health";
|
||||||
private int healthCheckTimeoutSeconds = 5;
|
private int healthCheckTimeoutSeconds = 5;
|
||||||
|
private int healthCheckMaxWaitSeconds = 180;
|
||||||
|
private int healthCheckRetryIntervalSeconds = 5;
|
||||||
private String restoreApplyScriptPath = "/home/tms/bin/resource-restore/apply-resource-backup.sh";
|
private String restoreApplyScriptPath = "/home/tms/bin/resource-restore/apply-resource-backup.sh";
|
||||||
private List<ConfiguredRestoreCommand> restoreStopCommands = List.of();
|
private List<ConfiguredRestoreCommand> restoreStopCommands = List.of();
|
||||||
private List<ConfiguredRestoreCommand> restoreStartCommands = List.of();
|
private List<ConfiguredRestoreCommand> restoreStartCommands = List.of();
|
||||||
@@ -144,6 +146,22 @@ public class ResourceBackupProperties {
|
|||||||
this.healthCheckTimeoutSeconds = healthCheckTimeoutSeconds;
|
this.healthCheckTimeoutSeconds = healthCheckTimeoutSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getHealthCheckMaxWaitSeconds() {
|
||||||
|
return healthCheckMaxWaitSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHealthCheckMaxWaitSeconds(int healthCheckMaxWaitSeconds) {
|
||||||
|
this.healthCheckMaxWaitSeconds = healthCheckMaxWaitSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHealthCheckRetryIntervalSeconds() {
|
||||||
|
return healthCheckRetryIntervalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHealthCheckRetryIntervalSeconds(int healthCheckRetryIntervalSeconds) {
|
||||||
|
this.healthCheckRetryIntervalSeconds = healthCheckRetryIntervalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
public String getRestoreApplyScriptPath() {
|
public String getRestoreApplyScriptPath() {
|
||||||
return restoreApplyScriptPath;
|
return restoreApplyScriptPath;
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-80
@@ -1,31 +1,22 @@
|
|||||||
package com.cisd.tms.modules.backup.crypto;
|
package com.cisd.tms.modules.backup.crypto;
|
||||||
|
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.*;
|
import com.cisd.tms.integration.crypto.pcie.model.*;
|
||||||
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
||||||
import java.security.Security;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCryptoService {
|
public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCryptoService {
|
||||||
|
|
||||||
private static final String PROVIDER = "BC";
|
private static final int PAYLOAD_SESSION_KEY_BITS = 128;
|
||||||
|
private static final int PAYLOAD_ALG_ID = Gm0018AlgorithmIds.SM4_CBC;
|
||||||
|
private static final String PAYLOAD_ALG_NAME = "SDF_SM4_CBC";
|
||||||
private final PcieCryptoService pcieCryptoService;
|
private final PcieCryptoService pcieCryptoService;
|
||||||
|
|
||||||
static {
|
|
||||||
if (Security.getProvider(PROVIDER) == null) {
|
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceEnvelopeCryptoServiceImpl(PcieCryptoService pcieCryptoService) {
|
public ResourceEnvelopeCryptoServiceImpl(PcieCryptoService pcieCryptoService) {
|
||||||
this.pcieCryptoService = pcieCryptoService;
|
this.pcieCryptoService = pcieCryptoService;
|
||||||
}
|
}
|
||||||
@@ -35,38 +26,30 @@ public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCrypto
|
|||||||
if (payloadBytes == null) {
|
if (payloadBytes == null) {
|
||||||
throw new IllegalArgumentException("payloadBytes不能为空");
|
throw new IllegalArgumentException("payloadBytes不能为空");
|
||||||
}
|
}
|
||||||
// 资源包正文使用随机 SM4 DEK 加密,再由用户密钥把 DEK 封装成 envelope。
|
|
||||||
// 这样大 payload 仍走对称加密,ECC 只负责保护一次性数据密钥。
|
|
||||||
byte[] sm4Key = randomBytes(16);
|
|
||||||
byte[] iv = randomBytes(16);
|
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", PROVIDER);
|
// 资源包正文加密走 SDF 会话密钥流程:内部 ECC 公钥生成会话密钥,SDF_Encrypt 加密 payload。
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sm4Key, "SM4"), new IvParameterSpec(iv));
|
EccSessionKeyEncryptRequest encryptRequest = new EccSessionKeyEncryptRequest();
|
||||||
|
|
||||||
// 用户密钥只负责封装随机 DEK;真正的大 payload 仍走对称加密,避免直接把大文件送进 ECC 接口。
|
|
||||||
EccInternalEncryptRequest encryptRequest = new EccInternalEncryptRequest();
|
|
||||||
encryptRequest.setKeyIndex(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_INDEX);
|
encryptRequest.setKeyIndex(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_INDEX);
|
||||||
encryptRequest.setData(sm4Key);
|
encryptRequest.setKeyBits(PAYLOAD_SESSION_KEY_BITS);
|
||||||
BackupDataResult wrappedDek = pcieCryptoService.sdfeInternalEncryptEcc(encryptRequest);
|
encryptRequest.setAlgId(PAYLOAD_ALG_ID);
|
||||||
|
encryptRequest.setData(payloadBytes);
|
||||||
|
encryptRequest.setOutBufferSize(payloadBytes.length + 16);
|
||||||
|
EccSessionKeyEncryptResult encryptResult = pcieCryptoService.encryptWithInternalEccSessionKey(encryptRequest);
|
||||||
|
|
||||||
Map<String, Object> envelope = new LinkedHashMap<>();
|
Map<String, Object> envelope = new LinkedHashMap<>();
|
||||||
envelope.put("version", 1);
|
envelope.put("version", 1);
|
||||||
envelope.put("payloadAlg", "SM4-CBC");
|
envelope.put("payloadAlg", PAYLOAD_ALG_NAME);
|
||||||
envelope.put("keyWrapAlg", "SDFE_InternalEncrypt_ECC");
|
envelope.put("ivBase64", Base64.getEncoder().encodeToString(nullToEmpty(encryptResult.getIv())));
|
||||||
envelope.put("ivBase64", Base64.getEncoder().encodeToString(iv));
|
envelope.put("wrappedSessionKeyBase64", Base64.getEncoder().encodeToString(nullToEmpty(encryptResult.getWrappedSessionKey())));
|
||||||
envelope.put("wrappedDekBase64", Base64.getEncoder().encodeToString(normalize(wrappedDek)));
|
|
||||||
envelope.put("dekLength", sm4Key.length);
|
|
||||||
|
|
||||||
ResourceSealedPayload sealedPayload = new ResourceSealedPayload();
|
ResourceSealedPayload sealedPayload = new ResourceSealedPayload();
|
||||||
sealedPayload.setCipherBytes(cipher.doFinal(payloadBytes));
|
sealedPayload.setCipherBytes(nullToEmpty(encryptResult.getEncryptedData()));
|
||||||
sealedPayload.setEnvelope(envelope);
|
sealedPayload.setEnvelope(envelope);
|
||||||
sealedPayload.setStorageEncKeyFingerprint(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_FINGERPRINT);
|
sealedPayload.setStorageEncKeyFingerprint(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_FINGERPRINT);
|
||||||
sealedPayload.setAuthenticityKeyFingerprint(ResourceCryptoKeyConstants.AUTHENTICITY_KEY_FINGERPRINT);
|
sealedPayload.setAuthenticityKeyFingerprint(ResourceCryptoKeyConstants.AUTHENTICITY_KEY_FINGERPRINT);
|
||||||
return sealedPayload;
|
return sealedPayload;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new IllegalStateException("资源备份载荷加封失败", ex);
|
throw new IllegalStateException("资源备份载荷加封失败:" + rootCauseMessage(ex), ex);
|
||||||
} finally {
|
|
||||||
wipe(sm4Key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,30 +95,28 @@ public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCrypto
|
|||||||
throw new IllegalArgumentException("signature不能为空");
|
throw new IllegalArgumentException("signature不能为空");
|
||||||
}
|
}
|
||||||
verify(dataToVerify, signature);
|
verify(dataToVerify, signature);
|
||||||
|
if (!Integer.valueOf(1).equals(envelope.get("version"))) {
|
||||||
byte[] wrappedDek = Base64.getDecoder().decode(String.valueOf(envelope.get("wrappedDekBase64")));
|
throw new IllegalArgumentException("envelope版本不支持");
|
||||||
int dekLength = parseInt(envelope.get("dekLength"), 16);
|
}
|
||||||
// 先验签,再解数字信封,再解 payload;顺序固定,避免篡改包先进入明文处理路径。
|
if (!PAYLOAD_ALG_NAME.equals(String.valueOf(envelope.get("payloadAlg")))) {
|
||||||
UserKeyDecryptRequest decryptRequest = new UserKeyDecryptRequest();
|
throw new IllegalArgumentException("payload加密算法不支持");
|
||||||
decryptRequest.setKeyIndex(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_INDEX);
|
|
||||||
decryptRequest.setCipherBlob(wrappedDek);
|
|
||||||
byte[] sm4Key = normalizeAndWipe(pcieCryptoService.userKeyDecrypt(decryptRequest));
|
|
||||||
byte[] iv = Base64.getDecoder().decode(String.valueOf(envelope.get("ivBase64")));
|
|
||||||
try {
|
|
||||||
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", PROVIDER);
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sm4Key, "SM4"), new IvParameterSpec(iv));
|
|
||||||
return cipher.doFinal(cipherBytes);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new IllegalStateException("资源备份载荷解封失败", ex);
|
|
||||||
} finally {
|
|
||||||
wipe(sm4Key);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] randomBytes(int size) {
|
byte[] wrappedSessionKey = Base64.getDecoder().decode(String.valueOf(envelope.get("wrappedSessionKeyBase64")));
|
||||||
byte[] bytes = new byte[size];
|
// 先验签,再导入会话密钥,最后 SDF_Decrypt 解 payload;顺序固定,避免篡改包先进入明文处理路径。
|
||||||
new java.security.SecureRandom().nextBytes(bytes);
|
EccSessionKeyDecryptRequest decryptRequest = new EccSessionKeyDecryptRequest();
|
||||||
return bytes;
|
decryptRequest.setKeyIndex(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_INDEX);
|
||||||
|
decryptRequest.setAlgId(PAYLOAD_ALG_ID);
|
||||||
|
decryptRequest.setWrappedSessionKey(wrappedSessionKey);
|
||||||
|
decryptRequest.setEncryptedData(cipherBytes);
|
||||||
|
decryptRequest.setIv(Base64.getDecoder().decode(String.valueOf(envelope.get("ivBase64"))));
|
||||||
|
decryptRequest.setOutBufferSize(cipherBytes.length);
|
||||||
|
try {
|
||||||
|
EccSessionKeyDecryptResult decryptResult = pcieCryptoService.decryptWithInternalEccSessionKey(decryptRequest);
|
||||||
|
return nullToEmpty(decryptResult.getDecryptedData());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalStateException("资源备份载荷解封失败:" + rootCauseMessage(ex), ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] sm3(byte[] data) {
|
private static byte[] sm3(byte[] data) {
|
||||||
@@ -158,33 +139,16 @@ public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCrypto
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] normalizeAndWipe(BackupDataResult result) {
|
private static byte[] nullToEmpty(byte[] bytes) {
|
||||||
try {
|
return bytes == null ? new byte[0] : bytes;
|
||||||
return normalize(result);
|
|
||||||
} finally {
|
|
||||||
if (result != null) {
|
|
||||||
wipe(result.getData());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int parseInt(Object value, int defaultValue) {
|
private static String rootCauseMessage(Throwable throwable) {
|
||||||
if (value == null) {
|
Throwable current = throwable;
|
||||||
return defaultValue;
|
while (current.getCause() != null) {
|
||||||
}
|
current = current.getCause();
|
||||||
if (value instanceof Number number) {
|
|
||||||
return number.intValue();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(String.valueOf(value));
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void wipe(byte[] bytes) {
|
|
||||||
if (bytes != null) {
|
|
||||||
Arrays.fill(bytes, (byte) 0);
|
|
||||||
}
|
}
|
||||||
|
String message = current.getMessage();
|
||||||
|
return message == null || message.isBlank() ? current.getClass().getSimpleName() : message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -20,19 +20,25 @@ public class LightweightRestoreApplierLauncher {
|
|||||||
private final String datasourceUrl;
|
private final String datasourceUrl;
|
||||||
private final String datasourceUsername;
|
private final String datasourceUsername;
|
||||||
private final String datasourcePassword;
|
private final String datasourcePassword;
|
||||||
|
private final String standardHomeDir;
|
||||||
|
private final String standardRunUser;
|
||||||
|
|
||||||
public LightweightRestoreApplierLauncher(
|
public LightweightRestoreApplierLauncher(
|
||||||
ResourceBackupProperties properties,
|
ResourceBackupProperties properties,
|
||||||
RestoreCommandInvoker invoker,
|
RestoreCommandInvoker invoker,
|
||||||
@Value("${spring.datasource.url:}") String datasourceUrl,
|
@Value("${spring.datasource.url:}") String datasourceUrl,
|
||||||
@Value("${spring.datasource.username:}") String datasourceUsername,
|
@Value("${spring.datasource.username:}") String datasourceUsername,
|
||||||
@Value("${spring.datasource.password:}") String datasourcePassword
|
@Value("${spring.datasource.password:}") String datasourcePassword,
|
||||||
|
@Value("${tms.init.executor.standard-home-dir:/home/cmep4i}") String standardHomeDir,
|
||||||
|
@Value("${tms.init.executor.standard-run-user:cmep4i}") String standardRunUser
|
||||||
) {
|
) {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.invoker = invoker;
|
this.invoker = invoker;
|
||||||
this.datasourceUrl = datasourceUrl;
|
this.datasourceUrl = datasourceUrl;
|
||||||
this.datasourceUsername = datasourceUsername;
|
this.datasourceUsername = datasourceUsername;
|
||||||
this.datasourcePassword = datasourcePassword;
|
this.datasourcePassword = datasourcePassword;
|
||||||
|
this.standardHomeDir = standardHomeDir;
|
||||||
|
this.standardRunUser = standardRunUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void launch(Path workDir) {
|
public void launch(Path workDir) {
|
||||||
@@ -115,7 +121,11 @@ public class LightweightRestoreApplierLauncher {
|
|||||||
values.put("DB_PASSWORD", trim(datasourcePassword));
|
values.put("DB_PASSWORD", trim(datasourcePassword));
|
||||||
values.put("HEALTH_CHECK_URL", trim(properties.getHealthCheckUrl()));
|
values.put("HEALTH_CHECK_URL", trim(properties.getHealthCheckUrl()));
|
||||||
values.put("HEALTH_CHECK_TIMEOUT_SECONDS", String.valueOf(properties.getHealthCheckTimeoutSeconds()));
|
values.put("HEALTH_CHECK_TIMEOUT_SECONDS", String.valueOf(properties.getHealthCheckTimeoutSeconds()));
|
||||||
|
values.put("HEALTH_CHECK_MAX_WAIT_SECONDS", String.valueOf(properties.getHealthCheckMaxWaitSeconds()));
|
||||||
|
values.put("HEALTH_CHECK_RETRY_INTERVAL_SECONDS", String.valueOf(properties.getHealthCheckRetryIntervalSeconds()));
|
||||||
values.put("RESTORE_APPLY_DELAY_SECONDS", "3");
|
values.put("RESTORE_APPLY_DELAY_SECONDS", "3");
|
||||||
|
values.put("STANDARD_HOME_DIR", trim(standardHomeDir));
|
||||||
|
values.put("STANDARD_RUN_USER", trim(standardRunUser));
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder("# generated by TMS resource restore\n");
|
StringBuilder builder = new StringBuilder("# generated by TMS resource restore\n");
|
||||||
for (Map.Entry<String, String> entry : values.entrySet()) {
|
for (Map.Entry<String, String> entry : values.entrySet()) {
|
||||||
|
|||||||
+17
-1
@@ -114,7 +114,7 @@ public class ResourceBackupServiceImpl implements ResourceBackupService {
|
|||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
entity.setStatus("FAILED");
|
entity.setStatus("FAILED");
|
||||||
entity.setErrorCode("PACKAGE_CREATE_FAILED");
|
entity.setErrorCode("PACKAGE_CREATE_FAILED");
|
||||||
entity.setErrorMessage(ex.getMessage());
|
entity.setErrorMessage(rootCauseAwareMessage(ex));
|
||||||
}
|
}
|
||||||
entity.setFinishTime(LocalDateTime.now());
|
entity.setFinishTime(LocalDateTime.now());
|
||||||
entity.setUpdateTime(entity.getFinishTime());
|
entity.setUpdateTime(entity.getFinishTime());
|
||||||
@@ -229,4 +229,20 @@ public class ResourceBackupServiceImpl implements ResourceBackupService {
|
|||||||
return "{}";
|
return "{}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String rootCauseAwareMessage(Throwable throwable) {
|
||||||
|
String message = throwable.getMessage();
|
||||||
|
Throwable current = throwable;
|
||||||
|
while (current.getCause() != null) {
|
||||||
|
current = current.getCause();
|
||||||
|
}
|
||||||
|
String rootMessage = current.getMessage();
|
||||||
|
if (rootMessage == null || rootMessage.isBlank() || rootMessage.equals(message)) {
|
||||||
|
return message == null || message.isBlank() ? current.getClass().getSimpleName() : message;
|
||||||
|
}
|
||||||
|
if (message == null || message.isBlank()) {
|
||||||
|
return rootMessage;
|
||||||
|
}
|
||||||
|
return message + ";原因:" + rootMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
|||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.ExchangeDigitEnvelopeEccRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.ExchangeDigitEnvelopeEccRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.ExchangeDigitEnvelopeRsaRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.ExchangeDigitEnvelopeRsaRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.ExportKekComponentRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.ExportKekComponentRequest;
|
||||||
@@ -401,6 +403,40 @@ public class CryptoCardController {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/session-key/ecc/internal-crypto-self-test")
|
||||||
|
@Operation(summary = "执行内部 ECC 会话密钥加解密自测", description = "使用索引 1 的内部 ECC 密钥生成并导入会话密钥,未传 IV 时使用密码卡随机数接口生成 IV,完成 SDF 加密和解密闭环。")
|
||||||
|
public ApiResponse<Map<String, Object>> eccSessionKeyCryptoSelfTest(
|
||||||
|
@Valid @RequestBody CryptoEccSessionKeyCryptoSelfTestInternalRequest request
|
||||||
|
) {
|
||||||
|
EccSessionKeyCryptoSelfTestRequest req = new EccSessionKeyCryptoSelfTestRequest();
|
||||||
|
req.setKeyIndex(1);
|
||||||
|
req.setKeyBits(128);
|
||||||
|
req.setAlgId(Gm0018AlgorithmIds.SM4_CBC);
|
||||||
|
req.setData(request.getPlainText().getBytes(StandardCharsets.UTF_8));
|
||||||
|
req.setEncOutBufferSize(4096);
|
||||||
|
req.setDecOutBufferSize(4096);
|
||||||
|
|
||||||
|
EccSessionKeyCryptoSelfTestResult result = pcieCryptoService.eccSessionKeyEncryptDecryptSelfTest(req);
|
||||||
|
byte[] wrappedSessionKey = result.getWrappedSessionKey() == null ? new byte[0] : result.getWrappedSessionKey();
|
||||||
|
byte[] encryptedData = result.getEncryptedData() == null ? new byte[0] : result.getEncryptedData();
|
||||||
|
byte[] decryptedData = result.getDecryptedData() == null ? new byte[0] : result.getDecryptedData();
|
||||||
|
|
||||||
|
Map<String, Object> payload = new LinkedHashMap<>();
|
||||||
|
payload.put("keyIndex", result.getKeyIndex());
|
||||||
|
payload.put("keyBits", result.getKeyBits());
|
||||||
|
payload.put("algId", result.getAlgId());
|
||||||
|
payload.put("ivBase64", result.getIv() == null ? null : Base64.getEncoder().encodeToString(result.getIv()));
|
||||||
|
payload.put("wrappedSessionKeyLength", wrappedSessionKey.length);
|
||||||
|
payload.put("wrappedSessionKeyBase64", Base64.getEncoder().encodeToString(wrappedSessionKey));
|
||||||
|
payload.put("encryptedDataLength", encryptedData.length);
|
||||||
|
payload.put("encryptedDataBase64", Base64.getEncoder().encodeToString(encryptedData));
|
||||||
|
payload.put("decryptedLength", decryptedData.length);
|
||||||
|
payload.put("decryptedBase64", Base64.getEncoder().encodeToString(decryptedData));
|
||||||
|
payload.put("decryptedText", new String(decryptedData, StandardCharsets.UTF_8));
|
||||||
|
payload.put("matched", result.isMatched());
|
||||||
|
return ApiResponse.success(payload);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/device/init-identify")
|
@PostMapping("/device/init-identify")
|
||||||
@Operation(summary = "初始化识别码", description = "使用旧 PIN 和新 PIN 初始化设备识别码。")
|
@Operation(summary = "初始化识别码", description = "使用旧 PIN 和新 PIN 初始化设备识别码。")
|
||||||
public ApiResponse<Void> initIdentify(@Valid @RequestBody CryptoInitIdentifyInternalRequest request) {
|
public ApiResponse<Void> initIdentify(@Valid @RequestBody CryptoInitIdentifyInternalRequest request) {
|
||||||
|
|||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.cisd.tms.modules.device.dto.crypto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
public class CryptoEccSessionKeyCryptoSelfTestInternalRequest {
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String plainText;
|
||||||
|
|
||||||
|
public String getPlainText() {
|
||||||
|
return plainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlainText(String plainText) {
|
||||||
|
this.plainText = plainText;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -315,6 +315,10 @@ tms:
|
|||||||
health-check-url: ${TMS_BACKUP_HEALTH_CHECK_URL:http://127.0.0.1:8080/actuator/health}
|
health-check-url: ${TMS_BACKUP_HEALTH_CHECK_URL:http://127.0.0.1:8080/actuator/health}
|
||||||
# 健康检查请求超时(秒)。
|
# 健康检查请求超时(秒)。
|
||||||
health-check-timeout-seconds: ${TMS_BACKUP_HEALTH_CHECK_TIMEOUT_SECONDS:5}
|
health-check-timeout-seconds: ${TMS_BACKUP_HEALTH_CHECK_TIMEOUT_SECONDS:5}
|
||||||
|
# 健康检查最大等待时间(秒);TMS 启动命令返回后,Spring Boot 仍可能需要一段时间才监听端口。
|
||||||
|
health-check-max-wait-seconds: ${TMS_BACKUP_HEALTH_CHECK_MAX_WAIT_SECONDS:180}
|
||||||
|
# 健康检查重试间隔(秒)。
|
||||||
|
health-check-retry-interval-seconds: ${TMS_BACKUP_HEALTH_CHECK_RETRY_INTERVAL_SECONDS:5}
|
||||||
mk:
|
mk:
|
||||||
init-identify:
|
init-identify:
|
||||||
# SDFE_InitIdentify 旧 PIN(Base64 编码);主密钥初始化接口会读取该值。
|
# SDFE_InitIdentify 旧 PIN(Base64 编码);主密钥初始化接口会读取该值。
|
||||||
|
|||||||
+290
@@ -14,6 +14,12 @@ import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
|||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.FirmwareInfoResult;
|
import com.cisd.tms.integration.crypto.pcie.model.FirmwareInfoResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.IkSignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.IkSignRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest;
|
||||||
@@ -27,8 +33,10 @@ import com.sun.jna.Pointer;
|
|||||||
import com.sun.jna.ptr.IntByReference;
|
import com.sun.jna.ptr.IntByReference;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
@@ -238,6 +246,288 @@ class JnaPcieCryptoServiceTest {
|
|||||||
Mockito.verify(sessionTemplate).withSession(Mockito.eq("SDFE_VerifyIK"), Mockito.any());
|
Mockito.verify(sessionTemplate).withSession(Mockito.eq("SDFE_VerifyIK"), Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldGenerateEncryptImportAndDecryptWithInternalEccSessionKey() {
|
||||||
|
PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class);
|
||||||
|
CryptoCardProperties properties = new CryptoCardProperties();
|
||||||
|
JnaPcieCryptoService service = new JnaPcieCryptoService(sessionTemplate, properties);
|
||||||
|
EccSessionKeyCryptoSelfTestRequest request = new EccSessionKeyCryptoSelfTestRequest();
|
||||||
|
request.setKeyIndex(1);
|
||||||
|
request.setKeyBits(128);
|
||||||
|
request.setAlgId(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC);
|
||||||
|
request.setData("hello".getBytes(StandardCharsets.UTF_8));
|
||||||
|
request.setEncOutBufferSize(64);
|
||||||
|
request.setDecOutBufferSize(64);
|
||||||
|
|
||||||
|
Mockito.when(sessionTemplate.withSession(Mockito.eq("SDF_GenerateKeyWithIPK_ECC+self-test"), Mockito.any()))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
PcieSessionTemplate.SessionCallback<EccSessionKeyCryptoSelfTestResult> callback =
|
||||||
|
(PcieSessionTemplate.SessionCallback<EccSessionKeyCryptoSelfTestResult>) invocation.getArgument(1);
|
||||||
|
PcieNativeLibrary lib = Mockito.mock(PcieNativeLibrary.class);
|
||||||
|
Pointer session = Pointer.createConstant(2);
|
||||||
|
Pointer generatedHandle = Pointer.createConstant(101);
|
||||||
|
Pointer importedHandle = Pointer.createConstant(202);
|
||||||
|
byte[] paddedPlain = new byte[] {
|
||||||
|
0x68, 0x65, 0x6C, 0x6C,
|
||||||
|
0x6F, 0x0B, 0x0B, 0x0B,
|
||||||
|
0x0B, 0x0B, 0x0B, 0x0B,
|
||||||
|
0x0B, 0x0B, 0x0B, 0x0B
|
||||||
|
};
|
||||||
|
byte[] cipher = new byte[] {
|
||||||
|
0x21, 0x43, 0x65, 0x01,
|
||||||
|
0x02, 0x03, 0x04, 0x05,
|
||||||
|
0x06, 0x07, 0x08, 0x09,
|
||||||
|
0x0A, 0x0B, 0x0C, 0x0D
|
||||||
|
};
|
||||||
|
byte[] generatedIv = new byte[] {
|
||||||
|
0x10, 0x11, 0x12, 0x13,
|
||||||
|
0x14, 0x15, 0x16, 0x17,
|
||||||
|
0x18, 0x19, 0x1A, 0x1B,
|
||||||
|
0x1C, 0x1D, 0x1E, 0x1F
|
||||||
|
};
|
||||||
|
Mockito.when(lib.SDF_GenerateRandom(Mockito.eq(session), Mockito.eq(16), Mockito.any(byte[].class)))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
byte[] out = call.getArgument(2);
|
||||||
|
System.arraycopy(generatedIv, 0, out, 0, generatedIv.length);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.when(lib.SDF_GenerateKeyWithIPK_ECC(Mockito.eq(session), Mockito.eq(1), Mockito.eq(128),
|
||||||
|
Mockito.any(EccCipher.class), Mockito.any()))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
EccCipher wrapped = call.getArgument(3);
|
||||||
|
wrapped.l = 3;
|
||||||
|
wrapped.c[0] = 0x01;
|
||||||
|
wrapped.c[1] = 0x02;
|
||||||
|
wrapped.c[2] = 0x03;
|
||||||
|
com.sun.jna.ptr.PointerByReference handleRef = call.getArgument(4);
|
||||||
|
handleRef.setValue(generatedHandle);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.when(lib.SDF_Encrypt(Mockito.eq(session), Mockito.eq(generatedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
Mockito.any(), Mockito.any(), Mockito.eq(16), Mockito.any(), Mockito.any()))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
byte[] out = call.getArgument(6);
|
||||||
|
System.arraycopy(cipher, 0, out, 0, cipher.length);
|
||||||
|
IntByReference outLen = call.getArgument(7);
|
||||||
|
outLen.setValue(cipher.length);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.when(lib.SDF_GetPrivateKeyAccessRight(Mockito.eq(session), Mockito.eq(1), Mockito.any(), Mockito.anyInt()))
|
||||||
|
.thenReturn(0);
|
||||||
|
Mockito.when(lib.SDF_ImportKeyWithISK_ECC(Mockito.eq(session), Mockito.eq(1), Mockito.any(EccCipher.class), Mockito.any()))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
com.sun.jna.ptr.PointerByReference handleRef = call.getArgument(3);
|
||||||
|
handleRef.setValue(importedHandle);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.when(lib.SDF_Decrypt(Mockito.eq(session), Mockito.eq(importedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
Mockito.any(), Mockito.any(), Mockito.eq(16), Mockito.any(), Mockito.any()))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
byte[] out = call.getArgument(6);
|
||||||
|
System.arraycopy(paddedPlain, 0, out, 0, paddedPlain.length);
|
||||||
|
IntByReference outLen = call.getArgument(7);
|
||||||
|
outLen.setValue(paddedPlain.length);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.when(lib.SDF_ReleasePrivateKeyAccessRight(Mockito.eq(session), Mockito.eq(1))).thenReturn(0);
|
||||||
|
Mockito.when(lib.SDF_DestroyKey(Mockito.eq(session), Mockito.any())).thenReturn(0);
|
||||||
|
|
||||||
|
EccSessionKeyCryptoSelfTestResult result = callback.apply(lib, Pointer.createConstant(1), session);
|
||||||
|
|
||||||
|
InOrder inOrder = Mockito.inOrder(lib);
|
||||||
|
inOrder.verify(lib).SDF_GenerateRandom(Mockito.eq(session), Mockito.eq(16), Mockito.any(byte[].class));
|
||||||
|
inOrder.verify(lib).SDF_GenerateKeyWithIPK_ECC(Mockito.eq(session), Mockito.eq(1), Mockito.eq(128),
|
||||||
|
Mockito.any(EccCipher.class), Mockito.any());
|
||||||
|
inOrder.verify(lib).SDF_Encrypt(Mockito.eq(session), Mockito.eq(generatedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
Mockito.any(), Mockito.any(), Mockito.eq(16), Mockito.any(), Mockito.any());
|
||||||
|
inOrder.verify(lib).SDF_DestroyKey(session, generatedHandle);
|
||||||
|
inOrder.verify(lib).SDF_GetPrivateKeyAccessRight(Mockito.eq(session), Mockito.eq(1), Mockito.any(), Mockito.anyInt());
|
||||||
|
inOrder.verify(lib).SDF_ImportKeyWithISK_ECC(Mockito.eq(session), Mockito.eq(1), Mockito.any(EccCipher.class), Mockito.any());
|
||||||
|
inOrder.verify(lib).SDF_Decrypt(Mockito.eq(session), Mockito.eq(importedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
Mockito.any(), Mockito.any(), Mockito.eq(16), Mockito.any(), Mockito.any());
|
||||||
|
inOrder.verify(lib).SDF_DestroyKey(session, importedHandle);
|
||||||
|
inOrder.verify(lib).SDF_ReleasePrivateKeyAccessRight(session, 1);
|
||||||
|
ArgumentCaptor<byte[]> encryptIvCaptor = ArgumentCaptor.forClass(byte[].class);
|
||||||
|
ArgumentCaptor<byte[]> encryptDataCaptor = ArgumentCaptor.forClass(byte[].class);
|
||||||
|
ArgumentCaptor<byte[]> decryptIvCaptor = ArgumentCaptor.forClass(byte[].class);
|
||||||
|
Mockito.verify(lib).SDF_Encrypt(Mockito.eq(session), Mockito.eq(generatedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
encryptIvCaptor.capture(), encryptDataCaptor.capture(), Mockito.eq(16), Mockito.any(), Mockito.any());
|
||||||
|
Mockito.verify(lib).SDF_Decrypt(Mockito.eq(session), Mockito.eq(importedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
decryptIvCaptor.capture(), Mockito.any(), Mockito.eq(16), Mockito.any(), Mockito.any());
|
||||||
|
Assertions.assertArrayEquals(generatedIv, encryptIvCaptor.getValue());
|
||||||
|
Assertions.assertArrayEquals(generatedIv, decryptIvCaptor.getValue());
|
||||||
|
Assertions.assertArrayEquals(paddedPlain, encryptDataCaptor.getValue());
|
||||||
|
Assertions.assertArrayEquals(generatedIv, result.getIv());
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
EccSessionKeyCryptoSelfTestResult result = service.eccSessionKeyEncryptDecryptSelfTest(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, result.getKeyIndex());
|
||||||
|
Assertions.assertEquals(128, result.getKeyBits());
|
||||||
|
Assertions.assertArrayEquals(new byte[] {
|
||||||
|
0x10, 0x11, 0x12, 0x13,
|
||||||
|
0x14, 0x15, 0x16, 0x17,
|
||||||
|
0x18, 0x19, 0x1A, 0x1B,
|
||||||
|
0x1C, 0x1D, 0x1E, 0x1F
|
||||||
|
}, result.getIv());
|
||||||
|
Assertions.assertArrayEquals(new byte[] {
|
||||||
|
0x21, 0x43, 0x65, 0x01,
|
||||||
|
0x02, 0x03, 0x04, 0x05,
|
||||||
|
0x06, 0x07, 0x08, 0x09,
|
||||||
|
0x0A, 0x0B, 0x0C, 0x0D
|
||||||
|
}, result.getEncryptedData());
|
||||||
|
Assertions.assertArrayEquals("hello".getBytes(StandardCharsets.UTF_8), result.getDecryptedData());
|
||||||
|
Assertions.assertTrue(result.isMatched());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldEncryptLargePayloadWithInternalEccSessionKeyInBlocks() {
|
||||||
|
PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class);
|
||||||
|
CryptoCardProperties properties = new CryptoCardProperties();
|
||||||
|
JnaPcieCryptoService service = new JnaPcieCryptoService(sessionTemplate, properties);
|
||||||
|
AtomicReference<PcieNativeLibrary> libRef = new AtomicReference<>();
|
||||||
|
Pointer session = Pointer.createConstant(2);
|
||||||
|
Pointer generatedHandle = Pointer.createConstant(101);
|
||||||
|
EccSessionKeyEncryptRequest request = new EccSessionKeyEncryptRequest();
|
||||||
|
request.setKeyIndex(1);
|
||||||
|
request.setKeyBits(128);
|
||||||
|
request.setAlgId(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC);
|
||||||
|
byte[] plain = new byte[5000];
|
||||||
|
Arrays.fill(plain, (byte) 0x31);
|
||||||
|
request.setData(plain);
|
||||||
|
request.setOutBufferSize(plain.length + 16);
|
||||||
|
|
||||||
|
Mockito.when(sessionTemplate.withSession(Mockito.eq("SDF_GenerateKeyWithIPK_ECC+SDF_Encrypt"), Mockito.any()))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
PcieSessionTemplate.SessionCallback<EccSessionKeyEncryptResult> callback =
|
||||||
|
(PcieSessionTemplate.SessionCallback<EccSessionKeyEncryptResult>) invocation.getArgument(1);
|
||||||
|
PcieNativeLibrary lib = Mockito.mock(PcieNativeLibrary.class);
|
||||||
|
libRef.set(lib);
|
||||||
|
byte[] generatedIv = new byte[] {
|
||||||
|
0x10, 0x11, 0x12, 0x13,
|
||||||
|
0x14, 0x15, 0x16, 0x17,
|
||||||
|
0x18, 0x19, 0x1A, 0x1B,
|
||||||
|
0x1C, 0x1D, 0x1E, 0x1F
|
||||||
|
};
|
||||||
|
Mockito.when(lib.SDF_GenerateRandom(Mockito.eq(session), Mockito.eq(16), Mockito.any(byte[].class)))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
byte[] out = call.getArgument(2);
|
||||||
|
System.arraycopy(generatedIv, 0, out, 0, generatedIv.length);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.when(lib.SDF_GenerateKeyWithIPK_ECC(Mockito.eq(session), Mockito.eq(1), Mockito.eq(128),
|
||||||
|
Mockito.any(EccCipher.class), Mockito.any()))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
EccCipher wrapped = call.getArgument(3);
|
||||||
|
wrapped.l = 3;
|
||||||
|
wrapped.c[0] = 0x01;
|
||||||
|
wrapped.c[1] = 0x02;
|
||||||
|
wrapped.c[2] = 0x03;
|
||||||
|
com.sun.jna.ptr.PointerByReference handleRef = call.getArgument(4);
|
||||||
|
handleRef.setValue(generatedHandle);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.doAnswer(call -> {
|
||||||
|
int inputLength = call.getArgument(5);
|
||||||
|
if (inputLength > 4096) {
|
||||||
|
return 0x100001d;
|
||||||
|
}
|
||||||
|
byte[] input = call.getArgument(4);
|
||||||
|
byte[] out = call.getArgument(6);
|
||||||
|
System.arraycopy(input, 0, out, 0, inputLength);
|
||||||
|
IntByReference outLen = call.getArgument(7);
|
||||||
|
outLen.setValue(inputLength);
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
.when(lib).SDF_Encrypt(Mockito.any(), Mockito.any(),
|
||||||
|
Mockito.anyInt(), Mockito.any(), Mockito.any(), Mockito.anyInt(), Mockito.any(), Mockito.any());
|
||||||
|
Mockito.when(lib.SDF_DestroyKey(Mockito.eq(session), Mockito.eq(generatedHandle))).thenReturn(0);
|
||||||
|
return callback.apply(lib, Pointer.createConstant(1), session);
|
||||||
|
});
|
||||||
|
|
||||||
|
EccSessionKeyEncryptResult result = service.encryptWithInternalEccSessionKey(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(5008, result.getEncryptedData().length);
|
||||||
|
ArgumentCaptor<Integer> encryptLengthCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
Mockito.verify(libRef.get(), Mockito.atLeast(2)).SDF_Encrypt(Mockito.eq(session), Mockito.eq(generatedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
Mockito.any(), Mockito.any(), encryptLengthCaptor.capture(), Mockito.any(), Mockito.any());
|
||||||
|
Assertions.assertTrue(encryptLengthCaptor.getAllValues().stream().allMatch(length -> length <= 4096));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldDecryptLargePayloadWithInternalEccSessionKeyInBlocks() {
|
||||||
|
PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class);
|
||||||
|
CryptoCardProperties properties = new CryptoCardProperties();
|
||||||
|
JnaPcieCryptoService service = new JnaPcieCryptoService(sessionTemplate, properties);
|
||||||
|
AtomicReference<PcieNativeLibrary> libRef = new AtomicReference<>();
|
||||||
|
Pointer session = Pointer.createConstant(2);
|
||||||
|
Pointer importedHandle = Pointer.createConstant(202);
|
||||||
|
byte[] encryptedData = new byte[5008];
|
||||||
|
Arrays.fill(encryptedData, (byte) 0x31);
|
||||||
|
Arrays.fill(encryptedData, encryptedData.length - 8, encryptedData.length, (byte) 0x08);
|
||||||
|
|
||||||
|
EccSessionKeyDecryptRequest request = new EccSessionKeyDecryptRequest();
|
||||||
|
request.setKeyIndex(1);
|
||||||
|
request.setAlgId(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC);
|
||||||
|
request.setWrappedSessionKey(new byte[new EccCipher().size()]);
|
||||||
|
request.setEncryptedData(encryptedData);
|
||||||
|
request.setIv(new byte[16]);
|
||||||
|
request.setOutBufferSize(encryptedData.length);
|
||||||
|
|
||||||
|
Mockito.when(sessionTemplate.withSession(Mockito.eq("SDF_ImportKeyWithISK_ECC+SDF_Decrypt"), Mockito.any()))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
PcieSessionTemplate.SessionCallback<EccSessionKeyDecryptResult> callback =
|
||||||
|
(PcieSessionTemplate.SessionCallback<EccSessionKeyDecryptResult>) invocation.getArgument(1);
|
||||||
|
PcieNativeLibrary lib = Mockito.mock(PcieNativeLibrary.class);
|
||||||
|
libRef.set(lib);
|
||||||
|
Mockito.when(lib.SDF_GetPrivateKeyAccessRight(Mockito.eq(session), Mockito.eq(1), Mockito.any(), Mockito.anyInt()))
|
||||||
|
.thenReturn(0);
|
||||||
|
Mockito.when(lib.SDF_ImportKeyWithISK_ECC(Mockito.eq(session), Mockito.eq(1), Mockito.any(EccCipher.class), Mockito.any()))
|
||||||
|
.thenAnswer(call -> {
|
||||||
|
com.sun.jna.ptr.PointerByReference handleRef = call.getArgument(3);
|
||||||
|
handleRef.setValue(importedHandle);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
Mockito.doAnswer(call -> {
|
||||||
|
int inputLength = call.getArgument(5);
|
||||||
|
if (inputLength > 4096) {
|
||||||
|
return 0x100001d;
|
||||||
|
}
|
||||||
|
byte[] input = call.getArgument(4);
|
||||||
|
byte[] out = call.getArgument(6);
|
||||||
|
System.arraycopy(input, 0, out, 0, inputLength);
|
||||||
|
IntByReference outLen = call.getArgument(7);
|
||||||
|
outLen.setValue(inputLength);
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
.when(lib).SDF_Decrypt(Mockito.any(), Mockito.any(),
|
||||||
|
Mockito.anyInt(), Mockito.any(), Mockito.any(), Mockito.anyInt(), Mockito.any(), Mockito.any());
|
||||||
|
Mockito.when(lib.SDF_DestroyKey(Mockito.eq(session), Mockito.eq(importedHandle))).thenReturn(0);
|
||||||
|
Mockito.when(lib.SDF_ReleasePrivateKeyAccessRight(Mockito.eq(session), Mockito.eq(1))).thenReturn(0);
|
||||||
|
return callback.apply(lib, Pointer.createConstant(1), session);
|
||||||
|
});
|
||||||
|
|
||||||
|
EccSessionKeyDecryptResult result = service.decryptWithInternalEccSessionKey(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(5000, result.getDecryptedData().length);
|
||||||
|
ArgumentCaptor<Integer> decryptLengthCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
Mockito.verify(libRef.get(), Mockito.atLeast(2)).SDF_Decrypt(Mockito.eq(session), Mockito.eq(importedHandle),
|
||||||
|
Mockito.eq(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM4_CBC),
|
||||||
|
Mockito.any(), Mockito.any(), decryptLengthCaptor.capture(), Mockito.any(), Mockito.any());
|
||||||
|
Assertions.assertTrue(decryptLengthCaptor.getAllValues().stream().allMatch(length -> length <= 4096));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReadFirmwareInfoViaSdfeGetFirmwareInfo() {
|
void shouldReadFirmwareInfoViaSdfeGetFirmwareInfo() {
|
||||||
PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class);
|
PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class);
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ class ResourceBackupPropertiesTest {
|
|||||||
Assertions.assertTrue(properties.getTmsDbExcludedTables().contains("tms_role_account"));
|
Assertions.assertTrue(properties.getTmsDbExcludedTables().contains("tms_role_account"));
|
||||||
Assertions.assertTrue(properties.getTmsDbExcludedTables().contains("tms_role_ukey_binding"));
|
Assertions.assertTrue(properties.getTmsDbExcludedTables().contains("tms_role_ukey_binding"));
|
||||||
Assertions.assertTrue(properties.getTmsDbExcludedTables().contains("tms_auth_session"));
|
Assertions.assertTrue(properties.getTmsDbExcludedTables().contains("tms_auth_session"));
|
||||||
|
Assertions.assertEquals(180, properties.getHealthCheckMaxWaitSeconds());
|
||||||
|
Assertions.assertEquals(5, properties.getHealthCheckRetryIntervalSeconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+41
-32
@@ -1,8 +1,10 @@
|
|||||||
package com.cisd.tms.modules.backup.crypto;
|
package com.cisd.tms.modules.backup.crypto;
|
||||||
|
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
|
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
||||||
@@ -16,26 +18,30 @@ import org.mockito.Mockito;
|
|||||||
class ResourceEnvelopeCryptoServiceTest {
|
class ResourceEnvelopeCryptoServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldWrapDekWithEncUserKeyAndSignWithAuthUserKey() {
|
void shouldEncryptPayloadWithSdfEccSessionKeyAndSignWithAuthUserKey() {
|
||||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||||
|
|
||||||
AtomicReference<byte[]> capturedDek = new AtomicReference<>();
|
AtomicReference<byte[]> capturedPlainPayload = new AtomicReference<>();
|
||||||
AtomicReference<byte[]> decryptableDek = new AtomicReference<>();
|
Mockito.when(pcieCryptoService.encryptWithInternalEccSessionKey(Mockito.any(EccSessionKeyEncryptRequest.class)))
|
||||||
AtomicReference<byte[]> decryptResultDek = new AtomicReference<>();
|
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalEncryptEcc(Mockito.any(EccInternalEncryptRequest.class)))
|
|
||||||
.thenAnswer(invocation -> {
|
.thenAnswer(invocation -> {
|
||||||
EccInternalEncryptRequest request = invocation.getArgument(0);
|
EccSessionKeyEncryptRequest request = invocation.getArgument(0);
|
||||||
capturedDek.set(request.getData());
|
capturedPlainPayload.set(request.getData());
|
||||||
decryptableDek.set(Arrays.copyOf(request.getData(), request.getData().length));
|
EccSessionKeyEncryptResult result = new EccSessionKeyEncryptResult();
|
||||||
return backup(new byte[]{0x11, 0x22, 0x33});
|
result.setKeyIndex(request.getKeyIndex());
|
||||||
|
result.setKeyBits(request.getKeyBits());
|
||||||
|
result.setAlgId(request.getAlgId());
|
||||||
|
result.setIv(new byte[]{0x01, 0x02, 0x03, 0x04});
|
||||||
|
result.setWrappedSessionKey(new byte[]{0x11, 0x22, 0x33});
|
||||||
|
result.setEncryptedData(new byte[]{0x66, 0x77, 0x55});
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
Mockito.when(pcieCryptoService.userKeySignWithSm3(Mockito.any(UserKeySignRequest.class)))
|
Mockito.when(pcieCryptoService.userKeySignWithSm3(Mockito.any(UserKeySignRequest.class)))
|
||||||
.thenReturn(backup(new byte[]{0x44, 0x55, 0x66}));
|
.thenReturn(backup(new byte[]{0x44, 0x55, 0x66}));
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalDecryptEcc(Mockito.any(EccInternalDecryptRequest.class)))
|
Mockito.when(pcieCryptoService.decryptWithInternalEccSessionKey(Mockito.any(EccSessionKeyDecryptRequest.class)))
|
||||||
.thenAnswer(invocation -> {
|
.thenAnswer(invocation -> {
|
||||||
byte[] decryptedDek = Arrays.copyOf(decryptableDek.get(), decryptableDek.get().length);
|
EccSessionKeyDecryptResult result = new EccSessionKeyDecryptResult();
|
||||||
decryptResultDek.set(decryptedDek);
|
result.setDecryptedData(Arrays.copyOf(capturedPlainPayload.get(), capturedPlainPayload.get().length));
|
||||||
return backup(decryptedDek);
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
ResourceEnvelopeCryptoServiceImpl service = new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService);
|
ResourceEnvelopeCryptoServiceImpl service = new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService);
|
||||||
@@ -48,10 +54,23 @@ class ResourceEnvelopeCryptoServiceTest {
|
|||||||
|
|
||||||
Assertions.assertArrayEquals(payload, opened);
|
Assertions.assertArrayEquals(payload, opened);
|
||||||
Assertions.assertArrayEquals(new byte[]{0x44, 0x55, 0x66}, signature);
|
Assertions.assertArrayEquals(new byte[]{0x44, 0x55, 0x66}, signature);
|
||||||
|
Assertions.assertEquals(1, sealed.getEnvelope().get("version"));
|
||||||
|
Assertions.assertEquals("SDF_SM4_CBC", sealed.getEnvelope().get("payloadAlg"));
|
||||||
|
Assertions.assertTrue(sealed.getEnvelope().containsKey("ivBase64"));
|
||||||
|
Assertions.assertTrue(sealed.getEnvelope().containsKey("wrappedSessionKeyBase64"));
|
||||||
|
Assertions.assertFalse(sealed.getEnvelope().containsKey("wrappedDekBase64"));
|
||||||
|
Assertions.assertFalse(sealed.getEnvelope().containsKey("dekLength"));
|
||||||
|
Assertions.assertFalse(sealed.getEnvelope().containsKey("keyIndex"));
|
||||||
|
Assertions.assertFalse(sealed.getEnvelope().containsKey("keyBits"));
|
||||||
|
Assertions.assertFalse(sealed.getEnvelope().containsKey("algId"));
|
||||||
|
Assertions.assertFalse(sealed.getEnvelope().containsKey("sessionKeyAlg"));
|
||||||
|
Assertions.assertFalse(sealed.getEnvelope().containsKey("sessionKeyImportAlg"));
|
||||||
|
|
||||||
ArgumentCaptor<EccInternalEncryptRequest> encryptCaptor = ArgumentCaptor.forClass(EccInternalEncryptRequest.class);
|
ArgumentCaptor<EccSessionKeyEncryptRequest> encryptCaptor = ArgumentCaptor.forClass(EccSessionKeyEncryptRequest.class);
|
||||||
Mockito.verify(pcieCryptoService).sdfeInternalEncryptEcc(encryptCaptor.capture());
|
Mockito.verify(pcieCryptoService).encryptWithInternalEccSessionKey(encryptCaptor.capture());
|
||||||
Assertions.assertEquals(1, encryptCaptor.getValue().getKeyIndex());
|
Assertions.assertEquals(1, encryptCaptor.getValue().getKeyIndex());
|
||||||
|
Assertions.assertEquals(128, encryptCaptor.getValue().getKeyBits());
|
||||||
|
Assertions.assertArrayEquals(payload, encryptCaptor.getValue().getData());
|
||||||
|
|
||||||
ArgumentCaptor<UserKeySignRequest> signCaptor = ArgumentCaptor.forClass(UserKeySignRequest.class);
|
ArgumentCaptor<UserKeySignRequest> signCaptor = ArgumentCaptor.forClass(UserKeySignRequest.class);
|
||||||
Mockito.verify(pcieCryptoService).userKeySignWithSm3(signCaptor.capture());
|
Mockito.verify(pcieCryptoService).userKeySignWithSm3(signCaptor.capture());
|
||||||
@@ -61,11 +80,12 @@ class ResourceEnvelopeCryptoServiceTest {
|
|||||||
Mockito.verify(pcieCryptoService).eccInternalVerify(verifyCaptor.capture());
|
Mockito.verify(pcieCryptoService).eccInternalVerify(verifyCaptor.capture());
|
||||||
Assertions.assertEquals(1, verifyCaptor.getValue().getKeyIndex());
|
Assertions.assertEquals(1, verifyCaptor.getValue().getKeyIndex());
|
||||||
|
|
||||||
ArgumentCaptor<EccInternalDecryptRequest> decryptCaptor = ArgumentCaptor.forClass(EccInternalDecryptRequest.class);
|
ArgumentCaptor<EccSessionKeyDecryptRequest> decryptCaptor = ArgumentCaptor.forClass(EccSessionKeyDecryptRequest.class);
|
||||||
Mockito.verify(pcieCryptoService).sdfeInternalDecryptEcc(decryptCaptor.capture());
|
Mockito.verify(pcieCryptoService).decryptWithInternalEccSessionKey(decryptCaptor.capture());
|
||||||
Assertions.assertEquals(1, decryptCaptor.getValue().getKeyIndex());
|
Assertions.assertEquals(1, decryptCaptor.getValue().getKeyIndex());
|
||||||
Assertions.assertTrue(isAllZero(capturedDek.get()), "generated SM4 DEK should be wiped after sealing");
|
Assertions.assertArrayEquals(new byte[]{0x66, 0x77, 0x55}, decryptCaptor.getValue().getEncryptedData());
|
||||||
Assertions.assertTrue(isAllZero(decryptResultDek.get()), "decrypted SM4 DEK result buffer should be wiped after unsealing");
|
Mockito.verify(pcieCryptoService, Mockito.never()).sdfeInternalEncryptEcc(Mockito.any());
|
||||||
|
Mockito.verify(pcieCryptoService, Mockito.never()).sdfeInternalDecryptEcc(Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BackupDataResult backup(byte[] data) {
|
private static BackupDataResult backup(byte[] data) {
|
||||||
@@ -75,15 +95,4 @@ class ResourceEnvelopeCryptoServiceTest {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAllZero(byte[] bytes) {
|
|
||||||
if (bytes == null || bytes.length == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (byte value : bytes) {
|
|
||||||
if (value != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-29
@@ -4,8 +4,10 @@ import com.cisd.tms.modules.backup.config.ResourceBackupProperties;
|
|||||||
import com.cisd.tms.modules.backup.crypto.ResourceEnvelopeCryptoServiceImpl;
|
import com.cisd.tms.modules.backup.crypto.ResourceEnvelopeCryptoServiceImpl;
|
||||||
import com.cisd.tms.integration.crypto.pcie.PcieUserKeyType;
|
import com.cisd.tms.integration.crypto.pcie.PcieUserKeyType;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
|
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyDecryptResult;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyEncryptResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
||||||
import com.cisd.tms.modules.backup.dto.request.CreateResourceBackupRequest;
|
import com.cisd.tms.modules.backup.dto.request.CreateResourceBackupRequest;
|
||||||
import com.cisd.tms.modules.backup.dto.support.DiscoveredResourceItem;
|
import com.cisd.tms.modules.backup.dto.support.DiscoveredResourceItem;
|
||||||
@@ -45,19 +47,7 @@ class ResourcePackageServiceTest {
|
|||||||
properties.setOutputDir(tempDir.toString());
|
properties.setOutputDir(tempDir.toString());
|
||||||
Path payloadFile = writePayloadFile("resource/application.yml", "server.port=8080");
|
Path payloadFile = writePayloadFile("resource/application.yml", "server.port=8080");
|
||||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalEncryptEcc(Mockito.any(EccInternalEncryptRequest.class)))
|
mockEnvelopeCrypto(pcieCryptoService);
|
||||||
.thenAnswer(invocation -> {
|
|
||||||
EccInternalEncryptRequest request = invocation.getArgument(0);
|
|
||||||
return backup(request.getData());
|
|
||||||
});
|
|
||||||
Mockito.when(pcieCryptoService.userKeySignWithSm3(Mockito.any()))
|
|
||||||
.thenReturn(backup(new byte[]{0x11, 0x22, 0x33}));
|
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalDecryptEcc(Mockito.any(EccInternalDecryptRequest.class)))
|
|
||||||
.thenAnswer(invocation -> {
|
|
||||||
EccInternalDecryptRequest request = invocation.getArgument(0);
|
|
||||||
return backup(request.getCipherBlob());
|
|
||||||
});
|
|
||||||
Mockito.doNothing().when(pcieCryptoService).eccInternalVerify(Mockito.any());
|
|
||||||
ResourcePackageService service = new ResourcePackageServiceImpl(
|
ResourcePackageService service = new ResourcePackageServiceImpl(
|
||||||
properties,
|
properties,
|
||||||
new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService),
|
new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService),
|
||||||
@@ -177,13 +167,7 @@ class ResourcePackageServiceTest {
|
|||||||
Files.writeString(cmspDir.resolve("application-prd-node2.properties"), "spring.rabbitmq.username=user2");
|
Files.writeString(cmspDir.resolve("application-prd-node2.properties"), "spring.rabbitmq.username=user2");
|
||||||
Files.writeString(cmspDir.resolve("application-dev.properties"), "spring.rabbitmq.username=dev");
|
Files.writeString(cmspDir.resolve("application-dev.properties"), "spring.rabbitmq.username=dev");
|
||||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalEncryptEcc(Mockito.any(EccInternalEncryptRequest.class)))
|
mockEnvelopeCrypto(pcieCryptoService);
|
||||||
.thenAnswer(invocation -> backup(invocation.<EccInternalEncryptRequest>getArgument(0).getData()));
|
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalDecryptEcc(Mockito.any(EccInternalDecryptRequest.class)))
|
|
||||||
.thenAnswer(invocation -> backup(invocation.<EccInternalDecryptRequest>getArgument(0).getCipherBlob()));
|
|
||||||
Mockito.when(pcieCryptoService.userKeySignWithSm3(Mockito.any()))
|
|
||||||
.thenReturn(backup(new byte[]{0x11, 0x22, 0x33}));
|
|
||||||
Mockito.doNothing().when(pcieCryptoService).eccInternalVerify(Mockito.any());
|
|
||||||
ResourcePackageService service = new ResourcePackageServiceImpl(
|
ResourcePackageService service = new ResourcePackageServiceImpl(
|
||||||
properties,
|
properties,
|
||||||
new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService),
|
new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService),
|
||||||
@@ -224,13 +208,7 @@ class ResourcePackageServiceTest {
|
|||||||
ResourceBackupProperties properties = new ResourceBackupProperties();
|
ResourceBackupProperties properties = new ResourceBackupProperties();
|
||||||
properties.setOutputDir(tempDir.toString());
|
properties.setOutputDir(tempDir.toString());
|
||||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalEncryptEcc(Mockito.any(EccInternalEncryptRequest.class)))
|
mockEnvelopeCrypto(pcieCryptoService);
|
||||||
.thenAnswer(invocation -> backup(invocation.<EccInternalEncryptRequest>getArgument(0).getData()));
|
|
||||||
Mockito.when(pcieCryptoService.userKeySignWithSm3(Mockito.any()))
|
|
||||||
.thenReturn(backup(new byte[]{0x11, 0x22, 0x33}));
|
|
||||||
Mockito.when(pcieCryptoService.sdfeInternalDecryptEcc(Mockito.any(EccInternalDecryptRequest.class)))
|
|
||||||
.thenAnswer(invocation -> backup(invocation.<EccInternalDecryptRequest>getArgument(0).getCipherBlob()));
|
|
||||||
Mockito.doNothing().when(pcieCryptoService).eccInternalVerify(Mockito.any());
|
|
||||||
Mockito.when(pcieCryptoService.backupUserKey(2, PcieUserKeyType.SIGN.getCode(), 4096))
|
Mockito.when(pcieCryptoService.backupUserKey(2, PcieUserKeyType.SIGN.getCode(), 4096))
|
||||||
.thenReturn(backup(new byte[]{0x02, 0x20}));
|
.thenReturn(backup(new byte[]{0x02, 0x20}));
|
||||||
Mockito.when(pcieCryptoService.backupUserKey(3, PcieUserKeyType.SIGN.getCode(), 4096))
|
Mockito.when(pcieCryptoService.backupUserKey(3, PcieUserKeyType.SIGN.getCode(), 4096))
|
||||||
@@ -284,6 +262,31 @@ class ResourcePackageServiceTest {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void mockEnvelopeCrypto(PcieCryptoService pcieCryptoService) {
|
||||||
|
Mockito.when(pcieCryptoService.encryptWithInternalEccSessionKey(Mockito.any(EccSessionKeyEncryptRequest.class)))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
EccSessionKeyEncryptRequest request = invocation.getArgument(0);
|
||||||
|
EccSessionKeyEncryptResult result = new EccSessionKeyEncryptResult();
|
||||||
|
result.setKeyIndex(request.getKeyIndex());
|
||||||
|
result.setKeyBits(request.getKeyBits());
|
||||||
|
result.setAlgId(request.getAlgId());
|
||||||
|
result.setIv(new byte[16]);
|
||||||
|
result.setWrappedSessionKey(new byte[]{0x11, 0x22, 0x33});
|
||||||
|
result.setEncryptedData(request.getData());
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
Mockito.when(pcieCryptoService.decryptWithInternalEccSessionKey(Mockito.any(EccSessionKeyDecryptRequest.class)))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
EccSessionKeyDecryptRequest request = invocation.getArgument(0);
|
||||||
|
EccSessionKeyDecryptResult result = new EccSessionKeyDecryptResult();
|
||||||
|
result.setDecryptedData(request.getEncryptedData());
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
Mockito.when(pcieCryptoService.userKeySignWithSm3(Mockito.any()))
|
||||||
|
.thenReturn(backup(new byte[]{0x11, 0x22, 0x33}));
|
||||||
|
Mockito.doNothing().when(pcieCryptoService).eccInternalVerify(Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
private static InitTaskEntity sampleInitTask() {
|
private static InitTaskEntity sampleInitTask() {
|
||||||
InitTaskEntity entity = new InitTaskEntity();
|
InitTaskEntity entity = new InitTaskEntity();
|
||||||
entity.setTaskId("INIT-20260408-000001");
|
entity.setTaskId("INIT-20260408-000001");
|
||||||
|
|||||||
+42
@@ -128,6 +128,48 @@ class ApplyResourceBackupScriptContractTest {
|
|||||||
Assertions.assertTrue(log.contains("http://127.0.0.1:8080/actuator/health"));
|
Assertions.assertTrue(log.contains("http://127.0.0.1:8080/actuator/health"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldWaitForHealthCheckUntilServiceIsReady() throws Exception {
|
||||||
|
Path workDir = tempDir.resolve("work");
|
||||||
|
Path payloadDir = workDir.resolve("payload");
|
||||||
|
Files.createDirectories(payloadDir);
|
||||||
|
Files.writeString(payloadDir.resolve("payload-manifest.json"), "{\"resources\":[]}");
|
||||||
|
Path commandLog = tempDir.resolve("curl-retry.log");
|
||||||
|
Path counter = tempDir.resolve("curl-count");
|
||||||
|
Path binDir = tempDir.resolve("bin");
|
||||||
|
Files.createDirectories(binDir);
|
||||||
|
Path curl = binDir.resolve("curl");
|
||||||
|
Files.writeString(curl, """
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
count=0
|
||||||
|
if [ -f "%s" ]; then
|
||||||
|
count="$(cat "%s")"
|
||||||
|
fi
|
||||||
|
count=$((count + 1))
|
||||||
|
printf '%%s' "$count" > "%s"
|
||||||
|
printf 'attempt=%%s %%s\\n' "$count" "$*" >> "%s"
|
||||||
|
if [ "$count" -lt 3 ]; then
|
||||||
|
exit 7
|
||||||
|
fi
|
||||||
|
printf '{"status":"UP"}'
|
||||||
|
""".formatted(counter, counter, counter, commandLog));
|
||||||
|
curl.toFile().setExecutable(true);
|
||||||
|
|
||||||
|
runScript(workDir, Map.of(
|
||||||
|
"HEALTH_CHECK_URL", "http://127.0.0.1:8080/actuator/health",
|
||||||
|
"HEALTH_CHECK_TIMEOUT_SECONDS", "1",
|
||||||
|
"HEALTH_CHECK_MAX_WAIT_SECONDS", "10",
|
||||||
|
"HEALTH_CHECK_RETRY_INTERVAL_SECONDS", "0",
|
||||||
|
"PATH", binDir + ":" + System.getenv("PATH")
|
||||||
|
));
|
||||||
|
|
||||||
|
String log = Files.readString(commandLog);
|
||||||
|
Assertions.assertTrue(log.contains("attempt=1"));
|
||||||
|
Assertions.assertTrue(log.contains("attempt=2"));
|
||||||
|
Assertions.assertTrue(log.contains("attempt=3"));
|
||||||
|
Assertions.assertTrue(Files.readString(workDir.resolve("status.json")).contains("\"status\":\"SUCCESS\""));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldRunConfiguredStopAndStartCommandsInOrder() throws Exception {
|
void shouldRunConfiguredStopAndStartCommandsInOrder() throws Exception {
|
||||||
Path workDir = tempDir.resolve("work");
|
Path workDir = tempDir.resolve("work");
|
||||||
|
|||||||
+7
-1
@@ -42,7 +42,9 @@ class LightweightRestoreApplierLauncherTest {
|
|||||||
invoker,
|
invoker,
|
||||||
"jdbc:mysql://db-host:4000/tms?useUnicode=true",
|
"jdbc:mysql://db-host:4000/tms?useUnicode=true",
|
||||||
"root",
|
"root",
|
||||||
"p'ass"
|
"p'ass",
|
||||||
|
"/home/cmep4i",
|
||||||
|
"cmep4i"
|
||||||
);
|
);
|
||||||
|
|
||||||
Path workDir = tempDir.resolve("RRST-001");
|
Path workDir = tempDir.resolve("RRST-001");
|
||||||
@@ -65,6 +67,10 @@ class LightweightRestoreApplierLauncherTest {
|
|||||||
Assertions.assertTrue(env.contains("export DB_PORT='4000'"));
|
Assertions.assertTrue(env.contains("export DB_PORT='4000'"));
|
||||||
Assertions.assertTrue(env.contains("export DB_USER='root'"));
|
Assertions.assertTrue(env.contains("export DB_USER='root'"));
|
||||||
Assertions.assertTrue(env.contains("export DB_PASSWORD='p'\\''ass'"));
|
Assertions.assertTrue(env.contains("export DB_PASSWORD='p'\\''ass'"));
|
||||||
|
Assertions.assertTrue(env.contains("export HEALTH_CHECK_MAX_WAIT_SECONDS='180'"));
|
||||||
|
Assertions.assertTrue(env.contains("export HEALTH_CHECK_RETRY_INTERVAL_SECONDS='5'"));
|
||||||
|
Assertions.assertTrue(env.contains("export STANDARD_HOME_DIR='/home/cmep4i'"));
|
||||||
|
Assertions.assertTrue(env.contains("export STANDARD_RUN_USER='cmep4i'"));
|
||||||
|
|
||||||
String stopCommands = Files.readString(workDir.toAbsolutePath().normalize().resolve("restore-stop-commands.tsv"));
|
String stopCommands = Files.readString(workDir.toAbsolutePath().normalize().resolve("restore-stop-commands.tsv"));
|
||||||
Assertions.assertTrue(stopCommands.contains("TMS\tfalse\t/home/tms/scripts/tms.sh stop"));
|
Assertions.assertTrue(stopCommands.contains("TMS\tfalse\t/home/tms/scripts/tms.sh stop"));
|
||||||
|
|||||||
@@ -122,6 +122,48 @@ class ResourceBackupServiceTest {
|
|||||||
Assertions.assertNull(saved.getPackagePath());
|
Assertions.assertNull(saved.getPackagePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPersistPackageFailureWithRootCauseMessage() {
|
||||||
|
InitTaskRepository initTaskRepository = Mockito.mock(InitTaskRepository.class);
|
||||||
|
ResourceDiscoveryService resourceDiscoveryService = Mockito.mock(ResourceDiscoveryService.class);
|
||||||
|
ResourcePackageService resourcePackageService = Mockito.mock(ResourcePackageService.class);
|
||||||
|
ResourceBackupTaskRepository resourceBackupTaskRepository = Mockito.mock(ResourceBackupTaskRepository.class);
|
||||||
|
ResourceTaskAdmissionGuard admissionGuard = Mockito.mock(ResourceTaskAdmissionGuard.class);
|
||||||
|
|
||||||
|
InitTaskEntity initTask = sampleInitTask();
|
||||||
|
List<DiscoveredResourceItem> discoveredItems = List.of(resource("TMS_CONFIG", "/home/tms/config", true));
|
||||||
|
|
||||||
|
Mockito.when(initTaskRepository.findLatestByTaskTypeAndStatus("INIT", "SUCCESS")).thenReturn(Optional.of(initTask));
|
||||||
|
Mockito.when(resourceDiscoveryService.discover(initTask)).thenReturn(discoveredItems);
|
||||||
|
Mockito.when(resourcePackageService.createPackage(
|
||||||
|
Mockito.anyString(),
|
||||||
|
Mockito.anyString(),
|
||||||
|
Mockito.same(initTask),
|
||||||
|
Mockito.eq(discoveredItems),
|
||||||
|
Mockito.any(CreateResourceBackupRequest.class)
|
||||||
|
)).thenThrow(new IllegalStateException("资源备份载荷加封失败",
|
||||||
|
new RuntimeException("[SDF_Encrypt] 无效参数 (errorCode=16777245)")));
|
||||||
|
|
||||||
|
ResourceBackupService service = new ResourceBackupServiceImpl(
|
||||||
|
initTaskRepository,
|
||||||
|
resourceDiscoveryService,
|
||||||
|
resourcePackageService,
|
||||||
|
resourceBackupTaskRepository,
|
||||||
|
new ResourceTaskIdGenerator(),
|
||||||
|
admissionGuard
|
||||||
|
);
|
||||||
|
|
||||||
|
CreateResourceBackupResponse response = service.createBackupTask(sampleRequest());
|
||||||
|
|
||||||
|
Assertions.assertEquals("FAILED", response.getStatus());
|
||||||
|
ArgumentCaptor<ResourceBackupTaskEntity> captor = ArgumentCaptor.forClass(ResourceBackupTaskEntity.class);
|
||||||
|
Mockito.verify(resourceBackupTaskRepository).update(captor.capture());
|
||||||
|
ResourceBackupTaskEntity saved = captor.getValue();
|
||||||
|
Assertions.assertEquals("PACKAGE_CREATE_FAILED", saved.getErrorCode());
|
||||||
|
Assertions.assertTrue(saved.getErrorMessage().contains("资源备份载荷加封失败"));
|
||||||
|
Assertions.assertTrue(saved.getErrorMessage().contains("SDF_Encrypt"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldPersistRunningTaskWithConfiguredKeyFingerprintsBeforePackaging() {
|
void shouldPersistRunningTaskWithConfiguredKeyFingerprintsBeforePackaging() {
|
||||||
InitTaskRepository initTaskRepository = Mockito.mock(InitTaskRepository.class);
|
InitTaskRepository initTaskRepository = Mockito.mock(InitTaskRepository.class);
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
//package com.cisd.tms.modules.backup.service;
|
|
||||||
//
|
|
||||||
//import com.cisd.tms.common.config.properties.CisdPresetProperties;
|
|
||||||
//import com.cisd.tms.common.exception.BizException;
|
|
||||||
//import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
|
|
||||||
//import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
|
||||||
//import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
|
||||||
//import com.cisd.tms.modules.backup.config.ResourceBackupProperties;
|
|
||||||
//import com.cisd.tms.modules.backup.crypto.ResourceEnvelopeCryptoServiceImpl;
|
|
||||||
//import com.cisd.tms.modules.backup.dto.request.CreateResourceBackupRequest;
|
|
||||||
//import com.cisd.tms.modules.backup.dto.request.ResourceRestorePrecheckRequest;
|
|
||||||
//import com.cisd.tms.modules.backup.dto.response.ResourceRestorePrecheckResponse;
|
|
||||||
//import com.cisd.tms.modules.backup.dto.support.DiscoveredResourceItem;
|
|
||||||
//import com.cisd.tms.modules.backup.packagex.ResourcePackageResult;
|
|
||||||
//import com.cisd.tms.modules.backup.packagex.ResourcePackageService;
|
|
||||||
//import com.cisd.tms.modules.backup.packagex.ResourcePackageServiceImpl;
|
|
||||||
//import com.cisd.tms.modules.backup.service.impl.ResourcePrecheckServiceImpl;
|
|
||||||
//import com.cisd.tms.modules.backup.support.InMemoryResourcePrecheckStateStore;
|
|
||||||
//import com.cisd.tms.modules.backup.support.ResourceTaskIdGenerator;
|
|
||||||
//import com.cisd.tms.modules.file.entity.FileRecordEntity;
|
|
||||||
//import com.cisd.tms.modules.file.repository.FileRecordRepository;
|
|
||||||
//import com.cisd.tms.modules.init.entity.InitTaskEntity;
|
|
||||||
//import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
//import java.nio.file.Path;
|
|
||||||
//import java.util.List;
|
|
||||||
//import java.util.Optional;
|
|
||||||
//import org.junit.jupiter.api.Assertions;
|
|
||||||
//import org.junit.jupiter.api.Test;
|
|
||||||
//import org.junit.jupiter.api.io.TempDir;
|
|
||||||
//import org.mockito.Mockito;
|
|
||||||
//
|
|
||||||
//class ResourcePrecheckServiceTest {
|
|
||||||
//
|
|
||||||
// @TempDir
|
|
||||||
// Path tempDir;
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// void shouldOnlyVerifyHeaderWithoutDecryptingPayload() {
|
|
||||||
// FileRecordRepository fileRecordRepository = Mockito.mock(FileRecordRepository.class);
|
|
||||||
// ResourceBackupProperties backupProperties = new ResourceBackupProperties();
|
|
||||||
// backupProperties.setOutputDir(tempDir.toString());
|
|
||||||
// backupProperties.setStorageEncKeyIndex(7);
|
|
||||||
// backupProperties.setAuthenticitySignKeyIndex(9);
|
|
||||||
// PcieCryptoService pcieCryptoService = mockCrypto();
|
|
||||||
// ResourcePackageService packageService = new ResourcePackageServiceImpl(
|
|
||||||
// backupProperties,
|
|
||||||
// new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService, backupProperties),
|
|
||||||
// new ObjectMapper()
|
|
||||||
// );
|
|
||||||
// ResourcePackageResult packageResult = packageService.createPackage(
|
|
||||||
// "RBKP-TASK-20260414-000001",
|
|
||||||
// "RBKP-20260414-000001",
|
|
||||||
// sampleInitTask(),
|
|
||||||
// List.of(resource("TMS_CONFIG", "/home/tms/config")),
|
|
||||||
// new CreateResourceBackupRequest()
|
|
||||||
// );
|
|
||||||
// FileRecordEntity fileRecordEntity = new FileRecordEntity();
|
|
||||||
// fileRecordEntity.setFileId("FILE-20260414-000001");
|
|
||||||
// fileRecordEntity.setStoragePath(packageResult.getPackagePath());
|
|
||||||
// Mockito.when(fileRecordRepository.findByFileId("FILE-20260414-000001")).thenReturn(Optional.of(fileRecordEntity));
|
|
||||||
//
|
|
||||||
// CisdPresetProperties presetProperties = new CisdPresetProperties();
|
|
||||||
// presetProperties.setProductType("ENTERPRISE");
|
|
||||||
// ResourcePrecheckService service = new ResourcePrecheckServiceImpl(
|
|
||||||
// fileRecordRepository,
|
|
||||||
// packageService,
|
|
||||||
// new InMemoryResourcePrecheckStateStore(),
|
|
||||||
// new ResourceTaskIdGenerator(),
|
|
||||||
// presetProperties,
|
|
||||||
// backupProperties
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// ResourceRestorePrecheckResponse response = service.precheck(sampleRequest());
|
|
||||||
//
|
|
||||||
// Assertions.assertTrue(response.getSignatureValid());
|
|
||||||
// Assertions.assertTrue(response.getKeysetMatched());
|
|
||||||
// Assertions.assertFalse(response.getPayloadDecrypted());
|
|
||||||
// Assertions.assertInstanceOf(java.util.Map.class, response.getRestoreScopeSummary());
|
|
||||||
// Assertions.assertTrue(response.getRestoreScopeSummary().toString().contains("resourceCount"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// void shouldRejectRestoreWhenKeyFingerprintDoesNotMatch() {
|
|
||||||
// FileRecordRepository fileRecordRepository = Mockito.mock(FileRecordRepository.class);
|
|
||||||
// ResourceBackupProperties backupProperties = new ResourceBackupProperties();
|
|
||||||
// backupProperties.setOutputDir(tempDir.toString());
|
|
||||||
// backupProperties.setStorageEncKeyIndex(1);
|
|
||||||
// backupProperties.setAuthenticitySignKeyIndex(1);
|
|
||||||
// PcieCryptoService pcieCryptoService = mockCrypto();
|
|
||||||
// ResourcePackageService packageService = new ResourcePackageServiceImpl(
|
|
||||||
// backupProperties,
|
|
||||||
// new ResourceEnvelopeCryptoServiceImpl(pcieCryptoService, backupProperties),
|
|
||||||
// new ObjectMapper()
|
|
||||||
// );
|
|
||||||
// ResourcePackageResult packageResult = packageService.createPackage(
|
|
||||||
// "RBKP-TASK-20260414-000002",
|
|
||||||
// "RBKP-20260414-000002",
|
|
||||||
// sampleInitTask(),
|
|
||||||
// List.of(resource("TMS_CONFIG", "/home/tms/config")),
|
|
||||||
// new CreateResourceBackupRequest()
|
|
||||||
// );
|
|
||||||
// FileRecordEntity fileRecordEntity = new FileRecordEntity();
|
|
||||||
// fileRecordEntity.setFileId("FILE-20260414-000002");
|
|
||||||
// fileRecordEntity.setStoragePath(packageResult.getPackagePath());
|
|
||||||
// Mockito.when(fileRecordRepository.findByFileId("FILE-20260414-000002")).thenReturn(Optional.of(fileRecordEntity));
|
|
||||||
//
|
|
||||||
// CisdPresetProperties presetProperties = new CisdPresetProperties();
|
|
||||||
// presetProperties.setProductType("ENTERPRISE");
|
|
||||||
// ResourceBackupProperties mismatched = new ResourceBackupProperties();
|
|
||||||
// mismatched.setStorageEncKeyIndex(3);
|
|
||||||
// mismatched.setAuthenticitySignKeyIndex(5);
|
|
||||||
// ResourcePrecheckService service = new ResourcePrecheckServiceImpl(
|
|
||||||
// fileRecordRepository,
|
|
||||||
// packageService,
|
|
||||||
// new InMemoryResourcePrecheckStateStore(),
|
|
||||||
// new ResourceTaskIdGenerator(),
|
|
||||||
// presetProperties,
|
|
||||||
// mismatched
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// BizException exception = Assertions.assertThrows(BizException.class, () -> service.precheck(sampleMismatchRequest()));
|
|
||||||
// Assertions.assertTrue(exception.getMessage().contains("指纹"));
|
|
||||||
// Assertions.assertTrue(exception.getMessage().contains("不匹配"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private ResourceRestorePrecheckRequest sampleRequest() {
|
|
||||||
// ResourceRestorePrecheckRequest request = new ResourceRestorePrecheckRequest();
|
|
||||||
// request.setFileId("FILE-20260414-000001");
|
|
||||||
// return request;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private ResourceRestorePrecheckRequest sampleMismatchRequest() {
|
|
||||||
// ResourceRestorePrecheckRequest request = new ResourceRestorePrecheckRequest();
|
|
||||||
// request.setFileId("FILE-20260414-000002");
|
|
||||||
// return request;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static PcieCryptoService mockCrypto() {
|
|
||||||
// PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
|
||||||
// Mockito.when(pcieCryptoService.sdfeInternalEncryptEcc(Mockito.any(EccInternalEncryptRequest.class)))
|
|
||||||
// .thenAnswer(invocation -> {
|
|
||||||
// EccInternalEncryptRequest request = invocation.getArgument(0);
|
|
||||||
// return backup(request.getData());
|
|
||||||
// });
|
|
||||||
// Mockito.when(pcieCryptoService.eccInternalSign(Mockito.any()))
|
|
||||||
// .thenReturn(backup(new byte[]{0x21, 0x22, 0x23}));
|
|
||||||
// Mockito.doNothing().when(pcieCryptoService).eccInternalVerify(Mockito.any());
|
|
||||||
// return pcieCryptoService;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static BackupDataResult backup(byte[] data) {
|
|
||||||
// BackupDataResult result = new BackupDataResult();
|
|
||||||
// result.setData(data);
|
|
||||||
// result.setLength(data.length);
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static InitTaskEntity sampleInitTask() {
|
|
||||||
// InitTaskEntity entity = new InitTaskEntity();
|
|
||||||
// entity.setTaskId("INIT-20260408-000001");
|
|
||||||
// entity.setTaskType("INIT");
|
|
||||||
// entity.setStatus("SUCCESS");
|
|
||||||
// entity.setProductType("ENTERPRISE");
|
|
||||||
// entity.setMqType("RABBITMQ");
|
|
||||||
// entity.setOrgCode("AAAABBBBXXX");
|
|
||||||
// entity.setDeployMode("SINGLE");
|
|
||||||
// return entity;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static DiscoveredResourceItem resource(String code, String path) {
|
|
||||||
// DiscoveredResourceItem item = new DiscoveredResourceItem();
|
|
||||||
// item.setResourceType("FILE");
|
|
||||||
// item.setResourceCode(code);
|
|
||||||
// item.setResolvedPath(path);
|
|
||||||
// item.setRequired(true);
|
|
||||||
// return item;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -9,6 +9,8 @@ import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest;
|
|||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestRequest;
|
||||||
|
import com.cisd.tms.integration.crypto.pcie.model.EccSessionKeyCryptoSelfTestResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.GenerateKeyPairRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.GenerateKeyPairRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.KeyStatusResult;
|
import com.cisd.tms.integration.crypto.pcie.model.KeyStatusResult;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest;
|
||||||
@@ -20,6 +22,7 @@ import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2VerifyRequest;
|
|||||||
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest;
|
import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest;
|
||||||
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
||||||
|
import com.cisd.tms.modules.device.dto.crypto.request.CryptoEccSessionKeyCryptoSelfTestInternalRequest;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
@@ -31,6 +34,7 @@ import static org.hamcrest.Matchers.containsString;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
@@ -338,6 +342,65 @@ class CryptoCardControllerTest {
|
|||||||
assertArrayEquals(new byte[] {0x21, 0x43, 0x65}, captor.getValue().getCipherBlob());
|
assertArrayEquals(new byte[] {0x21, 0x43, 0x65}, captor.getValue().getCipherBlob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRunEccSessionKeyCryptoSelfTestWithPlaintextInput() throws Exception {
|
||||||
|
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||||
|
EccSessionKeyCryptoSelfTestResult result = new EccSessionKeyCryptoSelfTestResult();
|
||||||
|
result.setKeyIndex(1);
|
||||||
|
result.setKeyBits(128);
|
||||||
|
result.setAlgId(Gm0018AlgorithmIds.SM4_CBC);
|
||||||
|
result.setIv(new byte[] {0x01, 0x02});
|
||||||
|
result.setWrappedSessionKey(new byte[] {0x01, 0x02, 0x03});
|
||||||
|
result.setEncryptedData(new byte[] {0x11, 0x22, 0x33, 0x44});
|
||||||
|
result.setDecryptedData("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
result.setMatched(true);
|
||||||
|
Mockito.when(pcieCryptoService.eccSessionKeyEncryptDecryptSelfTest(Mockito.any())).thenReturn(result);
|
||||||
|
|
||||||
|
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService))
|
||||||
|
.setControllerAdvice(new GlobalExceptionHandler())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/v1/device/crypto/session-key/ecc/internal-crypto-self-test")
|
||||||
|
.contentType("application/json")
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"plainText": "hello"
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(containsString("\"keyIndex\":1")))
|
||||||
|
.andExpect(content().string(containsString("\"keyBits\":128")))
|
||||||
|
.andExpect(content().string(containsString("\"algId\":1026")))
|
||||||
|
.andExpect(content().string(containsString("\"ivBase64\":\"AQI=\"")))
|
||||||
|
.andExpect(content().string(containsString("\"wrappedSessionKeyLength\":3")))
|
||||||
|
.andExpect(content().string(containsString("\"wrappedSessionKeyBase64\":\"AQID\"")))
|
||||||
|
.andExpect(content().string(containsString("\"encryptedDataLength\":4")))
|
||||||
|
.andExpect(content().string(containsString("\"encryptedDataBase64\":\"ESIzRA==\"")))
|
||||||
|
.andExpect(content().string(containsString("\"decryptedText\":\"hello\"")))
|
||||||
|
.andExpect(content().string(containsString("\"matched\":true")));
|
||||||
|
|
||||||
|
ArgumentCaptor<EccSessionKeyCryptoSelfTestRequest> captor =
|
||||||
|
ArgumentCaptor.forClass(EccSessionKeyCryptoSelfTestRequest.class);
|
||||||
|
Mockito.verify(pcieCryptoService).eccSessionKeyEncryptDecryptSelfTest(captor.capture());
|
||||||
|
assertEquals(1, captor.getValue().getKeyIndex());
|
||||||
|
assertEquals(128, captor.getValue().getKeyBits());
|
||||||
|
assertEquals(Gm0018AlgorithmIds.SM4_CBC, captor.getValue().getAlgId());
|
||||||
|
assertEquals(4096, captor.getValue().getEncOutBufferSize());
|
||||||
|
assertEquals(4096, captor.getValue().getDecOutBufferSize());
|
||||||
|
assertNull(captor.getValue().getIv());
|
||||||
|
assertArrayEquals("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposeOnlyPlainTextOnEccSessionKeySelfTestRequest() {
|
||||||
|
String[] fieldNames = java.util.Arrays.stream(
|
||||||
|
CryptoEccSessionKeyCryptoSelfTestInternalRequest.class.getDeclaredFields())
|
||||||
|
.map(java.lang.reflect.Field::getName)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
|
||||||
|
assertArrayEquals(new String[] {"plainText"}, fieldNames);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldSignUserKeyViaHighLevelEndpoint() throws Exception {
|
void shouldSignUserKeyViaHighLevelEndpoint() throws Exception {
|
||||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user