fix:备份恢复密钥

This commit is contained in:
waner
2026-05-09 18:09:16 +08:00
parent 9ccb5147ef
commit 79d38cc566
28 changed files with 1543 additions and 332 deletions
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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.PointerByReference;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
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 HMAC_MAX_LENGTH = 64;
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 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
public BackupDataResult exchangeDigitEnvelopeBaseOnRsa(ExchangeDigitEnvelopeRsaRequest 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) {
if (fileName == null || fileName.isBlank()) {
throw new IllegalArgumentException("fileName不能为空");
@@ -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.EccInternalDecryptRequest;
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.EccExternalEncryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.EccExternalSignRequest;
@@ -466,6 +472,71 @@ public class MockPcieCryptoService implements PcieCryptoService {
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
public BackupDataResult exchangeDigitEnvelopeBaseOnRsa(ExchangeDigitEnvelopeRsaRequest request) {
ExchangeDigitEnvelopeRsaRequest req = requireRequest("request", request);
@@ -938,6 +1009,63 @@ public class MockPcieCryptoService implements PcieCryptoService {
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) {
if (keyType != PcieUserKeyType.SIGN.getCode() && keyType != PcieUserKeyType.ENC.getCode()) {
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.EccInternalDecryptRequest;
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.EccExternalEncryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.EccExternalSignRequest;
@@ -341,6 +347,21 @@ public interface PcieCryptoService {
*/
void importKeyWithIskEcc(ImportKeyWithIskEccRequest request);
/**
* 生成内部 ECC 公钥包裹的会话密钥,使用原会话密钥句柄加密数据,再导入包裹密钥并解密数据。
*/
EccSessionKeyCryptoSelfTestResult eccSessionKeyEncryptDecryptSelfTest(EccSessionKeyCryptoSelfTestRequest request);
/**
* 使用内部 ECC 公钥生成会话密钥,并通过 SDF_Encrypt 加密数据。
*/
EccSessionKeyEncryptResult encryptWithInternalEccSessionKey(EccSessionKeyEncryptRequest request);
/**
* 使用内部 ECC 私钥导入包裹会话密钥,并通过 SDF_Decrypt 解密数据。
*/
EccSessionKeyDecryptResult decryptWithInternalEccSessionKey(EccSessionKeyDecryptRequest request);
/**
* 执行基于 RSA 的数字信封交换。
*/
@@ -29,6 +29,8 @@ public class ResourceBackupProperties {
private String cmepDatabaseName = "CMEP";
private String healthCheckUrl = "http://127.0.0.1:8080/actuator/health";
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 List<ConfiguredRestoreCommand> restoreStopCommands = List.of();
private List<ConfiguredRestoreCommand> restoreStartCommands = List.of();
@@ -144,6 +146,22 @@ public class ResourceBackupProperties {
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() {
return restoreApplyScriptPath;
}
@@ -1,31 +1,22 @@
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.service.PcieCryptoService;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;
import java.util.LinkedHashMap;
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.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Service;
@Service
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;
static {
if (Security.getProvider(PROVIDER) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
public ResourceEnvelopeCryptoServiceImpl(PcieCryptoService pcieCryptoService) {
this.pcieCryptoService = pcieCryptoService;
}
@@ -35,38 +26,30 @@ public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCrypto
if (payloadBytes == null) {
throw new IllegalArgumentException("payloadBytes不能为空");
}
// 资源包正文使用随机 SM4 DEK 加密,再由用户密钥把 DEK 封装成 envelope。
// 这样大 payload 仍走对称加密,ECC 只负责保护一次性数据密钥。
byte[] sm4Key = randomBytes(16);
byte[] iv = randomBytes(16);
try {
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sm4Key, "SM4"), new IvParameterSpec(iv));
// 用户密钥只负责封装随机 DEK;真正的大 payload 仍走对称加密,避免直接把大文件送进 ECC 接口。
EccInternalEncryptRequest encryptRequest = new EccInternalEncryptRequest();
// 资源包正文加密走 SDF 会话密钥流程:内部 ECC 公钥生成会话密钥,SDF_Encrypt 加密 payload。
EccSessionKeyEncryptRequest encryptRequest = new EccSessionKeyEncryptRequest();
encryptRequest.setKeyIndex(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_INDEX);
encryptRequest.setData(sm4Key);
BackupDataResult wrappedDek = pcieCryptoService.sdfeInternalEncryptEcc(encryptRequest);
encryptRequest.setKeyBits(PAYLOAD_SESSION_KEY_BITS);
encryptRequest.setAlgId(PAYLOAD_ALG_ID);
encryptRequest.setData(payloadBytes);
encryptRequest.setOutBufferSize(payloadBytes.length + 16);
EccSessionKeyEncryptResult encryptResult = pcieCryptoService.encryptWithInternalEccSessionKey(encryptRequest);
Map<String, Object> envelope = new LinkedHashMap<>();
envelope.put("version", 1);
envelope.put("payloadAlg", "SM4-CBC");
envelope.put("keyWrapAlg", "SDFE_InternalEncrypt_ECC");
envelope.put("ivBase64", Base64.getEncoder().encodeToString(iv));
envelope.put("wrappedDekBase64", Base64.getEncoder().encodeToString(normalize(wrappedDek)));
envelope.put("dekLength", sm4Key.length);
envelope.put("payloadAlg", PAYLOAD_ALG_NAME);
envelope.put("ivBase64", Base64.getEncoder().encodeToString(nullToEmpty(encryptResult.getIv())));
envelope.put("wrappedSessionKeyBase64", Base64.getEncoder().encodeToString(nullToEmpty(encryptResult.getWrappedSessionKey())));
ResourceSealedPayload sealedPayload = new ResourceSealedPayload();
sealedPayload.setCipherBytes(cipher.doFinal(payloadBytes));
sealedPayload.setCipherBytes(nullToEmpty(encryptResult.getEncryptedData()));
sealedPayload.setEnvelope(envelope);
sealedPayload.setStorageEncKeyFingerprint(ResourceCryptoKeyConstants.STORAGE_ENC_KEY_FINGERPRINT);
sealedPayload.setAuthenticityKeyFingerprint(ResourceCryptoKeyConstants.AUTHENTICITY_KEY_FINGERPRINT);
return sealedPayload;
} catch (Exception ex) {
throw new IllegalStateException("资源备份载荷加封失败", ex);
} finally {
wipe(sm4Key);
throw new IllegalStateException("资源备份载荷加封失败:" + rootCauseMessage(ex), ex);
}
}
@@ -112,30 +95,28 @@ public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCrypto
throw new IllegalArgumentException("signature不能为空");
}
verify(dataToVerify, signature);
byte[] wrappedDek = Base64.getDecoder().decode(String.valueOf(envelope.get("wrappedDekBase64")));
int dekLength = parseInt(envelope.get("dekLength"), 16);
// 先验签,再解数字信封,再解 payload;顺序固定,避免篡改包先进入明文处理路径。
UserKeyDecryptRequest decryptRequest = new UserKeyDecryptRequest();
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);
if (!Integer.valueOf(1).equals(envelope.get("version"))) {
throw new IllegalArgumentException("envelope版本不支持");
}
if (!PAYLOAD_ALG_NAME.equals(String.valueOf(envelope.get("payloadAlg")))) {
throw new IllegalArgumentException("payload加密算法不支持");
}
}
private static byte[] randomBytes(int size) {
byte[] bytes = new byte[size];
new java.security.SecureRandom().nextBytes(bytes);
return bytes;
byte[] wrappedSessionKey = Base64.getDecoder().decode(String.valueOf(envelope.get("wrappedSessionKeyBase64")));
// 先验签,再导入会话密钥,最后 SDF_Decrypt 解 payload;顺序固定,避免篡改包先进入明文处理路径。
EccSessionKeyDecryptRequest decryptRequest = new EccSessionKeyDecryptRequest();
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) {
@@ -158,33 +139,16 @@ public class ResourceEnvelopeCryptoServiceImpl implements ResourceEnvelopeCrypto
return normalized;
}
private static byte[] normalizeAndWipe(BackupDataResult result) {
try {
return normalize(result);
} finally {
if (result != null) {
wipe(result.getData());
}
}
private static byte[] nullToEmpty(byte[] bytes) {
return bytes == null ? new byte[0] : bytes;
}
private static int parseInt(Object value, int defaultValue) {
if (value == null) {
return defaultValue;
}
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);
private static String rootCauseMessage(Throwable throwable) {
Throwable current = throwable;
while (current.getCause() != null) {
current = current.getCause();
}
String message = current.getMessage();
return message == null || message.isBlank() ? current.getClass().getSimpleName() : message;
}
}
@@ -20,19 +20,25 @@ public class LightweightRestoreApplierLauncher {
private final String datasourceUrl;
private final String datasourceUsername;
private final String datasourcePassword;
private final String standardHomeDir;
private final String standardRunUser;
public LightweightRestoreApplierLauncher(
ResourceBackupProperties properties,
RestoreCommandInvoker invoker,
@Value("${spring.datasource.url:}") String datasourceUrl,
@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.invoker = invoker;
this.datasourceUrl = datasourceUrl;
this.datasourceUsername = datasourceUsername;
this.datasourcePassword = datasourcePassword;
this.standardHomeDir = standardHomeDir;
this.standardRunUser = standardRunUser;
}
public void launch(Path workDir) {
@@ -115,7 +121,11 @@ public class LightweightRestoreApplierLauncher {
values.put("DB_PASSWORD", trim(datasourcePassword));
values.put("HEALTH_CHECK_URL", trim(properties.getHealthCheckUrl()));
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("STANDARD_HOME_DIR", trim(standardHomeDir));
values.put("STANDARD_RUN_USER", trim(standardRunUser));
StringBuilder builder = new StringBuilder("# generated by TMS resource restore\n");
for (Map.Entry<String, String> entry : values.entrySet()) {
@@ -114,7 +114,7 @@ public class ResourceBackupServiceImpl implements ResourceBackupService {
} catch (RuntimeException ex) {
entity.setStatus("FAILED");
entity.setErrorCode("PACKAGE_CREATE_FAILED");
entity.setErrorMessage(ex.getMessage());
entity.setErrorMessage(rootCauseAwareMessage(ex));
}
entity.setFinishTime(LocalDateTime.now());
entity.setUpdateTime(entity.getFinishTime());
@@ -229,4 +229,20 @@ public class ResourceBackupServiceImpl implements ResourceBackupService {
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.EccInternalSignRequest;
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.ExchangeDigitEnvelopeRsaRequest;
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")
@Operation(summary = "初始化识别码", description = "使用旧 PIN 和新 PIN 初始化设备识别码。")
public ApiResponse<Void> initIdentify(@Valid @RequestBody CryptoInitIdentifyInternalRequest request) {
@@ -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;
}
}
+4
View File
@@ -315,6 +315,10 @@ tms:
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}
# 健康检查最大等待时间(秒);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:
init-identify:
# SDFE_InitIdentify 旧 PIN(Base64 编码);主密钥初始化接口会读取该值。