fix:备份恢复密钥
This commit is contained in:
+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.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不能为空");
|
||||
|
||||
+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.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;
|
||||
}
|
||||
|
||||
+44
-80
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -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()) {
|
||||
|
||||
+17
-1
@@ -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) {
|
||||
|
||||
+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-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 编码);主密钥初始化接口会读取该值。
|
||||
|
||||
Reference in New Issue
Block a user