对称运算类接口
This commit is contained in:
parent
bd099ba04e
commit
9cb9b43052
@ -20,6 +20,11 @@
|
|||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sunyard.chsm</groupId>
|
||||||
|
<artifactId>chsm-params</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
@ -66,5 +71,4 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -1,6 +1,7 @@
|
|||||||
package com.sunyard.chsm.sdf;
|
package com.sunyard.chsm.sdf;
|
||||||
|
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
import com.sunyard.chsm.sdf.context.AlgId;
|
import com.sunyard.chsm.sdf.context.AlgId;
|
||||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||||
import com.sunyard.chsm.sdf.model.EccKey;
|
import com.sunyard.chsm.sdf.model.EccKey;
|
||||||
@ -40,6 +41,7 @@ public interface SdfApiService {
|
|||||||
* 对称加密
|
* 对称加密
|
||||||
*
|
*
|
||||||
* @param alg 算法,只支持对称算法
|
* @param alg 算法,只支持对称算法
|
||||||
|
* @param padding
|
||||||
* @param key 密钥值,明文
|
* @param key 密钥值,明文
|
||||||
* @param data 原始数据
|
* @param data 原始数据
|
||||||
*/
|
*/
|
||||||
@ -47,22 +49,22 @@ public interface SdfApiService {
|
|||||||
//
|
//
|
||||||
// byte[] symEncrypt(KeyAlg alg, byte[] key, byte[] data);
|
// byte[] symEncrypt(KeyAlg alg, byte[] key, byte[] data);
|
||||||
|
|
||||||
byte[] symEncrypt(AlgId alg, byte[] key, byte[] iv, byte[] data);
|
byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对称解密
|
* 对称解密
|
||||||
*
|
*
|
||||||
* @param alg 算法,只支持对称算法
|
|
||||||
* @param key 密钥值,明文
|
|
||||||
* @param mode 轮模式
|
* @param mode 轮模式
|
||||||
|
* @param alg 算法,只支持对称算法
|
||||||
* @param padding 填充模式
|
* @param padding 填充模式
|
||||||
|
* @param key 密钥值,明文
|
||||||
* @param data 密文数据
|
* @param data 密文数据
|
||||||
*/
|
*/
|
||||||
// byte[] symDecrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data);
|
// byte[] symDecrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data);
|
||||||
//
|
//
|
||||||
// byte[] symDecrypt(KeyAlg alg, byte[] key, byte[] data);
|
// byte[] symDecrypt(KeyAlg alg, byte[] key, byte[] data);
|
||||||
|
|
||||||
byte[] symDecrypt(AlgId alg, byte[] key, byte[] iv, byte[] data);
|
byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,12 +121,14 @@ public interface SdfApiService {
|
|||||||
/**
|
/**
|
||||||
* 计算MAC
|
* 计算MAC
|
||||||
*
|
*
|
||||||
|
* @param algId algId
|
||||||
|
* @param padding padding
|
||||||
* @param symKey 用户指定的密钥
|
* @param symKey 用户指定的密钥
|
||||||
* @param pucIv 缓冲区指针,用于存放输入和返回的IV数据
|
* @param pucIv 缓冲区指针,用于存放输入和返回的IV数据
|
||||||
* @param pucData 缓冲区指针,用于存放输入的数据明文
|
* @param pucData 缓冲区指针,用于存放输入的数据明文
|
||||||
* @return pucEncData 返回MAC值 | puiLength 返回MAC值长度
|
* @return pucEncData 返回MAC值 | puiLength 返回MAC值长度
|
||||||
*/
|
*/
|
||||||
byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData);
|
byte[] calculateMAC(AlgId algId, Padding padding, byte[] symKey, byte[] pucIv, byte[] pucData);
|
||||||
|
|
||||||
byte[] hmac(byte[] key, byte[] srcData);
|
byte[] hmac(byte[] key, byte[] srcData);
|
||||||
|
|
||||||
|
@ -1,10 +1,38 @@
|
|||||||
package com.sunyard.chsm.sdf.util;
|
package com.sunyard.chsm.sdf.util;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author liulu
|
* @author liulu
|
||||||
*/
|
*/
|
||||||
public abstract class PaddingUtil {
|
public abstract class PaddingUtil {
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] padding(Padding padding, byte[] data) {
|
||||||
|
switch (padding) {
|
||||||
|
case NOPadding:
|
||||||
|
return data;
|
||||||
|
case PCKS5Padding:
|
||||||
|
return PKCS5Padding(data);
|
||||||
|
case PCKS7Padding:
|
||||||
|
return PKCS7Padding(data);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] unpadding(Padding padding, byte[] data) {
|
||||||
|
switch (padding) {
|
||||||
|
case NOPadding:
|
||||||
|
return data;
|
||||||
|
case PCKS5Padding:
|
||||||
|
return PKCS5Unpadding(data);
|
||||||
|
case PCKS7Padding:
|
||||||
|
return PKCS7Unpadding(data);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static byte[] PKCS7Padding(byte[] content, int blockSize) {
|
public static byte[] PKCS7Padding(byte[] content, int blockSize) {
|
||||||
if (content == null || blockSize < 8)
|
if (content == null || blockSize < 8)
|
||||||
throw new IllegalStateException("parameter error");
|
throw new IllegalStateException("parameter error");
|
||||||
@ -34,10 +62,9 @@ public abstract class PaddingUtil {
|
|||||||
return PKCS7Padding(content, 8);
|
return PKCS7Padding(content, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] PKCS7Unpadding(byte[] content, int blockSize)
|
public static byte[] PKCS7Unpadding(byte[] content, int blockSize) {
|
||||||
throws Exception {
|
|
||||||
if (blockSize < 8 || content == null || content.length % blockSize != 0)
|
if (blockSize < 8 || content == null || content.length % blockSize != 0)
|
||||||
throw new Exception("parameter error");
|
throw new IllegalStateException("parameter error");
|
||||||
return PKCS7Unpadding(content);
|
return PKCS7Unpadding(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +84,8 @@ public abstract class PaddingUtil {
|
|||||||
return unpadded;
|
return unpadded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] PKCS5Unpadding(byte[] content) throws Exception {
|
public static byte[] PKCS5Unpadding(byte[] content) {
|
||||||
return PKCS7Unpadding(content, 8);
|
return PKCS7Unpadding(content, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.sunyard.chsm.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liulu
|
||||||
|
* @since 2024/10/22
|
||||||
|
*/
|
||||||
|
public enum HashAlg {
|
||||||
|
SM3,
|
||||||
|
;
|
||||||
|
}
|
@ -15,7 +15,7 @@ public class KeyInfoResp {
|
|||||||
/**
|
/**
|
||||||
* 密钥ID
|
* 密钥ID
|
||||||
*/
|
*/
|
||||||
private String KeyId;
|
private Long KeyId;
|
||||||
/**
|
/**
|
||||||
* 密钥算法
|
* 密钥算法
|
||||||
*/
|
*/
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.AlgMode;
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymDecryptReq {
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 密钥索引
|
||||||
|
@NotEmpty(message = "密钥索引不能为空")
|
||||||
|
@Size(min = 15, max = 24, message = "密钥索引长度在15-24")
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// 密文,使用Base64编码
|
||||||
|
@NotBlank(message = "密文不能为空")
|
||||||
|
private String cipherData;
|
||||||
|
|
||||||
|
// 填充方式, 默认PCKS7
|
||||||
|
@NotNull(message = "填充方式不能为空")
|
||||||
|
private Padding padding = Padding.PCKS7Padding;
|
||||||
|
|
||||||
|
// 加密模式, 默认ECB
|
||||||
|
@NotNull(message = "加密模式不能为空")
|
||||||
|
private AlgMode mode = AlgMode.ECB;
|
||||||
|
|
||||||
|
// iv,CBC模式下不能为空,Base64编码
|
||||||
|
private String iv;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymDecryptResp {
|
||||||
|
// 密钥ID
|
||||||
|
private Long keyId;
|
||||||
|
// 密钥索引
|
||||||
|
private String keyIndex;
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
private String plainData;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.AlgMode;
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymEncryptReq {
|
||||||
|
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 待加密明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
|
||||||
|
// 填充方式, 默认PCKS7
|
||||||
|
private Padding padding = Padding.PCKS7Padding;
|
||||||
|
|
||||||
|
// 加密模式, 默认ECB
|
||||||
|
@NotNull(message = "加密模式不能为空")
|
||||||
|
private AlgMode mode = AlgMode.ECB;
|
||||||
|
|
||||||
|
// iv, CBC模式下不能为空,Base64编码
|
||||||
|
private String iv;
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymEncryptResp {
|
||||||
|
// 密钥ID
|
||||||
|
private Long keyId;
|
||||||
|
// 密钥索引
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// 密文,使用Base64编码
|
||||||
|
private String cipherData;
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.HashAlg;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymHmacCheckReq {
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 密钥索引
|
||||||
|
@NotEmpty(message = "密钥索引不能为空")
|
||||||
|
@Size(min = 15, max = 24, message = "密钥索引长度在15-24")
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// Hash算法,默认SM3
|
||||||
|
private HashAlg hashAlg = HashAlg.SM3;
|
||||||
|
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
|
||||||
|
// hmac值,使用Base64编码
|
||||||
|
@NotBlank(message = "hmac不能为空")
|
||||||
|
private String hmac;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymHmacCheckResp {
|
||||||
|
|
||||||
|
private Boolean valid;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.HashAlg;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymHmacReq {
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
|
||||||
|
// Hash算法,默认SM3
|
||||||
|
private HashAlg hashAlg = HashAlg.SM3;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymHmacResp {
|
||||||
|
// 密钥ID
|
||||||
|
private Long keyId;
|
||||||
|
// 密钥索引
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// hmac值,使用Base64编码
|
||||||
|
private String hmac;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymMacCheckReq {
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 密钥索引
|
||||||
|
@NotEmpty(message = "密钥索引不能为空")
|
||||||
|
@Size(min = 15, max = 24, message = "密钥索引长度在15-24")
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// 填充方式, 默认PCKS7
|
||||||
|
private Padding padding = Padding.PCKS7Padding;
|
||||||
|
|
||||||
|
// iv,Base64编码
|
||||||
|
@NotBlank(message = "iv不能为空")
|
||||||
|
private String iv;
|
||||||
|
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
|
||||||
|
// mac值,使用Base64编码
|
||||||
|
@NotBlank(message = "mac不能为空")
|
||||||
|
private String mac;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymMacCheckResp {
|
||||||
|
|
||||||
|
private Boolean valid;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymMacReq {
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
|
||||||
|
// 填充方式, 默认PCKS7
|
||||||
|
private Padding padding = Padding.PCKS7Padding;
|
||||||
|
|
||||||
|
// iv,Base64编码
|
||||||
|
@NotBlank(message = "iv不能为空")
|
||||||
|
private String iv;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SymMacResp {
|
||||||
|
// 密钥ID
|
||||||
|
private Long keyId;
|
||||||
|
// 密钥索引
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// mac值,使用Base64编码
|
||||||
|
private String mac;
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.sunyard.chsm.sdf;
|
package com.sunyard.chsm.sdf;
|
||||||
|
|
||||||
import com.sunyard.chsm.enums.DeviceTmkStatus;
|
import com.sunyard.chsm.enums.DeviceTmkStatus;
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
import com.sunyard.chsm.mapper.SpDeviceMapper;
|
import com.sunyard.chsm.mapper.SpDeviceMapper;
|
||||||
import com.sunyard.chsm.model.dto.DeviceCheckRes;
|
import com.sunyard.chsm.model.dto.DeviceCheckRes;
|
||||||
import com.sunyard.chsm.model.entity.Device;
|
import com.sunyard.chsm.model.entity.Device;
|
||||||
@ -62,9 +63,9 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] symEncrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) {
|
public byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) {
|
||||||
checkStatus();
|
checkStatus();
|
||||||
byte[] pad = PaddingUtil.PKCS7Padding(data);
|
byte[] pad = PaddingUtil.padding(padding, data);
|
||||||
String hk = sdfApiAdapter.importKey(sessionHandle, key);
|
String hk = sdfApiAdapter.importKey(sessionHandle, key);
|
||||||
byte[] encrypt = sdfApiAdapter.symEncrypt(sessionHandle, hk, alg, iv, pad);
|
byte[] encrypt = sdfApiAdapter.symEncrypt(sessionHandle, hk, alg, iv, pad);
|
||||||
sdfApiAdapter.destroyKey(sessionHandle, hk);
|
sdfApiAdapter.destroyKey(sessionHandle, hk);
|
||||||
@ -72,12 +73,12 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] symDecrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) {
|
public byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) {
|
||||||
checkStatus();
|
checkStatus();
|
||||||
String hk = sdfApiAdapter.importKey(sessionHandle, key);
|
String hk = sdfApiAdapter.importKey(sessionHandle, key);
|
||||||
byte[] decrypt = sdfApiAdapter.symDecrypt(sessionHandle, hk, alg, iv, data);
|
byte[] decrypt = sdfApiAdapter.symDecrypt(sessionHandle, hk, alg, iv, data);
|
||||||
sdfApiAdapter.destroyKey(sessionHandle, hk);
|
sdfApiAdapter.destroyKey(sessionHandle, hk);
|
||||||
return PaddingUtil.PKCS7Unpadding(decrypt);
|
return PaddingUtil.unpadding(padding, decrypt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -138,10 +139,11 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) {
|
public byte[] calculateMAC(AlgId algId, Padding padding, byte[] symKey, byte[] pucIv, byte[] pucData) {
|
||||||
checkStatus();
|
checkStatus();
|
||||||
|
byte[] pad = PaddingUtil.padding(padding, pucData);
|
||||||
String hk = sdfApiAdapter.importKey(sessionHandle, symKey);
|
String hk = sdfApiAdapter.importKey(sessionHandle, symKey);
|
||||||
byte[] mac = sdfApiAdapter.calculateMAC(sessionHandle, hk, AlgId.SGD_SM4_MAC, pucIv, PaddingUtil.PKCS7Padding(pucData));
|
byte[] mac = sdfApiAdapter.calculateMAC(sessionHandle, hk, algId, pucIv, pad);
|
||||||
sdfApiAdapter.destroyKey(sessionHandle, hk);
|
sdfApiAdapter.destroyKey(sessionHandle, hk);
|
||||||
return mac;
|
return mac;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package sdf;
|
package sdf;
|
||||||
|
|
||||||
import com.sunyard.chsm.enums.ManufacturerModelEnum;
|
import com.sunyard.chsm.enums.ManufacturerModelEnum;
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
import com.sunyard.chsm.sdf.SdfApiService;
|
import com.sunyard.chsm.sdf.SdfApiService;
|
||||||
import com.sunyard.chsm.sdf.SingleSdfApiService;
|
import com.sunyard.chsm.sdf.SingleSdfApiService;
|
||||||
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
|
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
|
||||||
@ -92,35 +93,35 @@ public class SdfApiServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSymEncAndDec() {
|
public void testSymEncAndDec() {
|
||||||
byte[] ecbCipher = sdfService.symEncrypt(AlgId.SGD_SM4_ECB, symKey, null, plain.getBytes());
|
byte[] ecbCipher = sdfService.symEncrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, plain.getBytes());
|
||||||
byte[] ecbPlain = sdfService.symDecrypt(AlgId.SGD_SM4_ECB, symKey, null, ecbCipher);
|
byte[] ecbPlain = sdfService.symDecrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, ecbCipher);
|
||||||
log.info("ecb_cipher: {}", CodecUtils.encodeHex(ecbCipher));
|
log.info("ecb_cipher: {}", CodecUtils.encodeHex(ecbCipher));
|
||||||
Assertions.assertEquals(plain, new String(ecbPlain));
|
Assertions.assertEquals(plain, new String(ecbPlain));
|
||||||
|
|
||||||
byte[] cbcCipher = sdfService.symEncrypt(AlgId.SGD_SM4_CBC, symKey, iv, plain.getBytes());
|
byte[] cbcCipher = sdfService.symEncrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
|
||||||
log.info("cbc_cipher: {}", CodecUtils.encodeHex(cbcCipher));
|
log.info("cbc_cipher: {}", CodecUtils.encodeHex(cbcCipher));
|
||||||
byte[] cbcPlain = sdfService.symDecrypt(AlgId.SGD_SM4_CBC, symKey, iv, cbcCipher);
|
byte[] cbcPlain = sdfService.symDecrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, cbcCipher);
|
||||||
Assertions.assertEquals(plain, new String(cbcPlain));
|
Assertions.assertEquals(plain, new String(cbcPlain));
|
||||||
|
|
||||||
Assertions.assertArrayEquals(ecbPlain, cbcPlain);
|
Assertions.assertArrayEquals(ecbPlain, cbcPlain);
|
||||||
Assertions.assertNotEquals(CodecUtils.encodeHex(ecbCipher), CodecUtils.encodeHex(cbcCipher));
|
Assertions.assertNotEquals(CodecUtils.encodeHex(ecbCipher), CodecUtils.encodeHex(cbcCipher));
|
||||||
|
|
||||||
|
|
||||||
byte[] bcEcbCipher = bcService.symEncrypt(AlgId.SGD_SM4_ECB, symKey, null, plain.getBytes());
|
byte[] bcEcbCipher = bcService.symEncrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, plain.getBytes());
|
||||||
log.info("bc_ecb_cipher: {}", CodecUtils.encodeHex(bcEcbCipher));
|
log.info("bc_ecb_cipher: {}", CodecUtils.encodeHex(bcEcbCipher));
|
||||||
Assertions.assertArrayEquals(ecbCipher, bcEcbCipher);
|
Assertions.assertArrayEquals(ecbCipher, bcEcbCipher);
|
||||||
byte[] bcCbcCipher = bcService.symEncrypt(AlgId.SGD_SM4_CBC, symKey, iv, plain.getBytes());
|
byte[] bcCbcCipher = bcService.symEncrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
|
||||||
log.info("bc_cbc_cipher: {}", CodecUtils.encodeHex(bcCbcCipher));
|
log.info("bc_cbc_cipher: {}", CodecUtils.encodeHex(bcCbcCipher));
|
||||||
Assertions.assertArrayEquals(cbcCipher, bcCbcCipher);
|
Assertions.assertArrayEquals(cbcCipher, bcCbcCipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSm4Mac() {
|
public void testSm4Mac() {
|
||||||
byte[] sdfMac = sdfService.calculateMAC(symKey, iv, plain.getBytes());
|
byte[] sdfMac = sdfService.calculateMAC(AlgId.SGD_SM4_MAC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
|
||||||
log.info("sdf mac: {}", CodecUtils.encodeHex(sdfMac));
|
log.info("sdf mac: {}", CodecUtils.encodeHex(sdfMac));
|
||||||
Assertions.assertEquals(16, sdfMac.length);
|
Assertions.assertEquals(16, sdfMac.length);
|
||||||
|
|
||||||
byte[] bcMac = bcService.calculateMAC(symKey, iv, plain.getBytes());
|
byte[] bcMac = bcService.calculateMAC(AlgId.SGD_SM4_MAC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
|
||||||
log.info("bc mac: {}", CodecUtils.encodeHex(bcMac));
|
log.info("bc mac: {}", CodecUtils.encodeHex(bcMac));
|
||||||
Assertions.assertEquals(16, bcMac.length);
|
Assertions.assertEquals(16, bcMac.length);
|
||||||
|
|
||||||
|
@ -24,11 +24,6 @@
|
|||||||
<artifactId>chsm-common</artifactId>
|
<artifactId>chsm-common</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.sunyard.chsm</groupId>
|
|
||||||
<artifactId>chsm-params</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.dm</groupId>
|
<groupId>com.dm</groupId>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.sunyard.chsm.controller;
|
package com.sunyard.chsm.controller;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.model.R;
|
||||||
import com.sunyard.chsm.param.AppTokenReq;
|
import com.sunyard.chsm.param.AppTokenReq;
|
||||||
import com.sunyard.chsm.param.AppTokenResp;
|
import com.sunyard.chsm.param.AppTokenResp;
|
||||||
import com.sunyard.chsm.service.AppLoginService;
|
import com.sunyard.chsm.service.AppLoginService;
|
||||||
@ -35,15 +36,16 @@ public class AppLoginController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@PostMapping("/appUser/getAppToken")
|
@PostMapping("/appUser/getAppToken")
|
||||||
public AppTokenResp getAppToken(@Valid @RequestBody AppTokenReq appTokenReq) {
|
public R<AppTokenResp> getAppToken(@Valid @RequestBody AppTokenReq appTokenReq) {
|
||||||
return appLoginService.getAppToken(appTokenReq);
|
AppTokenResp appToken = appLoginService.getAppToken(appTokenReq);
|
||||||
|
return R.data(appToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/appUser/getAppTokenTest")
|
@PostMapping("/appUser/getAppTokenTest")
|
||||||
public AppTokenResp getAppTokenForTest(@Valid @RequestBody AppTokenReq appTokenReq) {
|
public R<AppTokenResp> getAppTokenForTest(@Valid @RequestBody AppTokenReq appTokenReq) {
|
||||||
return appLoginService.getAppTokenForTest(appTokenReq);
|
AppTokenResp appToken = appLoginService.getAppTokenForTest(appTokenReq);
|
||||||
|
return R.data(appToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ public class KeyManageController {
|
|||||||
* @return 分页列表
|
* @return 分页列表
|
||||||
*/
|
*/
|
||||||
@PostMapping("/info")
|
@PostMapping("/info")
|
||||||
public R<KeyInfoResp> queryInfo(Long id) {
|
public R<KeyInfoResp> queryInfo(@RequestBody Long id) {
|
||||||
Assert.notNull(id, "密钥id不能为空");
|
Assert.notNull(id, "密钥id不能为空");
|
||||||
KeyInfoResp resp = keyManageService.queryInfo(id);
|
KeyInfoResp resp = keyManageService.queryInfo(id);
|
||||||
return R.data(resp);
|
return R.data(resp);
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
package com.sunyard.chsm.controller;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.model.R;
|
||||||
|
import com.sunyard.chsm.param.SymDecryptReq;
|
||||||
|
import com.sunyard.chsm.param.SymDecryptResp;
|
||||||
|
import com.sunyard.chsm.param.SymEncryptReq;
|
||||||
|
import com.sunyard.chsm.param.SymEncryptResp;
|
||||||
|
import com.sunyard.chsm.param.SymHmacCheckReq;
|
||||||
|
import com.sunyard.chsm.param.SymHmacCheckResp;
|
||||||
|
import com.sunyard.chsm.param.SymHmacReq;
|
||||||
|
import com.sunyard.chsm.param.SymHmacResp;
|
||||||
|
import com.sunyard.chsm.param.SymMacCheckReq;
|
||||||
|
import com.sunyard.chsm.param.SymMacCheckResp;
|
||||||
|
import com.sunyard.chsm.param.SymMacReq;
|
||||||
|
import com.sunyard.chsm.param.SymMacResp;
|
||||||
|
import com.sunyard.chsm.service.SymKeyService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称运算类接口
|
||||||
|
*
|
||||||
|
* @author liulu
|
||||||
|
* @since 2024/12/17
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/sym")
|
||||||
|
public class SymKeyController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SymKeyService symKeyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称加密
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/encrypt")
|
||||||
|
public R<SymEncryptResp> encrypt(@Valid @RequestBody SymEncryptReq req) {
|
||||||
|
SymEncryptResp resp = symKeyService.encrypt(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称解密
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/decrypt")
|
||||||
|
public R<SymDecryptResp> decrypt(@Valid @RequestBody SymDecryptReq req) {
|
||||||
|
SymDecryptResp resp = symKeyService.decrypt(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算Hmac
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/hmac")
|
||||||
|
public R<SymHmacResp> hmac(@Valid @RequestBody SymHmacReq req) {
|
||||||
|
SymHmacResp resp = symKeyService.hmac(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证Hmac
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/hmac/check")
|
||||||
|
public R<SymHmacCheckResp> macCheck(@Valid @RequestBody SymHmacCheckReq req) {
|
||||||
|
SymHmacCheckResp resp = symKeyService.hmacCheck(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算Hmac
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/mac")
|
||||||
|
public R<SymMacResp> mac(@Valid @RequestBody SymMacReq req) {
|
||||||
|
SymMacResp resp = symKeyService.mac(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证Hmac
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/mac/check")
|
||||||
|
public R<SymMacCheckResp> macCheck(@Valid @RequestBody SymMacCheckReq req) {
|
||||||
|
SymMacCheckResp resp = symKeyService.macCheck(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.sunyard.chsm.pool;
|
package com.sunyard.chsm.pool;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
import com.sunyard.chsm.sdf.SdfApiService;
|
import com.sunyard.chsm.sdf.SdfApiService;
|
||||||
import com.sunyard.chsm.sdf.context.AlgId;
|
import com.sunyard.chsm.sdf.context.AlgId;
|
||||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||||
@ -46,24 +47,25 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] symEncrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) {
|
public byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) {
|
||||||
|
byte[] paddingData = PaddingUtil.padding(padding, data);
|
||||||
return apply(s -> {
|
return apply(s -> {
|
||||||
String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key);
|
String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key);
|
||||||
byte[] encrypt = s.getSdfApiAdapter().symEncrypt(s.getSessionHandle(), keyHandle, alg, iv, PaddingUtil.PKCS7Padding(data));
|
byte[] encrypt = s.getSdfApiAdapter().symEncrypt(s.getSessionHandle(), keyHandle, alg, iv, paddingData);
|
||||||
s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle);
|
s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle);
|
||||||
return encrypt;
|
return encrypt;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] symDecrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) {
|
public byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) {
|
||||||
byte[] decrypt = apply(s -> {
|
byte[] decrypt = apply(s -> {
|
||||||
String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key);
|
String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key);
|
||||||
byte[] d = s.getSdfApiAdapter().symDecrypt(s.getSessionHandle(), keyHandle, alg, iv, data);
|
byte[] d = s.getSdfApiAdapter().symDecrypt(s.getSessionHandle(), keyHandle, alg, iv, data);
|
||||||
s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle);
|
s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle);
|
||||||
return d;
|
return d;
|
||||||
});
|
});
|
||||||
return PaddingUtil.PKCS7Unpadding(decrypt);
|
return PaddingUtil.unpadding(padding, decrypt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -125,10 +127,11 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) {
|
public byte[] calculateMAC(AlgId algId, Padding padding, byte[] symKey, byte[] pucIv, byte[] pucData) {
|
||||||
|
byte[] pad = PaddingUtil.padding(padding, pucData);
|
||||||
return apply(s -> {
|
return apply(s -> {
|
||||||
String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), symKey);
|
String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), symKey);
|
||||||
byte[] mac = s.getSdfApiAdapter().calculateMAC(s.getSessionHandle(), keyHandle, AlgId.SGD_SM4_MAC, pucIv, PaddingUtil.PKCS7Padding(pucData));
|
byte[] mac = s.getSdfApiAdapter().calculateMAC(s.getSessionHandle(), keyHandle, algId, pucIv, pad);
|
||||||
s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle);
|
s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle);
|
||||||
return mac;
|
return mac;
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,8 @@ package com.sunyard.chsm.service;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.sunyard.chsm.auth.AppUser;
|
import com.sunyard.chsm.auth.AppUser;
|
||||||
import com.sunyard.chsm.auth.UserContext;
|
import com.sunyard.chsm.auth.UserContext;
|
||||||
|
import com.sunyard.chsm.enums.KeyCategory;
|
||||||
|
import com.sunyard.chsm.enums.KeyStatus;
|
||||||
import com.sunyard.chsm.mapper.KeyInfoMapper;
|
import com.sunyard.chsm.mapper.KeyInfoMapper;
|
||||||
import com.sunyard.chsm.model.dto.KeyInfoDTO;
|
import com.sunyard.chsm.model.dto.KeyInfoDTO;
|
||||||
import com.sunyard.chsm.model.entity.KeyInfo;
|
import com.sunyard.chsm.model.entity.KeyInfo;
|
||||||
@ -22,6 +24,7 @@ import javax.annotation.Resource;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +43,8 @@ public class KeyManageService {
|
|||||||
|
|
||||||
public Page<KeyInfoResp> queryPageList(KeyInfoQuery query) {
|
public Page<KeyInfoResp> queryPageList(KeyInfoQuery query) {
|
||||||
KeyInfoDTO.Query nq = new KeyInfoDTO.Query();
|
KeyInfoDTO.Query nq = new KeyInfoDTO.Query();
|
||||||
BeanUtils.copyProperties(query, nq);
|
nq.setKeyType(Optional.ofNullable(query.getKeyType()).map(KeyCategory::getCode).orElse(null));
|
||||||
|
nq.setStatus(Optional.ofNullable(query.getStatus()).map(KeyStatus::getCode).orElse(null));
|
||||||
nq.setAppId(UserContext.getCurrentAppId());
|
nq.setAppId(UserContext.getCurrentAppId());
|
||||||
Page<KeyInfoDTO.KeyView> page = keyInfoService.selectPageList(nq);
|
Page<KeyInfoDTO.KeyView> page = keyInfoService.selectPageList(nq);
|
||||||
List<KeyInfoDTO.KeyView> records = page.getRecords();
|
List<KeyInfoDTO.KeyView> records = page.getRecords();
|
||||||
@ -51,7 +55,7 @@ public class KeyManageService {
|
|||||||
.map(it -> {
|
.map(it -> {
|
||||||
KeyInfoResp resp = new KeyInfoResp();
|
KeyInfoResp resp = new KeyInfoResp();
|
||||||
BeanUtils.copyProperties(it, resp);
|
BeanUtils.copyProperties(it, resp);
|
||||||
resp.setKeyId(it.getCode());
|
resp.setKeyId(it.getId());
|
||||||
return resp;
|
return resp;
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@ -64,7 +68,7 @@ public class KeyManageService {
|
|||||||
KeyInfoDTO.KeyView view = keyInfoService.selectById(id);
|
KeyInfoDTO.KeyView view = keyInfoService.selectById(id);
|
||||||
KeyInfoResp resp = new KeyInfoResp();
|
KeyInfoResp resp = new KeyInfoResp();
|
||||||
BeanUtils.copyProperties(view, resp);
|
BeanUtils.copyProperties(view, resp);
|
||||||
resp.setKeyId(view.getCode());
|
resp.setKeyId(view.getId());
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,231 @@
|
|||||||
|
package com.sunyard.chsm.service;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.auth.UserContext;
|
||||||
|
import com.sunyard.chsm.enums.AlgMode;
|
||||||
|
import com.sunyard.chsm.enums.KeyAlg;
|
||||||
|
import com.sunyard.chsm.enums.KeyCategory;
|
||||||
|
import com.sunyard.chsm.enums.KeyStatus;
|
||||||
|
import com.sunyard.chsm.enums.KeyUsage;
|
||||||
|
import com.sunyard.chsm.enums.Padding;
|
||||||
|
import com.sunyard.chsm.mapper.KeyInfoMapper;
|
||||||
|
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
|
||||||
|
import com.sunyard.chsm.model.entity.KeyInfo;
|
||||||
|
import com.sunyard.chsm.model.entity.KeyRecord;
|
||||||
|
import com.sunyard.chsm.param.SymDecryptReq;
|
||||||
|
import com.sunyard.chsm.param.SymDecryptResp;
|
||||||
|
import com.sunyard.chsm.param.SymEncryptReq;
|
||||||
|
import com.sunyard.chsm.param.SymEncryptResp;
|
||||||
|
import com.sunyard.chsm.param.SymHmacCheckReq;
|
||||||
|
import com.sunyard.chsm.param.SymHmacCheckResp;
|
||||||
|
import com.sunyard.chsm.param.SymHmacReq;
|
||||||
|
import com.sunyard.chsm.param.SymHmacResp;
|
||||||
|
import com.sunyard.chsm.param.SymMacCheckReq;
|
||||||
|
import com.sunyard.chsm.param.SymMacCheckResp;
|
||||||
|
import com.sunyard.chsm.param.SymMacReq;
|
||||||
|
import com.sunyard.chsm.param.SymMacResp;
|
||||||
|
import com.sunyard.chsm.sdf.SdfApiService;
|
||||||
|
import com.sunyard.chsm.sdf.context.AlgId;
|
||||||
|
import com.sunyard.chsm.utils.CodecUtils;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liulu
|
||||||
|
* @since 2024/12/17
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SymKeyService {
|
||||||
|
|
||||||
|
private final KeyInfoMapper keyInfoMapper;
|
||||||
|
private final SpKeyRecordMapper spKeyRecordMapper;
|
||||||
|
private final SdfApiService sdfApiService;
|
||||||
|
|
||||||
|
|
||||||
|
public SymEncryptResp encrypt(SymEncryptReq req) {
|
||||||
|
byte[] iv = new byte[0];
|
||||||
|
if (AlgMode.CBC == req.getMode()) {
|
||||||
|
Assert.hasText(req.getIv(), "CBC模式iv不能为空");
|
||||||
|
byte[] bytes = CodecUtils.decodeBase64(req.getIv());
|
||||||
|
Assert.isTrue(bytes.length >= 16, "iv长度至少为16");
|
||||||
|
iv = bytes;
|
||||||
|
}
|
||||||
|
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.ENCRYPT_DECRYPT);
|
||||||
|
KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg());
|
||||||
|
Assert.notNull(keyAlg, "数据异常");
|
||||||
|
AlgId algId = null;
|
||||||
|
switch (keyAlg) {
|
||||||
|
case SM4:
|
||||||
|
if (Padding.PCKS5Padding == req.getPadding()) {
|
||||||
|
req.setPadding(Padding.PCKS7Padding);
|
||||||
|
}
|
||||||
|
switch (req.getMode()) {
|
||||||
|
case ECB:
|
||||||
|
algId = AlgId.SGD_SM4_ECB;
|
||||||
|
break;
|
||||||
|
case CBC:
|
||||||
|
algId = AlgId.SGD_SM4_CBC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode());
|
||||||
|
}
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId());
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
|
byte[] cipherData = sdfApiService.symEncrypt(algId, req.getPadding(), symKey, iv, plain);
|
||||||
|
|
||||||
|
SymEncryptResp resp = new SymEncryptResp();
|
||||||
|
resp.setKeyId(keyInfo.getId());
|
||||||
|
resp.setKeyIndex(keyRecord.getKeyIndex());
|
||||||
|
resp.setCipherData(CodecUtils.encodeBase64(cipherData));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SymDecryptResp decrypt(SymDecryptReq req) {
|
||||||
|
byte[] iv = new byte[0];
|
||||||
|
if (AlgMode.CBC == req.getMode()) {
|
||||||
|
Assert.hasText(req.getIv(), "CBC模式iv不能为空");
|
||||||
|
byte[] bytes = CodecUtils.decodeBase64(req.getIv());
|
||||||
|
Assert.isTrue(bytes.length >= 16, "iv长度至少为16");
|
||||||
|
iv = bytes;
|
||||||
|
}
|
||||||
|
byte[] cipher = CodecUtils.decodeBase64(req.getCipherData());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.ENCRYPT_DECRYPT);
|
||||||
|
KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg());
|
||||||
|
Assert.notNull(keyAlg, "数据异常");
|
||||||
|
AlgId algId = null;
|
||||||
|
switch (keyAlg) {
|
||||||
|
case SM4:
|
||||||
|
if (Padding.PCKS5Padding == req.getPadding()) {
|
||||||
|
req.setPadding(Padding.PCKS7Padding);
|
||||||
|
}
|
||||||
|
switch (req.getMode()) {
|
||||||
|
case ECB:
|
||||||
|
algId = AlgId.SGD_SM4_ECB;
|
||||||
|
break;
|
||||||
|
case CBC:
|
||||||
|
algId = AlgId.SGD_SM4_CBC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode());
|
||||||
|
}
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex()));
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配");
|
||||||
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
|
byte[] plain = sdfApiService.symDecrypt(algId, req.getPadding(), symKey, iv, cipher);
|
||||||
|
|
||||||
|
SymDecryptResp resp = new SymDecryptResp();
|
||||||
|
resp.setKeyId(keyInfo.getId());
|
||||||
|
resp.setKeyIndex(keyRecord.getKeyIndex());
|
||||||
|
resp.setPlainData(CodecUtils.encodeBase64(plain));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SymHmacResp hmac(SymHmacReq req) {
|
||||||
|
|
||||||
|
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.HMAC);
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId());
|
||||||
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
|
byte[] hmac = sdfApiService.hmac(symKey, plain);
|
||||||
|
|
||||||
|
SymHmacResp resp = new SymHmacResp();
|
||||||
|
resp.setKeyId(keyInfo.getId());
|
||||||
|
resp.setKeyIndex(keyRecord.getKeyIndex());
|
||||||
|
resp.setHmac(CodecUtils.encodeBase64(hmac));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymHmacCheckResp hmacCheck(SymHmacCheckReq req) {
|
||||||
|
|
||||||
|
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
byte[] originHmac = CodecUtils.decodeBase64(req.getHmac());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.HMAC);
|
||||||
|
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex()));
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配");
|
||||||
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
|
byte[] hmac = sdfApiService.hmac(symKey, plain);
|
||||||
|
|
||||||
|
SymHmacCheckResp resp = new SymHmacCheckResp();
|
||||||
|
resp.setValid(Arrays.equals(hmac, originHmac));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SymMacResp mac(SymMacReq req) {
|
||||||
|
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
byte[] iv = CodecUtils.decodeBase64(req.getIv());
|
||||||
|
Assert.isTrue(iv.length >= 16, "iv长度至少为16");
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.MAC);
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId());
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
if (Padding.PCKS5Padding == req.getPadding()) {
|
||||||
|
req.setPadding(Padding.PCKS7Padding);
|
||||||
|
}
|
||||||
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
|
byte[] mac = sdfApiService.calculateMAC(AlgId.SGD_SM4_MAC, req.getPadding(), symKey, iv, plain);
|
||||||
|
|
||||||
|
SymMacResp resp = new SymMacResp();
|
||||||
|
resp.setKeyId(keyInfo.getId());
|
||||||
|
resp.setKeyIndex(keyRecord.getKeyIndex());
|
||||||
|
resp.setMac(CodecUtils.encodeBase64(mac));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SymMacCheckResp macCheck(SymMacCheckReq req) {
|
||||||
|
|
||||||
|
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
byte[] iv = CodecUtils.decodeBase64(req.getIv());
|
||||||
|
byte[] originMac = CodecUtils.decodeBase64(req.getMac());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.MAC);
|
||||||
|
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex()));
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配");
|
||||||
|
if (Padding.PCKS5Padding == req.getPadding()) {
|
||||||
|
req.setPadding(Padding.PCKS7Padding);
|
||||||
|
}
|
||||||
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
|
byte[] mac = sdfApiService.calculateMAC(AlgId.SGD_SM4_MAC, req.getPadding(), symKey, iv, plain);
|
||||||
|
|
||||||
|
SymMacCheckResp resp = new SymMacCheckResp();
|
||||||
|
resp.setValid(Arrays.equals(mac, originMac));
|
||||||
|
return resp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private KeyInfo checkKey(Long keyId, KeyUsage usage) {
|
||||||
|
|
||||||
|
KeyInfo keyInfo = keyInfoMapper.selectById(keyId);
|
||||||
|
Assert.notNull(keyInfo, "密钥ID不存在");
|
||||||
|
Assert.isTrue(Objects.equals(keyInfo.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID");
|
||||||
|
Assert.isTrue(KeyCategory.SYM_KEY.getCode().equals(keyInfo.getKeyType()), "此密钥不是对称密钥");
|
||||||
|
|
||||||
|
KeyStatus status = KeyStatus.of(keyInfo.getCode());
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
Assert.isTrue(KeyStatus.ENABLED == status, "此密钥不是启用状态, 无法操作");
|
||||||
|
Assert.isTrue(now.isAfter(keyInfo.getEffectiveTime()) && now.isBefore(keyInfo.getExpiredTime()), "此密钥不是启用状态, 无法操作");
|
||||||
|
Assert.isTrue(KeyUsage.hasUsage(keyInfo.getKeyUsage(), usage), "此密钥无权进行" + usage.getDesc() + "操作");
|
||||||
|
|
||||||
|
return keyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
70
chsm-web-server/src/test/java/api/BaseTest.java
Normal file
70
chsm-web-server/src/test/java/api/BaseTest.java
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package api;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.sunyard.chsm.model.R;
|
||||||
|
import com.sunyard.chsm.param.AppTokenReq;
|
||||||
|
import com.sunyard.chsm.param.AppTokenResp;
|
||||||
|
import com.sunyard.chsm.utils.JsonUtils;
|
||||||
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.RequestEntity;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liulu
|
||||||
|
* @since 2024/12/17
|
||||||
|
*/
|
||||||
|
public abstract class BaseTest {
|
||||||
|
|
||||||
|
protected static final String ak = "216205d408130d83d13c5072305b8b65";
|
||||||
|
protected static final String sk = "ae64515d1d5adec2cc6ae8726d0c1bbc";
|
||||||
|
protected static final String server = "http://127.0.0.1:8900";
|
||||||
|
protected static final RestTemplate restTemplate;
|
||||||
|
protected static final String token;
|
||||||
|
|
||||||
|
static {
|
||||||
|
AppTokenReq req = new AppTokenReq();
|
||||||
|
req.setAppKey(ak);
|
||||||
|
req.setHmac(sk);
|
||||||
|
req.setRandom(System.currentTimeMillis());
|
||||||
|
|
||||||
|
RequestEntity<byte[]> request = RequestEntity.post(server + "/appUser/getAppTokenTest")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.body(JsonUtils.toJsonBytes(req));
|
||||||
|
ResponseEntity<String> response = new RestTemplate().exchange(request, String.class);
|
||||||
|
try {
|
||||||
|
R<AppTokenResp> r = JsonUtils.objectMapper()
|
||||||
|
.readValue(response.getBody(), new TypeReference<R<AppTokenResp>>() {
|
||||||
|
});
|
||||||
|
token = r.getResult().getToken();
|
||||||
|
restTemplate = new RestTemplateBuilder()
|
||||||
|
.rootUri(server)
|
||||||
|
.defaultHeader("Authorization", "Bearer " + token)
|
||||||
|
.defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
.build();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <T> T execute(String url, Object req, Class<T> tClass) {
|
||||||
|
try {
|
||||||
|
RequestEntity<byte[]> request = RequestEntity.post("/appUser/getAppTokenTest")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.header("Authorization", "Bearer " + token)
|
||||||
|
.body(JsonUtils.toJsonBytes(req));
|
||||||
|
|
||||||
|
byte[] res = restTemplate.postForObject(url, JsonUtils.toJsonBytes(req), byte[].class);
|
||||||
|
R<T> r = JsonUtils.objectMapper()
|
||||||
|
.readValue(res, new TypeReference<R<T>>() {
|
||||||
|
});
|
||||||
|
return r.getResult();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
chsm-web-server/src/test/java/api/KeyManageTest.java
Normal file
48
chsm-web-server/src/test/java/api/KeyManageTest.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package api;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.sunyard.chsm.model.R;
|
||||||
|
import com.sunyard.chsm.model.entity.KeyInfo;
|
||||||
|
import com.sunyard.chsm.param.KeyInfoQuery;
|
||||||
|
import com.sunyard.chsm.param.KeyInfoResp;
|
||||||
|
import com.sunyard.chsm.utils.JsonUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liulu
|
||||||
|
* @since 2024/12/17
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class KeyManageTest extends BaseTest {
|
||||||
|
|
||||||
|
private static Long keyId;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void before() throws Exception {
|
||||||
|
KeyInfoQuery query = new KeyInfoQuery();
|
||||||
|
byte[] res = restTemplate.postForObject("/key/pageList", JsonUtils.toJsonBytes(query), byte[].class);
|
||||||
|
R<Page<KeyInfoResp>> r = JsonUtils.objectMapper()
|
||||||
|
.readValue(res, new TypeReference<R<Page<KeyInfoResp>>>() {
|
||||||
|
});
|
||||||
|
Assertions.assertTrue(r.isSuccess());
|
||||||
|
|
||||||
|
List<KeyInfoResp> records = r.getResult().getRecords();
|
||||||
|
Assertions.assertFalse(CollectionUtils.isEmpty(records));
|
||||||
|
keyId = records.iterator().next().getKeyId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keyInfoTest(){
|
||||||
|
KeyInfo res = execute("/key/info", keyId, KeyInfo.class);
|
||||||
|
log.info("keyInfoTest: {}", JsonUtils.toJsonString(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user