Compare commits
76 Commits
huangqian-
...
chsm-v1.0
Author | SHA1 | Date | |
---|---|---|---|
|
91c496ffcd | ||
|
7deb678f91 | ||
|
16160bbb4d | ||
|
1a6aa93768 | ||
|
9207d48b8d | ||
|
492f9929c6 | ||
|
e6f38748b7 | ||
|
3fdf2f28b0 | ||
|
3e1572dcad | ||
|
334607a896 | ||
|
f84de95e9a | ||
|
ad679f48fd | ||
|
8be178317a | ||
|
7d2d5c9e3c | ||
|
48b9fba0c3 | ||
|
5f9680c19b | ||
|
baf38de11a | ||
|
c05423c0a3 | ||
|
265d81a7f0 | ||
|
b6fc524b3f | ||
|
6cdf222def | ||
|
182918a7b9 | ||
|
da77d42f89 | ||
|
17bc552dc5 | ||
|
7af2547027 | ||
|
81b0ea4c3b | ||
|
61f94f39cb | ||
|
7fe6482983 | ||
|
0ba56c7c25 | ||
|
b982446dab | ||
|
e3bf63ddef | ||
|
427df73a40 | ||
|
36a2066542 | ||
|
f19891c39d | ||
|
9cb9b43052 | ||
|
bd099ba04e | ||
|
be7bb30fd4 | ||
|
6893a1aca3 | ||
|
34b7b79bf6 | ||
|
1a6f527fbd | ||
|
0a8c49f533 | ||
|
8f997d2a00 | ||
|
f2720fdb83 | ||
|
36bf16353e | ||
|
44161e70bb | ||
|
2250a6234b | ||
|
3dc061907f | ||
|
63fce073dd | ||
|
4febd0520d | ||
|
b229e6abdf | ||
|
aabc9c56e1 | ||
|
29c5fb261f | ||
|
f4361d2fd5 | ||
|
258cd3ed81 | ||
|
3646cce811 | ||
|
ff59a4fe30 | ||
|
9e8b3fa5c8 | ||
|
48fb1db044 | ||
|
04cfab2f82 | ||
|
bcf8dfb5f3 | ||
|
0f61f4be5b | ||
|
b87165b850 | ||
|
00829ae822 | ||
|
f64f225b2e | ||
|
ff10930668 | ||
|
f868a72c99 | ||
|
7be43afa8b | ||
|
856723e729 | ||
|
8716e97f04 | ||
|
d003b1d971 | ||
|
90750ac3b9 | ||
|
dc349561bf | ||
|
f154af5352 | ||
|
1a12f4a7bf | ||
|
493e30b318 | ||
|
84aaa824c5 |
27
.gitea/workflows/main.yml
Normal file
27
.gitea/workflows/main.yml
Normal file
@ -0,0 +1,27 @@
|
||||
name: Actions Demo
|
||||
run-name: ${{ gitea.actor }} is testing out Actions 🚀
|
||||
on: [push]
|
||||
|
||||
|
||||
jobs:
|
||||
Auto-Build-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event. 1"
|
||||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.TOKEN }}
|
||||
repository: Sunyard/chsm-server # 你的仓库路径
|
||||
endpoint: http://git.fullstack.club/
|
||||
ref: chsm-v1.0 # Gitea实例地址
|
||||
|
||||
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build -t ${{ gitea.repository }}:latest .
|
||||
- run: echo "This job's status is ${{ job.status }} 1 ."
|
27
build.cmd
Normal file
27
build.cmd
Normal file
@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
SETLOCAL
|
||||
|
||||
chcp 65001
|
||||
|
||||
del /S *.log
|
||||
|
||||
call mvn clean -DskipTests=true package
|
||||
|
||||
cd chsm-web-manage
|
||||
echo ">>>>>>>>>>>begin build docker image ...<<<<<<<<<<<<<"
|
||||
docker build -t chsm-web-manager:latest .
|
||||
echo ">>>>>>>>>>>build docker image success<<<<<<<<<<<<<"
|
||||
|
||||
cd ../chsm-web-server
|
||||
echo ">>>>>>>>>>>begin build docker image ...<<<<<<<<<<<<<"
|
||||
docker build -t chsm-web-server:latest .
|
||||
echo ">>>>>>>>>>>build docker image success<<<<<<<<<<<<<"
|
||||
|
||||
cd ..
|
||||
echo ">>>>>>>>>>>begin build offline tar ...<<<<<<<<<<<<<"
|
||||
docker save -o chsm-web-manager.tar chsm-web-manager:latest
|
||||
docker save -o chsm-web-server.tar chsm-web-server:latest
|
||||
echo ">>>>>>>>>>>build docker offline tar success<<<<<<<<<<<<<"
|
||||
|
||||
pause
|
||||
|
20
build.sh
Normal file
20
build.sh
Normal file
@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
call /app/maven/bin/mvn clean -DskipTests=true package
|
||||
|
||||
cd chsm-web-manage
|
||||
echo ">>>>>>>>>>>begin build docker image ...<<<<<<<<<<<<<"
|
||||
docker build -t chsm-web-manager:latest .
|
||||
echo ">>>>>>>>>>>build docker image success<<<<<<<<<<<<<"
|
||||
|
||||
cd ../chsm-web-server
|
||||
echo ">>>>>>>>>>>begin build docker image ...<<<<<<<<<<<<<"
|
||||
docker build -t chsm-web-server:latest .
|
||||
echo ">>>>>>>>>>>build docker image success<<<<<<<<<<<<<"
|
||||
|
||||
cd ..
|
||||
echo ">>>>>>>>>>>begin build offline tar ...<<<<<<<<<<<<<"
|
||||
docker save -o chsm-web-manager.tar chsm-web-manager:latest
|
||||
docker save -o chsm-web-server.tar chsm-web-server:latest
|
||||
echo ">>>>>>>>>>>build docker offline tar success<<<<<<<<<<<<<"
|
||||
|
||||
|
@ -20,6 +20,11 @@
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sunyard.chsm</groupId>
|
||||
<artifactId>chsm-params</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@ -49,13 +54,21 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.briandilley.jsonrpc4j</groupId>
|
||||
<artifactId>jsonrpc4j</artifactId>
|
||||
<version>1.6</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,52 @@
|
||||
package com.sunyard.chsm.constant;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/16
|
||||
*/
|
||||
public interface AuthCodeConst {
|
||||
|
||||
// 密钥管理
|
||||
String key_info = "key_info";
|
||||
String key_list = "key_list";
|
||||
String key_create = "key_create";
|
||||
String key_update = "key_update";
|
||||
String key_enable = "key_enable";
|
||||
String key_disable = "key_disable";
|
||||
String key_archive = "key_archive";
|
||||
String key_destroy = "key_destroy";
|
||||
|
||||
// 对称运算
|
||||
String sym_enc = "sym_enc";
|
||||
String sym_dec = "sym_dec";
|
||||
String cal_hmac = "cal_hmac";
|
||||
String check_hmac = "check_hmac";
|
||||
String cal_mac = "cal_mac";
|
||||
String check_mac = "check_mac";
|
||||
String gen_random = "gen_random";
|
||||
|
||||
// 非对称运算
|
||||
String asym_enc = "asym_enc";
|
||||
String asym_dec = "asym_dec";
|
||||
String sign_raw = "sign_raw";
|
||||
String verify_raw = "verify_raw";
|
||||
String sign_p1 = "sign_p1";
|
||||
String verify_p1 = "verify_p1";
|
||||
String sign_P7Attach = "sign_P7Attach";
|
||||
String verify_P7Attach = "verify_P7Attach";
|
||||
String sign_P7Detach = "sign_P7Detach";
|
||||
String verify_P7Detach = "verify_P7Detach";
|
||||
String envelope_seal = "envelope_enc";
|
||||
String envelope_unseal = "envelope_dec";
|
||||
String signed_envelope_seal = "signed_envelope_enc";
|
||||
String signed_envelope_unseal = "signed_envelope_dec";
|
||||
|
||||
String cal_hash = "cal_hash";
|
||||
|
||||
// 证书
|
||||
String cert_info = "cert_info";
|
||||
String cert_exinfo = "cert_exinfo";
|
||||
String cert_check = "cert_check";
|
||||
String cert_upload = "cert_upload";
|
||||
String cert_remove = "cert_remove";
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.sunyard.chsm.constant;
|
||||
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/19
|
||||
*/
|
||||
public interface CryptoConst {
|
||||
|
||||
byte[] USER_ID = CodecUtils.decodeHex("31323334353637383132333435363738");
|
||||
|
||||
static byte[] iv() {
|
||||
return CodecUtils.decodeHex("30303030303030303030303030303030");
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.sunyard.ssp.common.constant;
|
||||
package com.sunyard.chsm.constant;
|
||||
|
||||
/**
|
||||
* @author:fyc
|
||||
@ -12,6 +12,13 @@ public interface ParamConfKeyConstant {
|
||||
*/
|
||||
String IP_WHITELIST_SWITCH = "ipWhitelistSwitch";
|
||||
|
||||
String TMK_INIT = "tmk_init";
|
||||
String TMK_CHECK_VALUE = "tmk_check_value";
|
||||
|
||||
String ENABLE_SOFT_DEVICE = "enable_soft_device";
|
||||
|
||||
String SOFT_ENC_TMK = "soft_enc_tmk";
|
||||
|
||||
/**
|
||||
* 通讯超时时间
|
||||
*/
|
||||
@ -105,18 +112,20 @@ public interface ParamConfKeyConstant {
|
||||
/**
|
||||
* 系统初始化配置文件路劲
|
||||
*/
|
||||
String SYS_PARAM_CONFIG_FILE_PATH = System.getProperty("user.dir") + "/config/sysParam.config.json";
|
||||
String SYS_PARAM_CONFIG_FILE_PATH = System.getProperty("user.dir") + "/config/sysParam.config.json";
|
||||
|
||||
/**
|
||||
* 调试模式开关枚举
|
||||
*/
|
||||
enum SYS_DEBUG_SWITCH_VALUE{
|
||||
enum SYS_DEBUG_SWITCH_VALUE {
|
||||
DEV("dev"), CONFIG("config"), PRODUCT("product");
|
||||
private String value;
|
||||
private SYS_DEBUG_SWITCH_VALUE(String value){
|
||||
|
||||
private SYS_DEBUG_SWITCH_VALUE(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
public String getValue(){
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.sunyard.ssp.common.constant;
|
||||
package com.sunyard.chsm.constant;
|
||||
|
||||
/**
|
||||
* @author Exrickx
|
||||
@ -20,6 +20,8 @@ public interface SecurityConstant {
|
||||
*/
|
||||
String HEADER = "accessToken";
|
||||
|
||||
String ATTRIBUTE_APP_USER = "ATTRIBUTE_APP_USER";
|
||||
|
||||
/**
|
||||
* 权限参数头
|
||||
*/
|
@ -1,5 +1,6 @@
|
||||
package com.sunyard.chsm.enums;
|
||||
|
||||
import com.sunyard.chsm.constant.AuthCodeConst;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@ -11,24 +12,46 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum ApiFunEnum {
|
||||
|
||||
sym_enc(ApiGroupEnum.SYM_API,"sym_enc", "对称加密"),
|
||||
sym_dec(ApiGroupEnum.ASYM_API,"sym_dec", "对称解密"),
|
||||
cal_hmac(ApiGroupEnum.SYM_API,"cal_hmac", "计算Hmac"),
|
||||
check_hmac(ApiGroupEnum.ASYM_API,"check_hmac", "验证Hmac"),
|
||||
cal_mac(ApiGroupEnum.SYM_API,"cal_mac", "计算mac"),
|
||||
check_mac(ApiGroupEnum.ASYM_API,"check_mac", "验证mac"),
|
||||
gen_random(ApiGroupEnum.SYM_API,"gen_random", "生成随机数"),
|
||||
key_info(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_info, "查询密钥详情"),
|
||||
key_list(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_list, "查询密钥列表"),
|
||||
key_create(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_create, "创建密钥"),
|
||||
key_update(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_update, "更新密钥"),
|
||||
key_enable(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_enable, "启用密钥"),
|
||||
key_disable(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_disable, "停用密钥"),
|
||||
key_archive(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_archive, "归档密钥"),
|
||||
key_destroy(ApiGroupEnum.KEY_MANAGE_API, AuthCodeConst.key_destroy, "销毁密钥"),
|
||||
|
||||
sym_enc(ApiGroupEnum.SYM_API, AuthCodeConst.sym_enc, "对称加密"),
|
||||
sym_dec(ApiGroupEnum.SYM_API, AuthCodeConst.sym_dec, "对称解密"),
|
||||
cal_hmac(ApiGroupEnum.SYM_API, AuthCodeConst.cal_hmac, "计算Hmac"),
|
||||
check_hmac(ApiGroupEnum.SYM_API, AuthCodeConst.check_hmac, "验证Hmac"),
|
||||
cal_mac(ApiGroupEnum.SYM_API, AuthCodeConst.cal_mac, "计算mac"),
|
||||
check_mac(ApiGroupEnum.SYM_API, AuthCodeConst.check_mac, "验证mac"),
|
||||
gen_random(ApiGroupEnum.SYM_API, AuthCodeConst.gen_random, "生成随机数"),
|
||||
|
||||
asym_enc(ApiGroupEnum.ASYM_API, AuthCodeConst.asym_enc, "非对称加密"),
|
||||
asym_dec(ApiGroupEnum.ASYM_API, AuthCodeConst.asym_dec, "非对称解密"),
|
||||
sign_raw(ApiGroupEnum.ASYM_API, AuthCodeConst.sign_raw, "RAW签名"),
|
||||
verify_raw(ApiGroupEnum.ASYM_API, AuthCodeConst.verify_raw, "RAW验签"),
|
||||
sign_p1(ApiGroupEnum.ASYM_API, AuthCodeConst.sign_p1, "P1签名"),
|
||||
verify_p1(ApiGroupEnum.ASYM_API, AuthCodeConst.verify_p1, "P1验签"),
|
||||
sign_P7Attach(ApiGroupEnum.ASYM_API, AuthCodeConst.sign_P7Attach, "P7 Attach签名"),
|
||||
verify_P7Attach(ApiGroupEnum.ASYM_API, AuthCodeConst.verify_P7Attach, "P7 Attach验签"),
|
||||
sign_P7Detach(ApiGroupEnum.ASYM_API, AuthCodeConst.sign_P7Detach, "P7 Detach签名"),
|
||||
verify_P7Detach(ApiGroupEnum.ASYM_API, AuthCodeConst.verify_P7Detach, "P7 Detach验签"),
|
||||
envelope_seal(ApiGroupEnum.ASYM_API, AuthCodeConst.envelope_seal, "P7数字信封加封"),
|
||||
envelope_unseal(ApiGroupEnum.ASYM_API, AuthCodeConst.envelope_unseal, "P7数字信封解封"),
|
||||
signed_envelope_seal(ApiGroupEnum.ASYM_API, AuthCodeConst.signed_envelope_seal, "带签名的数字信封加封"),
|
||||
signed_envelope_unseal(ApiGroupEnum.ASYM_API, AuthCodeConst.signed_envelope_unseal, "带签名的数字信封解封"),
|
||||
|
||||
cal_hash(ApiGroupEnum.HASH_API, AuthCodeConst.cal_hash, "计算Hash"),
|
||||
|
||||
cert_info(ApiGroupEnum.CERT_API, AuthCodeConst.cert_info, "获取证书信息"),
|
||||
cert_exinfo(ApiGroupEnum.CERT_API, AuthCodeConst.cert_exinfo, "获取证书拓展信"),
|
||||
cert_check(ApiGroupEnum.CERT_API, AuthCodeConst.cert_check, "验证证书"),
|
||||
cert_upload(ApiGroupEnum.CERT_API, AuthCodeConst.cert_upload, "上传用户证书"),
|
||||
cert_remove(ApiGroupEnum.CERT_API, AuthCodeConst.cert_remove, "删除用户证书"),
|
||||
|
||||
asym_enc(ApiGroupEnum.ASYM_API,"asym_enc", "非对称加密"),
|
||||
asym_dec(ApiGroupEnum.ASYM_API,"asym_dec", "非对称解密"),
|
||||
sign_raw(ApiGroupEnum.ASYM_API,"sign_raw", "RAW签名"),
|
||||
verify_raw(ApiGroupEnum.ASYM_API,"verify_raw", "RAW验签"),
|
||||
sign_p1(ApiGroupEnum.ASYM_API,"sign_p1", "P1签名"),
|
||||
verify_p1(ApiGroupEnum.ASYM_API,"verify_p1", "P1验签"),
|
||||
sign_P7Attach(ApiGroupEnum.ASYM_API,"sign_P7Attach", "P7 Attach签名"),
|
||||
verify_P7Attach(ApiGroupEnum.ASYM_API,"verify_P7Attach", "P7 Attach验签"),
|
||||
sign_P7Detach(ApiGroupEnum.ASYM_API,"sign_P7Detach", "P7 Detach签名"),
|
||||
verify_P7Detach(ApiGroupEnum.ASYM_API,"verify_P7Detach", "P7 Detach验签"),
|
||||
;
|
||||
|
||||
private final ApiGroupEnum group;
|
||||
|
@ -14,9 +14,12 @@ import java.util.Objects;
|
||||
@AllArgsConstructor
|
||||
public enum ApiGroupEnum {
|
||||
|
||||
KEY_MANAGE_API("key_manage_api", "密钥管理接口"),
|
||||
SYM_API("sym_api", "对称密钥计算接口"),
|
||||
ASYM_API("asym_api", "非对称密钥计算接口"),
|
||||
;
|
||||
HASH_API("hash_api", "杂凑计算接口"),
|
||||
CERT_API("cert_api", "证书接口"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String name;
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.sunyard.chsm.enums;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/7
|
||||
*/
|
||||
public enum DeviceTmkStatus {
|
||||
|
||||
device_error,
|
||||
key_error,
|
||||
available,
|
||||
finished,
|
||||
|
||||
}
|
@ -14,7 +14,7 @@ import java.util.Objects;
|
||||
@AllArgsConstructor
|
||||
public enum ManufacturerModelEnum {
|
||||
|
||||
enc001(ManufacturerEnum.SUNYARD, "enc001", "服务器密码机enc001"),
|
||||
enc001(ManufacturerEnum.SUNYARD, "SYD-001", "服务器密码机"),
|
||||
;
|
||||
|
||||
private final ManufacturerEnum manufacturer;
|
||||
|
@ -2,6 +2,8 @@ package com.sunyard.chsm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunyard.chsm.enums.KeyUsage;
|
||||
import com.sunyard.chsm.model.Subject;
|
||||
import com.sunyard.chsm.model.entity.AppCert;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.Assert;
|
||||
@ -28,5 +30,31 @@ public interface AppCertMapper extends BaseMapper<AppCert> {
|
||||
return certs.iterator().next();
|
||||
}
|
||||
|
||||
default AppCert selectSignBySubject(String dn) {
|
||||
Assert.hasText(dn, "证书序列号不能为空");
|
||||
String subject = Subject.fromDN(dn).getDN();
|
||||
List<AppCert> certs = selectList(new LambdaQueryWrapper<AppCert>()
|
||||
.eq(AppCert::getSubject, subject)
|
||||
.eq(AppCert::getCertType, KeyUsage.SIGN_VERIFY.getCode())
|
||||
);
|
||||
if (CollectionUtils.isEmpty(certs)) {
|
||||
return null;
|
||||
}
|
||||
return certs.iterator().next();
|
||||
}
|
||||
|
||||
default AppCert selectByTypeAndDn(String type,String dn) {
|
||||
Assert.hasText(dn, "证书序列号不能为空");
|
||||
String subject = Subject.fromDN(dn).getDN();
|
||||
List<AppCert> certs = selectList(new LambdaQueryWrapper<AppCert>()
|
||||
.eq(AppCert::getSubject, subject)
|
||||
.eq(AppCert::getCertType, type)
|
||||
);
|
||||
if (CollectionUtils.isEmpty(certs)) {
|
||||
return null;
|
||||
}
|
||||
return certs.iterator().next();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
package com.sunyard.chsm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunyard.chsm.model.entity.Application;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -10,4 +15,15 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
*/
|
||||
@Mapper
|
||||
public interface ApplicationMapper extends BaseMapper<Application> {
|
||||
|
||||
|
||||
default Application selectByAppKey(String appKey) {
|
||||
List<Application> apps = selectList(new QueryWrapper<Application>().eq("app_key", appKey));
|
||||
if (CollectionUtils.isEmpty(apps)) {
|
||||
return null;
|
||||
}
|
||||
Assert.isTrue(apps.size() == 1, "app 数据异常");
|
||||
return apps.iterator().next();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
package com.sunyard.chsm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunyard.chsm.model.entity.CryptoServiceApi;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -10,4 +15,17 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
*/
|
||||
@Mapper
|
||||
public interface CryptoServiceApiMapper extends BaseMapper<CryptoServiceApi> {
|
||||
|
||||
default List<CryptoServiceApi> selectByServiceIds(List<Long> serviceIds) {
|
||||
if (CollectionUtils.isEmpty(serviceIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return selectList(
|
||||
new LambdaQueryWrapper<CryptoServiceApi>()
|
||||
.in(CryptoServiceApi::getCryptoServiceId, serviceIds)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,36 @@
|
||||
package com.sunyard.chsm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunyard.chsm.model.entity.IpWhitelist;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/11/6
|
||||
*/
|
||||
@Mapper
|
||||
public interface IpWhitelisttMapper extends BaseMapper<IpWhitelist> {
|
||||
|
||||
default List<IpWhitelist> selectByAppIds(List<Long> appIds) {
|
||||
if (CollectionUtils.isEmpty(appIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return selectList(
|
||||
new LambdaQueryWrapper<IpWhitelist>()
|
||||
.in(IpWhitelist::getAppId, appIds)
|
||||
);
|
||||
}
|
||||
|
||||
default void deleteByAppId(Long appId) {
|
||||
delete(
|
||||
new LambdaQueryWrapper<IpWhitelist>()
|
||||
.eq(IpWhitelist::getAppId, appId)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.sunyard.ssp.modules.sysconf.paramconf.mapper;
|
||||
package com.sunyard.chsm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunyard.ssp.modules.sysconf.paramconf.entity.ParamConf;
|
||||
import com.sunyard.chsm.model.entity.ParamConf;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.springframework.stereotype.Repository;
|
@ -2,6 +2,8 @@ package com.sunyard.chsm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunyard.chsm.enums.DeviceTmkStatus;
|
||||
import com.sunyard.chsm.model.entity.Device;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@ -14,12 +16,20 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface SpDeviceMapper extends BaseMapper<Device> {
|
||||
|
||||
default List<Device> selectConnedList() {
|
||||
return selectList(
|
||||
new LambdaQueryWrapper<Device>()
|
||||
.eq(Device::getConnected, true)
|
||||
default Device selectOneByStatus(DeviceTmkStatus status) {
|
||||
Page<Device> devicePage = selectPage(
|
||||
Page.of(1L, 1L),
|
||||
new LambdaQueryWrapper<Device>().eq(Device::getTmkStatus, status.name())
|
||||
);
|
||||
}
|
||||
List<Device> records = devicePage.getRecords();
|
||||
if (records.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return records.iterator().next();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ public class R<T> {
|
||||
R<T> r = new R<>();
|
||||
r.setSuccess(false);
|
||||
r.setMessage(msg);
|
||||
r.setCode(500);
|
||||
r.setCode(400);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ public abstract class CertDTO {
|
||||
private Long id;
|
||||
private Long applicationId;
|
||||
private String appName;
|
||||
private Boolean single;
|
||||
/**
|
||||
* 密钥算法
|
||||
*/
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.sunyard.chsm.model.dto;
|
||||
|
||||
import com.sunyard.chsm.enums.DeviceTmkStatus;
|
||||
import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/10
|
||||
*/
|
||||
@Data
|
||||
public class DeviceCheckRes {
|
||||
|
||||
private DeviceTmkStatus status;
|
||||
private String deviceSerial;
|
||||
private String pubKey;
|
||||
private String encTmk;
|
||||
private boolean hasError = false;
|
||||
private String message;
|
||||
|
||||
private SdfApiAdapter sdfApiAdapter;
|
||||
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.sunyard.chsm.dto;
|
||||
package com.sunyard.chsm.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@ -17,6 +17,9 @@ public class TmkStatus {
|
||||
* 主密钥是否初始化
|
||||
*/
|
||||
private boolean tmkInit;
|
||||
|
||||
/**
|
||||
* 主密钥校验值
|
||||
*/
|
||||
private String checkValue;
|
||||
|
||||
}
|
@ -15,8 +15,8 @@ public class CryptoService {
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long deviceGroupId;
|
||||
private String deviceGroupName;
|
||||
// private Long deviceGroupId;
|
||||
// private String deviceGroupName;
|
||||
private String status;
|
||||
private Long creatorId;
|
||||
|
||||
|
@ -21,16 +21,20 @@ public class Device {
|
||||
private Integer managePort;
|
||||
private String manufacturer;
|
||||
private String manufacturerModel;
|
||||
private Integer encKeyIdx;
|
||||
private String accessCredentials;
|
||||
|
||||
private Boolean connected;
|
||||
private LocalDateTime lastCheckTime;
|
||||
private LocalDateTime lastConnectedTime;
|
||||
|
||||
private Long groupId;
|
||||
private String groupName;
|
||||
private Integer weight;
|
||||
|
||||
private String tmkStatus;
|
||||
private String deviceSerial;
|
||||
private String pubKey;
|
||||
private String encTmk;
|
||||
private Boolean connected;
|
||||
private LocalDateTime lastCheckTime;
|
||||
private LocalDateTime lastConnectedTime;
|
||||
|
||||
private String remark;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.sunyard.chsm.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/11/22
|
||||
*/
|
||||
@Data
|
||||
@TableName("sp_ip_whitelist")
|
||||
public class IpWhitelist {
|
||||
|
||||
private Long id;
|
||||
private Long appId;
|
||||
private String ip;
|
||||
private String scope;
|
||||
private String status;
|
||||
|
||||
private String creator;
|
||||
private String remark;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
package com.sunyard.ssp.modules.sysconf.paramconf.entity;
|
||||
package com.sunyard.chsm.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
@ -24,41 +22,32 @@ import java.time.LocalDateTime;
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("SC_PARAM_CONF")
|
||||
@ApiModel(value="", description="")
|
||||
public class ParamConf implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "主键")
|
||||
@TableId("ID")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "数据大类")
|
||||
@TableField("ITEM")
|
||||
private Integer item;
|
||||
|
||||
@ApiModelProperty(value = "数据名称")
|
||||
// @TableField("`KEY`") mysql需加
|
||||
@TableField("KEY")
|
||||
private String key;
|
||||
|
||||
@ApiModelProperty(value = "数据值")
|
||||
@TableField("VALUE")
|
||||
private String value;
|
||||
|
||||
@ApiModelProperty(value = "数据值类型")
|
||||
@TableField("TYPE")
|
||||
private String type;
|
||||
|
||||
@ApiModelProperty(value = "状态")
|
||||
@TableField("STATUS")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
@TableField("CREAT_TIME")
|
||||
@TableField("CREATE_TIME")
|
||||
private LocalDateTime creatTime;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
@TableField("MEMO")
|
||||
private String memo;
|
||||
|
@ -1,8 +1,6 @@
|
||||
package com.sunyard.chsm.sdf;
|
||||
|
||||
import com.sunyard.chsm.enums.AlgMode;
|
||||
import com.sunyard.chsm.enums.KeyAlg;
|
||||
import com.sunyard.chsm.enums.Padding;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -27,10 +25,10 @@ public abstract class AbstractSdfApiService implements SdfApiService {
|
||||
* 密钥长度根据 alg 指定的算法自动选择。
|
||||
* @return 密钥
|
||||
*/
|
||||
@Override
|
||||
public byte[] genSymKey(KeyAlg alg) {
|
||||
return genSymKey( alg, algKeyLen.get( alg ) );
|
||||
}
|
||||
// @Override
|
||||
// public byte[] genSymKey(KeyAlg alg) {
|
||||
// return genSymKey( alg, algKeyLen.get( alg ) );
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
@ -40,10 +38,10 @@ public abstract class AbstractSdfApiService implements SdfApiService {
|
||||
* @param key 密钥值,明文
|
||||
* @param data 原始数据
|
||||
*/
|
||||
@Override
|
||||
public byte[] symEncrypt(KeyAlg alg, byte[] key, byte[] data){
|
||||
return symEncrypt( alg, AlgMode.ECB, Padding.PCKS5Padding, key, data );
|
||||
}
|
||||
// @Override
|
||||
// public byte[] symEncrypt(KeyAlg alg, byte[] key, byte[] data){
|
||||
// return symEncrypt( alg, AlgMode.ECB, Padding.PCKS5Padding, key, data );
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
@ -53,9 +51,9 @@ public abstract class AbstractSdfApiService implements SdfApiService {
|
||||
* @param key 密钥值,明文
|
||||
* @param data 密文数据
|
||||
*/
|
||||
@Override
|
||||
public byte[] symDecrypt(KeyAlg alg, byte[] key, byte[] data) {
|
||||
return symDecrypt( alg, AlgMode.ECB, Padding.PCKS5Padding, key, data );
|
||||
}
|
||||
// @Override
|
||||
// public byte[] symDecrypt(KeyAlg alg, byte[] key, byte[] data) {
|
||||
// return symDecrypt( alg, AlgMode.ECB, Padding.PCKS5Padding, key, data );
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -1,171 +1,186 @@
|
||||
package com.sunyard.chsm.sdf;
|
||||
|
||||
|
||||
import com.sunyard.chsm.enums.AlgMode;
|
||||
import com.sunyard.chsm.enums.KeyAlg;
|
||||
import com.sunyard.chsm.enums.KeyCategory;
|
||||
import com.sunyard.chsm.enums.Padding;
|
||||
import com.sunyard.chsm.sdf.model.EccKey;
|
||||
import com.sunyard.chsm.sdf.util.LangUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM3Utils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM4Utils;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.util.BigIntegers;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import java.security.*;
|
||||
|
||||
|
||||
/**
|
||||
* 基于 BC 库的国密软算法实现
|
||||
* 当前实现类的接口返回的对称密钥和私钥认为是明文,在上层 Service 进行使用和存储时的加密和解密运算。
|
||||
* <p>
|
||||
* 主密钥:
|
||||
* - 生成。 软随机数。项目启动时不存在则自动生成。
|
||||
* - 存储。 存储在 SC_PARAM_CONF 表, KEY 为 mk 的字段,值为固定的 48 字节,以 HEX 格式 96 字节存储。
|
||||
* 前 16 字节为主密钥值,后 32 字节为主密钥值使用 SM3 计算的校验值。
|
||||
* - 同步。基于数据库进行导入、导出、和备份、恢复。多台应用服务器连接同一个数据库,无需同步。
|
||||
* - 使用。顶层密钥,用于保护其他存于数据的密钥。
|
||||
*
|
||||
* @author liulu 、Cheney
|
||||
* @since 2024/10/23
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class BCSdfApiService extends AbstractSdfApiService {
|
||||
|
||||
public BCSdfApiService() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generateRandom(int len) {
|
||||
byte[] res = new byte[len];
|
||||
new SecureRandom().nextBytes(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成对称算法密钥
|
||||
*
|
||||
* @param alg 算法,只支持对称算法
|
||||
* @param keyLen 密钥长度(bit),只针对密钥长度可变的算法。
|
||||
* 禁止传 null
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public byte[] genSymKey(KeyAlg alg, Integer keyLen) {
|
||||
switch (alg) {
|
||||
case SM4: {
|
||||
if (null == keyLen || keyLen != BCSM4Utils.DEFAULT_KEY_SIZE) {
|
||||
throw new IllegalArgumentException("Invalid key length: " + keyLen);
|
||||
}
|
||||
try {
|
||||
return BCSM4Utils.generateKey();
|
||||
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
throw new RuntimeException("算法实现错误", e);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unsupported algorithm: " + alg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] symEncrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) {
|
||||
return symCalc(Cipher.ENCRYPT_MODE, alg, mode, padding, key, data);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] symDecrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) {
|
||||
return symCalc(Cipher.DECRYPT_MODE, alg, mode, padding, key, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对称加解密的统一实现
|
||||
*
|
||||
* @param alg 算法,只支持对称算法
|
||||
* @param key 密钥值,明文
|
||||
* @param data 加密时明文数据,解密时为密文数据
|
||||
*/
|
||||
private byte[] symCalc(int cipherMode, KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) {
|
||||
if (alg.getCategory() != KeyCategory.SYM_KEY) {
|
||||
throw new IllegalArgumentException("Must SYM_KEY, unsupported algorithm: " + alg);
|
||||
}
|
||||
|
||||
// 算法
|
||||
String algName = null;
|
||||
if (alg == KeyAlg.SM4) {
|
||||
algName = "SM4/";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported algorithm: " + alg);
|
||||
}
|
||||
|
||||
// 算法轮模式
|
||||
algName += mode.getCode() + "/";
|
||||
|
||||
// 填充模式
|
||||
algName += padding.getCode();
|
||||
|
||||
|
||||
Cipher cipher = null;
|
||||
try {
|
||||
cipher = BCSM4Utils.generateECBCipher(algName, cipherMode, key);
|
||||
return cipher.doFinal(data);
|
||||
} catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException e) {
|
||||
throw new RuntimeException("算法执行错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public EccKey genKeyPairEcc() {
|
||||
// 生成密钥对
|
||||
KeyPair keyPair = BCSM2Utils.generateKeyPair();
|
||||
BCECPublicKey pubKey = (BCECPublicKey) keyPair.getPublic();
|
||||
BCECPrivateKey priKey = (BCECPrivateKey) keyPair.getPrivate();
|
||||
byte[] x = pubKey.getQ().getXCoord().getEncoded();
|
||||
byte[] y = pubKey.getQ().getYCoord().getEncoded();
|
||||
byte[] d = BigIntegers.asUnsignedByteArray(32, priKey.getD());
|
||||
return new EccKey(LangUtils.merge(x, y), d);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] hmac(byte[] key, byte[] srcData) {
|
||||
return BCSM3Utils.hmac(key, srcData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] hash(byte[] pucData) {
|
||||
return BCSM3Utils.hash(pucData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encryptByTMK(byte[] data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decryptByTMK(byte[] data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
//package com.sunyard.chsm.sdf;
|
||||
//
|
||||
//
|
||||
//import com.sunyard.chsm.enums.AlgMode;
|
||||
//import com.sunyard.chsm.enums.KeyAlg;
|
||||
//import com.sunyard.chsm.enums.KeyCategory;
|
||||
//import com.sunyard.chsm.enums.Padding;
|
||||
//import com.sunyard.chsm.sdf.context.AlgId;
|
||||
//import com.sunyard.chsm.sdf.model.EccKey;
|
||||
//import com.sunyard.chsm.sdf.util.LangUtils;
|
||||
//import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
//import com.sunyard.chsm.utils.gm.BCSM3Utils;
|
||||
//import com.sunyard.chsm.utils.gm.BCSM4Utils;
|
||||
//import lombok.SneakyThrows;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
//import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
//import org.bouncycastle.util.BigIntegers;
|
||||
//import org.springframework.stereotype.Service;
|
||||
//
|
||||
//import javax.crypto.BadPaddingException;
|
||||
//import javax.crypto.Cipher;
|
||||
//import javax.crypto.IllegalBlockSizeException;
|
||||
//import javax.crypto.NoSuchPaddingException;
|
||||
//import java.security.InvalidKeyException;
|
||||
//import java.security.KeyPair;
|
||||
//import java.security.NoSuchAlgorithmException;
|
||||
//import java.security.NoSuchProviderException;
|
||||
//import java.security.SecureRandom;
|
||||
//
|
||||
//
|
||||
///**
|
||||
// * 基于 BC 库的国密软算法实现
|
||||
// * 当前实现类的接口返回的对称密钥和私钥认为是明文,在上层 Service 进行使用和存储时的加密和解密运算。
|
||||
// * <p>
|
||||
// * 主密钥:
|
||||
// * - 生成。 软随机数。项目启动时不存在则自动生成。
|
||||
// * - 存储。 存储在 SC_PARAM_CONF 表, KEY 为 mk 的字段,值为固定的 48 字节,以 HEX 格式 96 字节存储。
|
||||
// * 前 16 字节为主密钥值,后 32 字节为主密钥值使用 SM3 计算的校验值。
|
||||
// * - 同步。基于数据库进行导入、导出、和备份、恢复。多台应用服务器连接同一个数据库,无需同步。
|
||||
// * - 使用。顶层密钥,用于保护其他存于数据的密钥。
|
||||
// *
|
||||
// * @author liulu 、Cheney
|
||||
// * @since 2024/10/23
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Service
|
||||
//public class BCSdfApiService extends AbstractSdfApiService {
|
||||
//
|
||||
// public BCSdfApiService() {
|
||||
// super();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] generateRandom(int len) {
|
||||
// byte[] res = new byte[len];
|
||||
// new SecureRandom().nextBytes(res);
|
||||
// return res;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 生成对称算法密钥
|
||||
// *
|
||||
// * @param alg 算法,只支持对称算法
|
||||
// * @param keyLen 密钥长度(bit),只针对密钥长度可变的算法。
|
||||
// * 禁止传 null
|
||||
// * @return
|
||||
// */
|
||||
// @Override
|
||||
// public byte[] genSymKey(KeyAlg alg, Integer keyLen) {
|
||||
// switch (alg) {
|
||||
// case SM4: {
|
||||
// if (null == keyLen || keyLen != BCSM4Utils.DEFAULT_KEY_SIZE) {
|
||||
// throw new IllegalArgumentException("Invalid key length: " + keyLen);
|
||||
// }
|
||||
// try {
|
||||
// return BCSM4Utils.generateKey();
|
||||
// } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
// throw new RuntimeException("算法实现错误", e);
|
||||
// }
|
||||
// }
|
||||
// default: {
|
||||
// throw new IllegalArgumentException("Unsupported algorithm: " + alg);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] symEncrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) {
|
||||
// return symCalc(Cipher.ENCRYPT_MODE, alg, mode, padding, key, data);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] symEncrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) {
|
||||
// return new byte[0];
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public byte[] symDecrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) {
|
||||
// return symCalc(Cipher.DECRYPT_MODE, alg, mode, padding, key, data);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] symDecrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) {
|
||||
// return new byte[0];
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 对称加解密的统一实现
|
||||
// *
|
||||
// * @param alg 算法,只支持对称算法
|
||||
// * @param key 密钥值,明文
|
||||
// * @param data 加密时明文数据,解密时为密文数据
|
||||
// */
|
||||
// private byte[] symCalc(int cipherMode, KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data) {
|
||||
// if (alg.getCategory() != KeyCategory.SYM_KEY) {
|
||||
// throw new IllegalArgumentException("Must SYM_KEY, unsupported algorithm: " + alg);
|
||||
// }
|
||||
//
|
||||
// // 算法
|
||||
// String algName = null;
|
||||
// if (alg == KeyAlg.SM4) {
|
||||
// algName = "SM4/";
|
||||
// } else {
|
||||
// throw new IllegalArgumentException("Unsupported algorithm: " + alg);
|
||||
// }
|
||||
//
|
||||
// // 算法轮模式
|
||||
// algName += mode.getCode() + "/";
|
||||
//
|
||||
// // 填充模式
|
||||
// algName += padding.getCode();
|
||||
//
|
||||
//
|
||||
// Cipher cipher = null;
|
||||
// try {
|
||||
// cipher = BCSM4Utils.generateECBCipher(algName, cipherMode, key);
|
||||
// return cipher.doFinal(data);
|
||||
// } catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException e) {
|
||||
// throw new RuntimeException("算法执行错误", e);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @SneakyThrows
|
||||
// @Override
|
||||
// public EccKey genKeyPairEcc() {
|
||||
// // 生成密钥对
|
||||
// KeyPair keyPair = BCSM2Utils.generateKeyPair();
|
||||
// BCECPublicKey pubKey = (BCECPublicKey) keyPair.getPublic();
|
||||
// BCECPrivateKey priKey = (BCECPrivateKey) keyPair.getPrivate();
|
||||
// byte[] x = pubKey.getQ().getXCoord().getEncoded();
|
||||
// byte[] y = pubKey.getQ().getYCoord().getEncoded();
|
||||
// byte[] d = BigIntegers.asUnsignedByteArray(32, priKey.getD());
|
||||
// return new EccKey(LangUtils.merge(x, y), d);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) {
|
||||
// return new byte[0];
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] hmac(byte[] key, byte[] srcData) {
|
||||
// return BCSM3Utils.hmac(key, srcData);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] hash(byte[] pucData) {
|
||||
// return BCSM3Utils.hash(pucData);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] encryptByTMK(byte[] data) {
|
||||
// return data;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public byte[] decryptByTMK(byte[] data) {
|
||||
// return data;
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.sunyard.chsm.sdf;
|
||||
|
||||
|
||||
import com.sunyard.chsm.enums.AlgMode;
|
||||
import com.sunyard.chsm.enums.KeyAlg;
|
||||
import com.sunyard.chsm.enums.Padding;
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccKey;
|
||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||
|
||||
|
||||
/**
|
||||
@ -23,41 +24,47 @@ public interface SdfApiService {
|
||||
byte[] generateRandom(int len);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 生成对称密钥
|
||||
* @param alg 算法,只支持对称算法
|
||||
*
|
||||
* @param alg 算法,只支持对称算法
|
||||
* @param keyLen 密钥长度(bit),只针对密钥长度可变的算法。
|
||||
* 禁止传 null
|
||||
* @return 对称密钥
|
||||
*/
|
||||
byte[] genSymKey(KeyAlg alg, Integer keyLen);
|
||||
byte[] genSymKey(KeyAlg alg);
|
||||
// byte[] genSymKey(KeyAlg alg, Integer keyLen);
|
||||
//
|
||||
// byte[] genSymKey(KeyAlg alg);
|
||||
|
||||
|
||||
/**
|
||||
* 对称加密
|
||||
* @param alg 算法,只支持对称算法
|
||||
* @param mode 轮模式
|
||||
* @param padding 填充模式
|
||||
* @param key 密钥值,明文
|
||||
* @param data 原始数据
|
||||
*
|
||||
* @param alg 算法,只支持对称算法
|
||||
* @param padding
|
||||
* @param key 密钥值,明文
|
||||
* @param data 原始数据
|
||||
*/
|
||||
byte[] symEncrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data);
|
||||
byte[] symEncrypt(KeyAlg alg, byte[] key, byte[] data);
|
||||
// byte[] symEncrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data);
|
||||
//
|
||||
// byte[] symEncrypt(KeyAlg alg, byte[] key, 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 data 密文数据
|
||||
* @param key 密钥值,明文
|
||||
* @param 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, AlgMode mode, Padding padding, byte[] key, byte[] data);
|
||||
//
|
||||
// byte[] symDecrypt(KeyAlg alg, byte[] key, byte[] data);
|
||||
|
||||
byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data);
|
||||
|
||||
|
||||
/**
|
||||
@ -67,15 +74,61 @@ public interface SdfApiService {
|
||||
*/
|
||||
EccKey genKeyPairEcc();
|
||||
|
||||
/**
|
||||
* 外部密钥ECC签名
|
||||
*
|
||||
* @param privateKey ECC私钥
|
||||
* @param pucData 缓冲区指针,用于存放外部输入的数据
|
||||
* @param userId 签名者id
|
||||
* @return pucSignature 返回签名值数据
|
||||
*/
|
||||
EccSignature externalSignWithIdECC(byte[] privateKey, byte[] pucData, byte[] userId);
|
||||
|
||||
EccSignature externalSignECC(byte[] privateKey, byte[] pucData);
|
||||
|
||||
/**
|
||||
* 外部密钥ECC验证
|
||||
*
|
||||
* @param publicKey ECC公钥
|
||||
* @param pubData 原文
|
||||
* @param userId 签名者id
|
||||
* @param signData 外部签名数据
|
||||
* @return 0 成功; 非0 失败,返回错误代码
|
||||
*/
|
||||
boolean externalVerifyWithIdECC(byte[] publicKey, byte[] pubData, byte[] signData, byte[] userId);
|
||||
|
||||
boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData);
|
||||
|
||||
|
||||
/**
|
||||
* 外部密钥ECC公钥加密
|
||||
*
|
||||
* @param publicKey 外部ECC公钥结构
|
||||
* @param pucData 缓冲区指针,用于存放外部输入的数据
|
||||
* @return pucEncData 返回数据密文
|
||||
*/
|
||||
EccCipher externalEncryptECC(byte[] publicKey, byte[] pucData);
|
||||
|
||||
/**
|
||||
* 外部密钥ECC私钥解密
|
||||
*
|
||||
* @param privateKey 外部ECC私钥结构
|
||||
* @param encData 缓冲区指针,用于存放输入的数据密文
|
||||
* @return pucData 返回数据明文
|
||||
*/
|
||||
byte[] externalDecryptECC(byte[] privateKey, byte[] encData);
|
||||
|
||||
/**
|
||||
* 计算MAC
|
||||
*
|
||||
* @param algId algId
|
||||
* @param padding padding
|
||||
* @param symKey 用户指定的密钥
|
||||
* @param pucIv 缓冲区指针,用于存放输入和返回的IV数据
|
||||
* @param pucData 缓冲区指针,用于存放输入的数据明文
|
||||
* @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);
|
||||
|
||||
@ -83,8 +136,12 @@ public interface SdfApiService {
|
||||
* 杂凑运算
|
||||
*
|
||||
* @param pucData 缓冲区指针,用于存放输入的数据明文
|
||||
* @param pubKey 执行预处理的公钥
|
||||
* @param userId 执行预处理的userId
|
||||
* @return hash值
|
||||
*/
|
||||
byte[] hash(byte[] pucData, byte[] pubKey, byte[] userId);
|
||||
|
||||
byte[] hash(byte[] pucData);
|
||||
|
||||
byte[] encryptByTMK(byte[] data);
|
||||
|
@ -1,22 +1,36 @@
|
||||
package com.sunyard.chsm.sdf.adapter;
|
||||
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.model.DeviceInfo;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPriKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPubKey;
|
||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||
import com.sunyard.chsm.sdf.util.LangUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM4Utils;
|
||||
import lombok.SneakyThrows;
|
||||
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.bouncycastle.util.BigIntegers;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -24,6 +38,7 @@ import java.security.SecureRandom;
|
||||
*/
|
||||
public class BcSdfApiAdaptor implements SdfApiAdapter {
|
||||
|
||||
protected static final Map<String, byte[]> KEY_HANDLE_CONTEXT = new ConcurrentHashMap<>();
|
||||
|
||||
private static final DeviceInfo deviceInfo;
|
||||
|
||||
@ -39,6 +54,8 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
|
||||
deviceInfo.setHashAlgAbility(7010608454676760881L);
|
||||
}
|
||||
|
||||
protected static final BcSdfApiAdaptor INSTANCE = new BcSdfApiAdaptor();
|
||||
|
||||
@Override
|
||||
public String openDevice() {
|
||||
return "c95a78d9c04a557b7b46dbcb5f36cc66";
|
||||
@ -51,7 +68,7 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
|
||||
|
||||
@Override
|
||||
public String openSession(String deviceHandle) {
|
||||
return "6975feaffaa35b31b6d4e4555ac403a1";
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -72,10 +89,10 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) {
|
||||
public EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) {
|
||||
BigInteger d = new BigInteger(1, getD());
|
||||
ECPoint q = BCSM2Utils.G_POINT.multiply(d).normalize();
|
||||
return LangUtils.merge(q.getXCoord().getEncoded(), q.getYCoord().getEncoded());
|
||||
return new EccPubKey(256, q.getXCoord().getEncoded(), q.getYCoord().getEncoded());
|
||||
}
|
||||
|
||||
private byte[] getD() {
|
||||
@ -87,7 +104,7 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits) {
|
||||
public EccKey generateKeyPairECC(String sessionHandle, AlgId alg) {
|
||||
// 生成密钥对
|
||||
KeyPair keyPair = BCSM2Utils.generateKeyPair();
|
||||
BCECPublicKey pubKey = (BCECPublicKey) keyPair.getPublic();
|
||||
@ -99,26 +116,203 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, byte[] pubKey, byte[] pucEncDateIn) {
|
||||
return new byte[0];
|
||||
public boolean getPrivateKeyAccessRight(String hSessionHandle, int uiKeyIndex, byte[] pucPassword) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] externalEncryptECC(String sessionHandle, byte[] pubKey, byte[] pucData) {
|
||||
if (pubKey[0] == 4) {
|
||||
pubKey = Arrays.copyOfRange(pubKey, 1, 65);
|
||||
}
|
||||
ECPublicKeyParameters parameters = BCECUtils.createECPublicKeyParameters(
|
||||
Arrays.copyOfRange(pubKey, 0, 32),
|
||||
Arrays.copyOfRange(pubKey, 32, 64)
|
||||
);
|
||||
public EccCipher exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, EccPubKey pubKey, EccCipher pucEncDateIn) {
|
||||
|
||||
ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(pubKey.getX(), pubKey.getY());
|
||||
ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(getD());
|
||||
try {
|
||||
byte[] encrypt = BCSM2Utils.encrypt(parameters, pucData);
|
||||
return Arrays.copyOfRange(encrypt, 1, encrypt.length);
|
||||
byte[] k = BCSM2Utils.decrypt(pri, LangUtils.merge(new byte[]{0x04}, pucEncDateIn.getC1C3C2Bytes()));
|
||||
byte[] encrypt = BCSM2Utils.encrypt(pub, k);
|
||||
return EccCipher.fromBytes(encrypt);
|
||||
} catch (InvalidCipherTextException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
@Override
|
||||
public EccSignature externalSignECC(String sessionHandle, EccPriKey privateKey, byte[] pucData) {
|
||||
ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(privateKey.getD());
|
||||
|
||||
BigInteger n = pri.getParameters().getN();
|
||||
BigInteger d = pri.getD();
|
||||
|
||||
BigInteger e = new BigInteger(1, pucData);
|
||||
BigInteger r, s = null;
|
||||
|
||||
do {
|
||||
BigInteger k;
|
||||
do {
|
||||
k = new BigInteger(n.bitLength(), RANDOM);
|
||||
} while (k.compareTo(BigInteger.ONE) < 0 || k.compareTo(n) >= 0);
|
||||
|
||||
ECPoint kG = pri.getParameters().getG().multiply(k).normalize();
|
||||
BigInteger x1 = kG.getAffineXCoord().toBigInteger();
|
||||
|
||||
r = e.add(x1).mod(n);
|
||||
|
||||
if (r.equals(BigInteger.ZERO) || r.add(k).equals(n)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BigInteger dPlus1Inv = d.add(BigInteger.ONE).modInverse(n);
|
||||
s = dPlus1Inv.multiply(k.subtract(r.multiply(d))).mod(n);
|
||||
|
||||
} while (Objects.equals(s, BigInteger.ZERO));
|
||||
return new EccSignature(BigIntegers.asUnsignedByteArray(32, r),
|
||||
BigIntegers.asUnsignedByteArray(32, s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean externalVerifyECC(String sessionHandle, EccPubKey publicKey, byte[] pucData, EccSignature pucSignature) {
|
||||
ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(publicKey.getX(), publicKey.getY());
|
||||
if (pucData.length != 32) {
|
||||
throw new IllegalArgumentException("Hash length must be 32 bytes");
|
||||
}
|
||||
ECDomainParameters domainParams = pub.getParameters();
|
||||
ECPoint G = domainParams.getG();
|
||||
BigInteger n = domainParams.getN();
|
||||
ECPoint Q = pub.getQ();
|
||||
BigInteger e = new BigInteger(1, pucData);
|
||||
|
||||
BigInteger r = new BigInteger(1, pucSignature.getR());
|
||||
BigInteger s = new BigInteger(1, pucSignature.getS());
|
||||
if (r.compareTo(BigIntegers.ONE) < 0 || r.compareTo(n) >= 0 || s.compareTo(BigIntegers.ONE) < 0 || s.compareTo(n) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger t = r.add(s).mod(n);
|
||||
if (t.equals(BigInteger.ZERO)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ECPoint point = G.multiply(s).add(Q.multiply(t)).normalize();
|
||||
|
||||
BigInteger R = e.add(point.getAffineXCoord().toBigInteger()).mod(n);
|
||||
|
||||
return R.equals(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EccCipher externalEncryptECC(String sessionHandle, EccPubKey pubKey, byte[] pucData) {
|
||||
ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(pubKey.getX(), pubKey.getY());
|
||||
try {
|
||||
byte[] encrypt = BCSM2Utils.encrypt(pub, pucData);
|
||||
return EccCipher.fromBytes(encrypt);
|
||||
} catch (InvalidCipherTextException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] externalDecryptECC(String sessionHandle, EccPriKey pucPrivateKeyEcc, EccCipher pucEncData) {
|
||||
ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(pucPrivateKeyEcc.getD());
|
||||
try {
|
||||
return BCSM2Utils.decrypt(pri, LangUtils.merge(new byte[]{0x04}, pucEncData.getC1C3C2Bytes()));
|
||||
} catch (InvalidCipherTextException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String importKeyWithISKECC(String sessionHandle, int uiIskIndex, EccCipher eccCipher) {
|
||||
ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(getD());
|
||||
|
||||
try {
|
||||
byte[] pucKey = BCSM2Utils.decrypt(pri, LangUtils.merge(new byte[]{0x04}, eccCipher.getC1C3C2Bytes()));
|
||||
Assert.isTrue(pucKey.length == 16, "密钥长度错误");
|
||||
String key = UUID.randomUUID().toString();
|
||||
KEY_HANDLE_CONTEXT.put(key, pucKey);
|
||||
return key;
|
||||
} catch (InvalidCipherTextException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String importKey(String sessionHandle, byte[] pucKey) {
|
||||
Assert.isTrue(pucKey.length == 16, "密钥长度错误");
|
||||
String key = UUID.randomUUID().toString();
|
||||
KEY_HANDLE_CONTEXT.put(key, pucKey);
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int destroyKey(String sessionHandle, String hKeyHandle) {
|
||||
KEY_HANDLE_CONTEXT.remove(hKeyHandle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public byte[] symEncrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucData) {
|
||||
byte[] symKey = getSymKey(keyHandle);
|
||||
switch (alg) {
|
||||
case SGD_SM4_ECB:
|
||||
return BCSM4Utils.encrypt_ECB_NoPadding(symKey, pucData);
|
||||
case SGD_SM4_CBC:
|
||||
return BCSM4Utils.encrypt_CBC_NoPadding(symKey, pucIv, pucData);
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的算法:" + alg.name());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public byte[] symDecrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucEncData) {
|
||||
byte[] symKey = getSymKey(keyHandle);
|
||||
switch (alg) {
|
||||
case SGD_SM4_ECB:
|
||||
return BCSM4Utils.decrypt_ECB_NoPadding(symKey, pucEncData);
|
||||
case SGD_SM4_CBC:
|
||||
return BCSM4Utils.decrypt_CBC_NoPadding(symKey, pucIv, pucEncData);
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的算法:" + alg.name());
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] getSymKey(String keyHandle) {
|
||||
byte[] key = KEY_HANDLE_CONTEXT.get(keyHandle);
|
||||
Assert.notNull(key, "密钥句柄不存在");
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public byte[] calculateMAC(String sessionHandle, String keyHandle, AlgId uiAlg, byte[] pucIv, byte[] pucData) {
|
||||
byte[] symKey = getSymKey(keyHandle);
|
||||
return BCSM4Utils.doCBCMac(symKey, pucIv, null, pucData);
|
||||
}
|
||||
|
||||
protected static final Map<String, SM3Digest> HASH_CONTEXT = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public int hashInit(String sessionHandle, AlgId alg, EccPubKey pucPublicKey, byte[] pucID) {
|
||||
HASH_CONTEXT.put(sessionHandle, new SM3Digest());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashUpdate(String sessionHandle, byte[] pucData) {
|
||||
Optional.ofNullable(HASH_CONTEXT.get(sessionHandle))
|
||||
.ifPresent(it -> it.update(pucData, 0, pucData.length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] hashFinish(String sessionHandle) {
|
||||
SM3Digest digest = HASH_CONTEXT.remove(sessionHandle);
|
||||
Assert.notNull(digest, "session异常");
|
||||
byte[] hash = new byte[digest.getDigestSize()];
|
||||
digest.doFinal(hash, 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,19 +1,25 @@
|
||||
package com.sunyard.chsm.sdf.adapter;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.context.SdrCode;
|
||||
import com.sunyard.chsm.sdf.lib.SdfLibrary;
|
||||
import com.sunyard.chsm.sdf.model.DeviceInfo;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPriKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPubKey;
|
||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||
import com.sunyard.chsm.sdf.model.SDF_DeviceInfo;
|
||||
import com.sunyard.chsm.sdf.util.LangUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -31,7 +37,7 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter {
|
||||
|
||||
protected final SdfLibrary sdfLibrary;
|
||||
|
||||
protected abstract int getAlgId(String alg);
|
||||
protected abstract int getAlgId(AlgId alg);
|
||||
|
||||
@Override
|
||||
public String openDevice() {
|
||||
@ -58,6 +64,7 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter {
|
||||
|
||||
PointerByReference phSessionHandle = new PointerByReference();
|
||||
sdfLibrary.SDF_OpenSession(device, phSessionHandle);
|
||||
Assert.notNull(phSessionHandle.getValue(), "SDF_OpenSession failed");
|
||||
|
||||
String key = UUID.randomUUID().toString();
|
||||
SESSION_HANDLE_CONTEXT.put(key, phSessionHandle.getValue());
|
||||
@ -97,9 +104,9 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter {
|
||||
sdfLibrary.SDF_GetDeviceInfo(getSessionHandle(sessionHandle), sdfInfo);
|
||||
|
||||
DeviceInfo deviceInfo = new DeviceInfo();
|
||||
deviceInfo.setIssuerName(new String(sdfInfo.IssuerName));
|
||||
deviceInfo.setDeviceName(new String(sdfInfo.DeviceName));
|
||||
deviceInfo.setDeviceSerial(new String(sdfInfo.DeviceSerial));
|
||||
deviceInfo.setIssuerName(new String(LangUtils.removeLastZero(sdfInfo.IssuerName)));
|
||||
deviceInfo.setDeviceName(new String(LangUtils.removeLastZero(sdfInfo.DeviceName)));
|
||||
deviceInfo.setDeviceSerial(new String(LangUtils.removeLastZero(sdfInfo.DeviceSerial)));
|
||||
deviceInfo.setDeviceVersion(sdfInfo.DeviceVersion);
|
||||
deviceInfo.setStandardVersion(sdfInfo.StandardVersion);
|
||||
deviceInfo.setAsymAlgAbility(new long[]{sdfInfo.AsymAlgAbility[0], sdfInfo.AsymAlgAbility[1]});
|
||||
@ -118,28 +125,166 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) {
|
||||
public EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) {
|
||||
byte[] pubKey = new byte[132];
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
sdfLibrary.SDF_ExportEncPublicKey_ECC(hSessionHandle, uiKeyIndex, pubKey);
|
||||
return LangUtils.merge(Arrays.copyOfRange(pubKey, 36, 68), Arrays.copyOfRange(pubKey, 100, 132));
|
||||
return EccPubKey.fromBytes(pubKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits) {
|
||||
return null;
|
||||
public EccKey generateKeyPairECC(String sessionHandle, AlgId alg) {
|
||||
byte[] sdfPubKey = new byte[132];
|
||||
byte[] sdfPriKey = new byte[68];
|
||||
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
sdfLibrary.SDF_GenerateKeyPair_ECC(hSessionHandle, getAlgId(alg), 256, sdfPubKey, sdfPriKey);
|
||||
return new EccKey(EccPubKey.fromBytes(sdfPubKey).getPubKeyBytes(), EccPriKey.fromBytes(sdfPriKey).getD());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, byte[] pubKey, byte[] pucEncDateIn) {
|
||||
return new byte[0];
|
||||
public boolean getPrivateKeyAccessRight(String sessionHandle, int uiKeyIndex, byte[] pucPassword) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
int res = sdfLibrary.SDF_GetPrivateKeyAccessRight(hSessionHandle, uiKeyIndex, pucPassword, pucPassword.length);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] externalEncryptECC(String sessionHandle, byte[] pubKey, byte[] pucData) {
|
||||
public String importKeyWithISKECC(String sessionHandle, int uiIskIndex, EccCipher eccCipher) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
PointerByReference phKeyHandle = new PointerByReference();
|
||||
|
||||
sdfLibrary.SDF_ImportKeyWithISK_ECC(hSessionHandle, uiIskIndex, eccCipher.toSdfData(), phKeyHandle);
|
||||
String key = UUID.randomUUID().toString();
|
||||
KEY_HANDLE_CONTEXT.put(key, phKeyHandle.getValue());
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String importKey(String sessionHandle, byte[] pucKey) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
PointerByReference phKeyHandle = new PointerByReference();
|
||||
sdfLibrary.SDF_ImportKey(hSessionHandle, pucKey, pucKey.length, phKeyHandle);
|
||||
String key = UUID.randomUUID().toString();
|
||||
KEY_HANDLE_CONTEXT.put(key, phKeyHandle.getValue());
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int destroyKey(String sessionHandle, String hKeyHandle) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
Pointer keyHandle = KEY_HANDLE_CONTEXT.remove(hKeyHandle);
|
||||
return sdfLibrary.SDF_DestroyKey(hSessionHandle, keyHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EccCipher exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, EccPubKey pubKey, EccCipher pucEncDateIn) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
|
||||
byte[] pucEncDateOut = new byte[164 + pucEncDateIn.getL()];
|
||||
sdfLibrary.SDF_ExchangeDigitEnvelopeBaseOnECC(hSessionHandle, uiKeyIndex, getAlgId(AlgId.SGD_SM2_1),
|
||||
pubKey.toSdfData(), pucEncDateIn.toSdfData(), pucEncDateOut);
|
||||
return EccCipher.fromBytes(pucEncDateOut);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EccSignature externalSignECC(String sessionHandle, EccPriKey privateKey, byte[] pucData) {
|
||||
byte[] eccSignature = new byte[128];
|
||||
int uiDataLength = pucData.length;
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
sdfLibrary.SDF_ExternalSign_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_1), privateKey.toSdfData(),
|
||||
pucData, uiDataLength, eccSignature);
|
||||
return EccSignature.fromBytes(eccSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean externalVerifyECC(String sessionHandle, EccPubKey publicKey, byte[] pucData, EccSignature pucSignature) {
|
||||
int uiDataLength = pucData.length;
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
int result = sdfLibrary.SDF_ExternalVerify_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_1), publicKey.toSdfData(),
|
||||
pucData, uiDataLength, pucSignature.toSdfData());
|
||||
return Objects.equals(result, SdrCode.SDR_OK.getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EccCipher externalEncryptECC(String sessionHandle, EccPubKey pubKey, byte[] pucData) {
|
||||
byte[] encData = new byte[128 + 32 + 4 + pucData.length];
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
sdfLibrary.SDF_ExternalEncrypt_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_3), pubKey, pucData, pucData.length, encData);
|
||||
return encData;
|
||||
sdfLibrary.SDF_ExternalEncrypt_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_3), pubKey.toSdfData(), pucData, pucData.length, encData);
|
||||
return EccCipher.fromBytes(encData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] externalDecryptECC(String sessionHandle, EccPriKey priKey, EccCipher pucEncData) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
byte[] pucData = new byte[pucEncData.getL()];
|
||||
IntByReference puiLength = new IntByReference();
|
||||
sdfLibrary.SDF_ExternalDecrypt_ECC(hSessionHandle, getAlgId(AlgId.SGD_SM2_3), priKey.toSdfData(), pucEncData.toSdfData(), pucData, puiLength);
|
||||
return pucData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] symEncrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucData) {
|
||||
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
Pointer hKeyHandle = getKeyHandle(keyHandle);
|
||||
int uiDataLength = pucData.length;
|
||||
byte[] pucEncData = new byte[uiDataLength];
|
||||
IntByReference puiLength = new IntByReference();
|
||||
//加密
|
||||
sdfLibrary.SDF_Encrypt(hSessionHandle, hKeyHandle, getAlgId(alg),
|
||||
pucIv, pucData, uiDataLength, pucEncData, puiLength);
|
||||
|
||||
return pucEncData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] symDecrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucEncData) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
Pointer hKeyHandle = getKeyHandle(keyHandle);
|
||||
int uiEncDataLength = pucEncData.length;
|
||||
byte[] pucData = new byte[uiEncDataLength];
|
||||
//解密
|
||||
IntByReference puiLength = new IntByReference();
|
||||
sdfLibrary.SDF_Decrypt(hSessionHandle, hKeyHandle, getAlgId(alg),
|
||||
pucIv, pucEncData, uiEncDataLength, pucData, puiLength);
|
||||
return pucData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] calculateMAC(String sessionHandle, String keyHandle, AlgId uiAlg, byte[] pucIv, byte[] pucData) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
Pointer hKeyHandle = getKeyHandle(keyHandle);
|
||||
|
||||
int uiMacLength = pucData.length;
|
||||
byte[] pucMac = new byte[16];
|
||||
|
||||
IntByReference puiLength = new IntByReference();
|
||||
sdfLibrary.SDF_CalculateMAC(hSessionHandle, hKeyHandle, getAlgId(uiAlg), pucIv, pucData, uiMacLength, pucMac, puiLength);
|
||||
|
||||
return pucMac;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashInit(String sessionHandle, AlgId alg, EccPubKey pucPublicKey, byte[] pucID) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
byte[] pubPub = pucPublicKey == null ? null : pucPublicKey.toSdfData();
|
||||
return sdfLibrary.SDF_HashInit(hSessionHandle, getAlgId(AlgId.SGD_SM3), pubPub, pucID, pucID == null ? 0 : pucID.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashUpdate(String sessionHandle, byte[] pucData) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
return sdfLibrary.SDF_HashUpdate(hSessionHandle, pucData, pucData.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] hashFinish(String sessionHandle) {
|
||||
Pointer hSessionHandle = getSessionHandle(sessionHandle);
|
||||
byte[] pucHash = new byte[32];
|
||||
IntByReference puiLength = new IntByReference();
|
||||
sdfLibrary.SDF_HashFinal(hSessionHandle, pucHash, puiLength);
|
||||
return pucHash;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package com.sunyard.chsm.sdf.adapter;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/12
|
||||
*/
|
||||
public interface RpcSdfAdapter extends SdfApiAdapter {
|
||||
|
||||
String openDevice(String ip, int port, int connTimeout, int dealTimeout, int ipMode);
|
||||
|
||||
}
|
@ -1,7 +1,12 @@
|
||||
package com.sunyard.chsm.sdf.adapter;
|
||||
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.model.DeviceInfo;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPriKey;
|
||||
import com.sunyard.chsm.sdf.model.EccPubKey;
|
||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -48,29 +53,150 @@ public interface SdfApiAdapter {
|
||||
* @return pucRandom 返回随机数
|
||||
*/
|
||||
byte[] generateRandom(String sessionHandle, int uiLength);
|
||||
|
||||
|
||||
/**
|
||||
* 导出ECC加密公钥
|
||||
*
|
||||
* @param uiKeyIndex 密码设备存储的ECC密钥对索引值
|
||||
* @return pucPublicKeyEcc 返回ECC加密公钥x+y
|
||||
*/
|
||||
byte[] exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex);
|
||||
EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex);
|
||||
|
||||
/**
|
||||
* 产生ECC密钥对并输出
|
||||
* 产生ECC密钥对并输出 密钥模长 256
|
||||
*
|
||||
* @param alg 指定算法标识 SGD_SM2_1
|
||||
* @param uiKeyBits 指定密钥模长
|
||||
* @return pucPublicKeyEcc 返回公钥 | pucPrivateKeyEcc 返回私钥
|
||||
*/
|
||||
EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits);
|
||||
EccKey generateKeyPairECC(String sessionHandle, AlgId alg);
|
||||
|
||||
boolean getPrivateKeyAccessRight(String hSessionHandle, int uiKeyIndex, byte[] pucPassword);
|
||||
|
||||
byte[] exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, byte[] pubKey, byte[] pucEncDateIn);
|
||||
/**
|
||||
* 导入会话密钥并用内部ECC私钥解密
|
||||
*
|
||||
* @param uiIskIndex 密码设备内部存储加密私钥的索引值,对应于加密时的公钥
|
||||
* @param eccCipher 缓冲区指针,用于存放返回的密钥密文
|
||||
* @return phKeyHandle 返回密钥句柄
|
||||
*/
|
||||
String importKeyWithISKECC(String sessionHandle, int uiIskIndex, EccCipher eccCipher);
|
||||
|
||||
/**
|
||||
* 导入明文会话密钥
|
||||
*
|
||||
* @param sessionHandle 与设备建立的会话句柄
|
||||
* @param pucKey 缓冲区指针,用于存放输入的密钥密文
|
||||
* @return phKeyHandle 返回密钥句柄
|
||||
*/
|
||||
String importKey(String sessionHandle, byte[] pucKey);
|
||||
|
||||
byte[] externalEncryptECC(String sessionHandle, byte[] pubKey, byte[] pucData);
|
||||
/**
|
||||
* 销毁会话密钥
|
||||
*
|
||||
* @param hKeyHandle 密钥句柄
|
||||
* @return 0 成功; 非0 失败,返回错误代码
|
||||
*/
|
||||
int destroyKey(String sessionHandle, String hKeyHandle);
|
||||
|
||||
EccCipher exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, EccPubKey pubKey, EccCipher pucEncDateIn);
|
||||
|
||||
/**
|
||||
* 外部密钥ECC签名
|
||||
*
|
||||
* @param privateKey ECC私钥
|
||||
* @param pucData 缓冲区指针,用于存放外部输入的数据
|
||||
* @return pucSignature 返回签名值数据
|
||||
*/
|
||||
EccSignature externalSignECC(String sessionHandle, EccPriKey privateKey, byte[] pucData);
|
||||
|
||||
/**
|
||||
* 外部密钥ECC验证
|
||||
*
|
||||
* @param publicKey ECC公钥
|
||||
* @param pucData 缓冲区指针,用于存放外部输入的数据
|
||||
* @param pucSignature 缓冲区指针,用于存放输入的签名值数据
|
||||
* @return 0 成功; 非0 失败,返回错误代码
|
||||
*/
|
||||
boolean externalVerifyECC(String sessionHandle, EccPubKey publicKey, byte[] pucData, EccSignature pucSignature);
|
||||
|
||||
/**
|
||||
* 外部密钥ECC公钥加密
|
||||
*
|
||||
* @param pubKey 外部ECC公钥结构
|
||||
* @param pucData 缓冲区指针,用于存放外部输入的数据
|
||||
* @return pucEncData 返回数据密文
|
||||
*/
|
||||
EccCipher externalEncryptECC(String sessionHandle, EccPubKey pubKey, byte[] pucData);
|
||||
|
||||
/**
|
||||
* 外部密钥ECC私钥解密
|
||||
*
|
||||
* @param pucPrivateKeyEcc 外部ECC私钥结构
|
||||
* @param pucEncData 缓冲区指针,用于存放输入的数据密文
|
||||
* @return pucData 返回数据明文
|
||||
*/
|
||||
byte[] externalDecryptECC(String sessionHandle, EccPriKey pucPrivateKeyEcc, EccCipher pucEncData);
|
||||
|
||||
/**
|
||||
* 对称加密
|
||||
*
|
||||
* @param sessionHandle 与设备建立的会话句柄
|
||||
* @param keyHandle 指定的密钥句柄
|
||||
* @param alg 算法标识,指定对称加密算法 SGD_SMS4_ECB
|
||||
* @param pucIv IV数据
|
||||
* @param pucData 数据明文
|
||||
* @return 数据密文
|
||||
*/
|
||||
byte[] symEncrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucData);
|
||||
|
||||
/**
|
||||
* 对称解密
|
||||
*
|
||||
* @param sessionHandle 与设备建立的会话句柄
|
||||
* @param keyHandle 指定的密钥句柄
|
||||
* @param alg 算法标识,指定对称加密算法 SGD_SMS4_ECB
|
||||
* @param pucIv IV数据
|
||||
* @param pucEncData 数据密文
|
||||
* @return 数据明文
|
||||
*/
|
||||
byte[] symDecrypt(String sessionHandle, String keyHandle, AlgId alg, byte[] pucIv, byte[] pucEncData);
|
||||
|
||||
/**
|
||||
* @param sessionHandle 与设备建立的会话句柄
|
||||
* @param keyHandle 指定的密钥句柄
|
||||
* @param uiAlg 算法标识,指定MAC算法 SGD_SMS4_MAC
|
||||
* @param pucIv IV数据
|
||||
* @param pucData 数据明文
|
||||
* @return MAC值
|
||||
*/
|
||||
byte[] calculateMAC(String sessionHandle, String keyHandle, AlgId uiAlg, byte[] pucIv, byte[] pucData);
|
||||
|
||||
/**
|
||||
* 杂凑运算初始化
|
||||
*
|
||||
* @param sessionHandle 与设备建立的会话句柄
|
||||
* @param alg 当前杂凑算法标识 SGD_SM3
|
||||
* @param pucPublicKey 签名者公钥,当uiAlgID为SGD_SM3时有效
|
||||
* @param pucID 签名者的ID值,当uiAlgID为SGD_SM3时有效
|
||||
* @return 0 成功; 非0 失败,返回错误代码
|
||||
*/
|
||||
int hashInit(String sessionHandle, AlgId alg, EccPubKey pucPublicKey, byte[] pucID);
|
||||
|
||||
/**
|
||||
* 多包杂凑运算
|
||||
*
|
||||
* @param sessionHandle sessionHandle
|
||||
* @param pucData 数据明文
|
||||
* @return 0 成功; 非0 失败,返回错误代码
|
||||
*/
|
||||
int hashUpdate(String sessionHandle, byte[] pucData);
|
||||
|
||||
/**
|
||||
* 杂凑运算结束
|
||||
*
|
||||
* @param sessionHandle 与设备建立的会话句柄
|
||||
* @return 杂凑数据
|
||||
*/
|
||||
byte[] hashFinish(String sessionHandle);
|
||||
|
||||
}
|
||||
|
@ -1,35 +1,79 @@
|
||||
package com.sunyard.chsm.sdf.adapter;
|
||||
|
||||
import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
|
||||
import com.googlecode.jsonrpc4j.ProxyUtil;
|
||||
import com.sun.jna.Platform;
|
||||
import com.sunyard.chsm.enums.ManufacturerModelEnum;
|
||||
import com.sunyard.chsm.sdf.context.DeviceContext;
|
||||
import com.sunyard.chsm.utils.JsonUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/11/5
|
||||
*/
|
||||
@Slf4j
|
||||
public class SdfApiAdapterFactory {
|
||||
|
||||
|
||||
public static SdfApiAdapter newInstance(DeviceContext device) {
|
||||
public static SdfApiAdapter getBcAdapter() {
|
||||
return BcSdfApiAdaptor.INSTANCE;
|
||||
}
|
||||
|
||||
Assert.hasText(device.getManufacturerModel(), "设备型号不能为空");
|
||||
ManufacturerModelEnum model = ManufacturerModelEnum.of(device.getManufacturerModel());
|
||||
public static SdfApiAdapter newInstance(String model, String ip, Integer port) {
|
||||
|
||||
if (Objects.isNull(model) && Objects.equals(BouncyCastleProvider.PROVIDER_NAME, device.getManufacturerModel())) {
|
||||
Assert.hasText(model, "设备型号不能为空");
|
||||
ManufacturerModelEnum modelEnum = ManufacturerModelEnum.of(model);
|
||||
|
||||
if (Objects.isNull(modelEnum) && Objects.equals(BouncyCastleProvider.PROVIDER_NAME, model)) {
|
||||
// bc adaptor
|
||||
return new BcSdfApiAdaptor();
|
||||
return BcSdfApiAdaptor.INSTANCE;
|
||||
}
|
||||
switch (model) {
|
||||
case enc001:
|
||||
return new SunyardJnaSdfAdaptor(device.getServiceIp(), device.getServicePort());
|
||||
default:
|
||||
throw new UnsupportedOperationException("不支持的设备型号: " + device.getManufacturerModel());
|
||||
try {
|
||||
switch (modelEnum) {
|
||||
case enc001:
|
||||
return Platform.isMac() ? getProxyRcpAdapter(ip, port) : new SunyardJnaSdfAdaptor(ip, port);
|
||||
default:
|
||||
throw new UnsupportedOperationException("不支持的设备型号: " + model);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
log.warn("build SdfApiAdapter error", ex);
|
||||
throw new IllegalArgumentException("build SdfApiAdapter error");
|
||||
}
|
||||
}
|
||||
|
||||
private static final RpcSdfAdapter rpcSdfAdapter;
|
||||
|
||||
static {
|
||||
try {
|
||||
JsonRpcHttpClient client = new JsonRpcHttpClient(JsonUtils.objectMapper(),
|
||||
new URL("http://172.16.18.46:9989/sdf/adapter"), Collections.emptyMap());
|
||||
rpcSdfAdapter = ProxyUtil.createClientProxy(
|
||||
ClassUtils.getDefaultClassLoader(), RpcSdfAdapter.class, client);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static SdfApiAdapter getProxyRcpAdapter(String ip, Integer port) {
|
||||
return (SdfApiAdapter) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(),
|
||||
new Class[]{SdfApiAdapter.class},
|
||||
(proxy, method, args) -> {
|
||||
if (Objects.equals(method.getName(), "openDevice")) {
|
||||
return rpcSdfAdapter.openDevice(ip, port, 3000, 10000, 0);
|
||||
}
|
||||
return method.invoke(rpcSdfAdapter, args);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -2,10 +2,19 @@ package com.sunyard.chsm.sdf.adapter;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.context.SdrCode;
|
||||
import com.sunyard.chsm.sdf.context.SunyardAlgId;
|
||||
import com.sunyard.chsm.sdf.lib.SdfLibrary;
|
||||
import com.sunyard.chsm.sdf.lib.SunyardSdfLibrary;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -13,26 +22,52 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* @author liulu
|
||||
* @since 2024/11/4
|
||||
*/
|
||||
@Slf4j
|
||||
public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor {
|
||||
|
||||
private static final Map<String, SunyardSdfLibrary> SDF_LIB_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
private final String ip;
|
||||
private final Integer port;
|
||||
private final Integer connTimeout;
|
||||
private final Integer dealTimeout;
|
||||
|
||||
public SunyardJnaSdfAdaptor(String ip, int port) {
|
||||
this(ip, port, "sdf");
|
||||
this(ip, port, 3000, 10000);
|
||||
}
|
||||
|
||||
public SunyardJnaSdfAdaptor(String ip, int port, String libName) {
|
||||
this(ip, port, libName, 3000, 3000);
|
||||
}
|
||||
public SunyardJnaSdfAdaptor(String ip, int port, int connTimeout, int dealTimeout) {
|
||||
super((SdfLibrary) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(),
|
||||
new Class[]{SdfLibrary.class},
|
||||
new InvocationHandler() {
|
||||
private final SunyardSdfLibrary sunyardSdfLibrary;
|
||||
|
||||
public SunyardJnaSdfAdaptor(String ip, int port, String libName, int connTimeout, int dealTimeout) {
|
||||
super(SDF_LIB_MAP.computeIfAbsent(libName, k -> Native.load(libName, SunyardSdfLibrary.class)));
|
||||
{
|
||||
sunyardSdfLibrary = SDF_LIB_MAP.computeIfAbsent("sdf", k -> Native.load(k, SunyardSdfLibrary.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
Object res = method.invoke(sunyardSdfLibrary, args);
|
||||
if (!(res instanceof Integer)) {
|
||||
return res;
|
||||
}
|
||||
int resInt = (Integer) res;
|
||||
if (Objects.equals("SDF_ExternalVerify_ECC", method.getName())) {
|
||||
log.info("SDF_ExternalVerify_ECC: {}, 返回值: {}", method.getName(), Integer.toHexString(resInt));
|
||||
return resInt;
|
||||
}
|
||||
if (method.getName().startsWith("SDF_")) {
|
||||
if (resInt != SdrCode.SDR_OK.getCode()) {
|
||||
log.warn("调用异常: {}, 返回值: {} -{}", method.getName(), Integer.toHexString(resInt), resInt);
|
||||
SdrCode sdrCode = SdrCode.of((Integer) res);
|
||||
throw new IllegalArgumentException(sdrCode != null ? sdrCode.getMsg() : "调用sdf接口异常: " + Integer.toHexString(resInt));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
));
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
this.connTimeout = connTimeout;
|
||||
@ -41,7 +76,7 @@ public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor {
|
||||
|
||||
@Override
|
||||
public String openDevice() {
|
||||
SunyardSdfLibrary sunyardSdfLibrary = (SunyardSdfLibrary) sdfLibrary;
|
||||
SunyardSdfLibrary sunyardSdfLibrary = SDF_LIB_MAP.computeIfAbsent("sdf", k -> Native.load(k, SunyardSdfLibrary.class));
|
||||
PointerByReference phDeviceHandle = new PointerByReference();
|
||||
sunyardSdfLibrary.SDF_OpenDevice(phDeviceHandle, safeStringBytes(ip), port, connTimeout, dealTimeout, 0);
|
||||
String key = UUID.randomUUID().toString();
|
||||
@ -50,10 +85,11 @@ public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAlgId(String alg) {
|
||||
return SunyardAlgId.valueOf(alg).getValue();
|
||||
protected int getAlgId(AlgId alg) {
|
||||
return SunyardAlgId.valueOf(alg.name()).getValue();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] safeStringBytes(String str) {
|
||||
if (null == str || str.isEmpty()) {
|
||||
return new byte[]{0x30};
|
||||
|
@ -4,15 +4,15 @@ package com.sunyard.chsm.sdf.context;
|
||||
* @author liulu
|
||||
* @since 2024/11/5
|
||||
*/
|
||||
public interface AlgId {
|
||||
public enum AlgId {
|
||||
SGD_SM3,
|
||||
|
||||
String SGD_SM3 = "SGD_SM3";
|
||||
SGD_SM4_CBC,
|
||||
SGD_SM4_ECB,
|
||||
SGD_SM4_MAC,
|
||||
|
||||
String SGD_SM4_CBC = "SGD_SMS4_CBC";
|
||||
String SGD_SM4_ECB = "SGD_SMS4_ECB";
|
||||
String SGD_SM4_MAC = "SGD_SMS4_MAC";
|
||||
|
||||
String SGD_SM2_1 = "SGD_SM2_1"; // 签名验签
|
||||
String SGD_SM2_2 = "SGD_SM2_2";
|
||||
String SGD_SM2_3 = "SGD_SM2_3"; // 加密解密
|
||||
SGD_SM2_1,// 签名验签
|
||||
SGD_SM2_2,
|
||||
SGD_SM2_3,// 加密解密
|
||||
;
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
package com.sunyard.chsm.sdf.context;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/11/5
|
||||
*/
|
||||
@Data
|
||||
public class DeviceContext {
|
||||
|
||||
private String serviceIp;
|
||||
private Integer servicePort;
|
||||
private String manufacturer;
|
||||
private String manufacturerModel;
|
||||
private String accessCredentials;
|
||||
private String jnaLibName;
|
||||
|
||||
private Integer weight;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package com.sunyard.chsm.sdf.context;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/7
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum SdrCode {
|
||||
|
||||
SDR_OK(0x0, "操作成功"),
|
||||
SDR_BASE(0x01000000, "错误码基础值"),
|
||||
|
||||
SDR_UNKNOWER(SDR_BASE.code + 0x00000001, "未知错误"),
|
||||
SDR_NOTSUPPORT(SDR_BASE.code + 0x00000002, "不支持的接口调用"),
|
||||
SDR_COMMFAIL(SDR_BASE.code + 0x00000003, "与设备通信失败"),
|
||||
SDR_HARDFAIL(SDR_BASE.code + 0x00000004, "运算模块无响应"),
|
||||
SDR_OPENDEVICE(SDR_BASE.code + 0x00000005, "打开设备失败"),
|
||||
SDR_OPENSESSION(SDR_BASE.code + 0x00000006, "创建会话失败"),
|
||||
SDR_PARDENY(SDR_BASE.code + 0x00000007, "无私钥使用权限"),
|
||||
SDR_KEYNOTEXIST(SDR_BASE.code + 0x00000008, "不存在的密钥调用"),
|
||||
SDR_ALGNOTSUPPORT(SDR_BASE.code + 0x00000009, "不支持的算法调用"),
|
||||
SDR_ALGMODNOTSUPPORT(SDR_BASE.code + 0x0000000A, "不支持的算法模式调用"),
|
||||
SDR_PKOPERR(SDR_BASE.code + 0x0000000B, "公钥运算失败"),
|
||||
SDR_SKOPERR(SDR_BASE.code + 0x0000000C, "私钥运算失败"),
|
||||
SDR_SIGNERR(SDR_BASE.code + 0x0000000D, "签名运算失败"),
|
||||
SDR_VERIFYERR(SDR_BASE.code + 0x0000000E, "验证签名失败"),
|
||||
SDR_SYMOPERR(SDR_BASE.code + 0x0000000F, "对称算法运算失败"),
|
||||
SDR_STEPERR(SDR_BASE.code + 0x00000010, "多步运算步骤错误"),
|
||||
SDR_FILESIZEERR(SDR_BASE.code + 0x00000011, "文件长度超出限制"),
|
||||
SDR_FILENOEXIST(SDR_BASE.code + 0x00000012, "指定的文件不存在"),
|
||||
SDR_FILEOFSERR(SDR_BASE.code + 0x00000013, "文件起始位置错误"),
|
||||
SDR_KEYTYPEERR(SDR_BASE.code + 0x00000014, "密钥类型错误"),
|
||||
SDR_KEYERR(SDR_BASE.code + 0x00000015, "密钥错误"),
|
||||
SDR_ENCDATAERR(SDR_BASE.code + 0x00000016, "ECC加密数据错误"),
|
||||
SDR_RANDERR(SDR_BASE.code + 0x00000017, "随机数产生失败"),
|
||||
SDR_PRKERR(SDR_BASE.code + 0x00000018, "私钥使用权限获取失败"),
|
||||
SDR_MACERR(SDR_BASE.code + 0x00000019, "MAC运算失败"),
|
||||
SDR_FILEEXISTS(SDR_BASE.code + 0x0000001A, "指定文件已存在"),
|
||||
SDR_FILEWERR(SDR_BASE.code + 0x0000001B, "文件写入失败"),
|
||||
SDR_NOBUFFER(SDR_BASE.code + 0x0000001C, "存储空间不足"),
|
||||
SDR_INARGERR(SDR_BASE.code + 0x0000001D, "输入参数错误"),
|
||||
SDR_OUTARGERR(SDR_BASE.code + 0x0000001E, "输出参数错误"),
|
||||
SDR_FILERDERR(SDR_BASE.code + 0x0000001F, "文件读取失败"),
|
||||
SDR_CONSULTERR(SDR_BASE.code + 0x00000020, "密钥协商错误"),
|
||||
SDR_LEVELERR(SDR_BASE.code + 0x00000021, "权限不足"),
|
||||
SDR_INDEXERR(SDR_BASE.code + 0x00000022, "索引错误"),
|
||||
SDR_KEYLENERR(SDR_BASE.code + 0x00000023, "密钥长度错误"),
|
||||
SDR_DATALENERR(SDR_BASE.code + 0x00000024, "数据长度错误"),
|
||||
SDR_RIGHTLENERR(SDR_BASE.code + 0x00000025, "私钥授权码长度错误"),
|
||||
SDR_RIGHTERR(SDR_BASE.code + 0x00000026, "私钥授权码错误"),
|
||||
SDR_FILEEMPTYERR(SDR_BASE.code + 0x00000027, "文件为空"),
|
||||
SDR_ERRSTATE(SDR_BASE.code + 0x00000028, "处于错误状态"),
|
||||
SDR_INITSTATE(SDR_BASE.code + 0x00000029, "处于初始状态"),
|
||||
SDR_RPRKERR(SDR_BASE.code + 0x0000002A, "私钥使用权限释放失败"),
|
||||
SDR_SIGNPRKERR(SDR_BASE.code + 0x0000002B, "签名公钥导出失败"),
|
||||
SDR_ENCPRKERR(SDR_BASE.code + 0x0000002C, "加密公钥导出失败"),
|
||||
SDR_KPERR(SDR_BASE.code + 0x0000002D, "密钥对生成失败"),
|
||||
SDR_GENERATEKEYERR(SDR_BASE.code + 0x0000002E, "会话密钥生成失败"),
|
||||
SDR_IMKEYERR(SDR_BASE.code + 0x0000002F, "导入密钥失败"),
|
||||
SDR_INTOEXERR(SDR_BASE.code + 0x00000030, "数字信封转换失败"),
|
||||
|
||||
/* API新增错误码 */
|
||||
SRD_DEV_FILE_CONFIG_ERR(SDR_BASE.code + 0x00000040, "配置文件信息错误(请检查配置文件的信息是否有误)"),
|
||||
SDR_DEVICEHANDLE_INVAILD(SDR_BASE.code + 0x00000041, "设备句柄错误(请检查句柄是否初始化或已经被释放)"),
|
||||
SDR_SESSIONHANLE_INVAILD(SDR_BASE.code + 0x00000042, "会话句柄错误(请检查句柄是否初始化或已经被释放)"),
|
||||
SDR_KEYHANLE_INVAILD(SDR_BASE.code + 0x00000043, "密钥句柄错误(请检查句柄是否初始化或已经被释放)"),
|
||||
SDR_AGREEMENTHANLE_INVAILD(SDR_BASE.code + 0x00000044, "协商句柄错误(请检查句柄是否初始化或已经被释放)"),
|
||||
SDR_UNKNOWN_HANDLE(SDR_BASE.code + 0x00000045, "未知的句柄"),
|
||||
SDR_NO_AVAILABL_DEVICE(SDR_BASE.code + 0x00000046, "此设备无法连接, 请检查IP和端口是否正确"),
|
||||
|
||||
|
||||
SYD_KEYSCHEMEERR(0x26, "错误密钥方案"),
|
||||
SYD_PRIKEYPWDERR(0x02, "私钥授权码验证错误"),
|
||||
SYD_HASHERR(0x29, "哈希失败(请联系管理员)"),
|
||||
SYD_CARDERR(0x47, "密码卡计算错误(请联系管理员)"),
|
||||
SYD_PACKAGELENERR(0x80, "数据包长度错误(请联系管理员)"),
|
||||
SYD_GETKEYERR(0x21, "获取密钥失败,检查密钥状态"),
|
||||
SYD_LMKCALCERR(0x13, "主密钥计算失败(请联系管理员)"),
|
||||
SYD_PKGPARAMERR(0x15, "输入参数错误(请联系管理员)"),
|
||||
SYD_AGREEMENTERR(0x06, "ECC密钥协商失败(请联系管理员)"),
|
||||
SYD_LMKDECPRIKEYERR(0x18, "主密钥解密私钥错误(请联系管理员)"),
|
||||
SYD_SYMENCDECERR(0x14, "对称加解密错误(请联系管理员)"),
|
||||
SYD_FILEEXISTS(0x71, "文件已存在"),
|
||||
SYD_FILENOEXIST(0x72, "文件不存在"),
|
||||
SYD_FILECREATEERR(0x73, "文件创建失败(请联系管理员)"),
|
||||
SYD_FILERDERR(0x74, "文件读取失败"),
|
||||
SYD_FILEWERR(0x75, "文件写入失败"),
|
||||
SYD_FILEDELEERR(0x76, "文件删除失败(请联系管理员)"),
|
||||
SYD_FILESIZEERR(0x77, "超出文件大小限制"),
|
||||
SYD_FILEOPENEERR(0x78, "文件打开失败(请联系管理员)"),
|
||||
SYD_FILEEMPTYERR(0x79, "文件为空"),
|
||||
SYD_VERIFYERR(0x8002, "验签错误"),
|
||||
|
||||
;
|
||||
|
||||
private final int code;
|
||||
private final String msg;
|
||||
|
||||
public static SdrCode of(int code) {
|
||||
|
||||
return Arrays.stream(SdrCode.values())
|
||||
.filter(it -> Objects.equals(it.code, code))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
@ -179,6 +179,27 @@ public interface SdfLibrary extends Library {
|
||||
*/
|
||||
int SDF_ExchangeDigitEnvelopeBaseOnECC(Pointer hSessionHandle, int uiKeyIndex, int uiAlgID, byte[] pucPublicKey, byte[] pucEncDateIn, byte[] pucEncDateOut);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hSessionHandle 与设备建立的会话句柄
|
||||
* @param pucKey 缓冲区指针,用于存放输入的密钥密文
|
||||
* @param puiKeyLength 返回的密钥明文长度
|
||||
* @param phKeyHandle 返回的密钥句柄
|
||||
* @return 0 成功; 非0 失败,返回错误代码
|
||||
*/
|
||||
int SDF_ImportKey(Pointer hSessionHandle, byte[] pucKey, int puiKeyLength, PointerByReference phKeyHandle);
|
||||
|
||||
/**
|
||||
* 获取私钥使用权限
|
||||
*
|
||||
* @param hSessionHandle 与设备建立的会话句柄
|
||||
* @param uiKeyIndex 密码设备存储私钥的索引值
|
||||
* @param pucPassword 使用私钥权限的标识码
|
||||
* @param uiPwdLength 私钥访问控制码长度,不少于8字节
|
||||
* @return 0 成功; 非0 失败,返回错误代码
|
||||
*/
|
||||
int SDF_GetPrivateKeyAccessRight(Pointer hSessionHandle, int uiKeyIndex, byte[] pucPassword, int uiPwdLength);
|
||||
|
||||
/**
|
||||
* 导入会话密钥并用内部ECC私钥解密
|
||||
*
|
||||
@ -200,7 +221,7 @@ public interface SdfLibrary extends Library {
|
||||
* @param phKeyhandle
|
||||
* @return
|
||||
*/
|
||||
int SDF_DestroyKey(Pointer phSessionHandle, PointerByReference phKeyhandle);
|
||||
int SDF_DestroyKey(Pointer phSessionHandle, Pointer phKeyhandle);
|
||||
|
||||
|
||||
/**
|
||||
@ -262,7 +283,7 @@ public interface SdfLibrary extends Library {
|
||||
* @param pucIDlength 签名者ID的长度
|
||||
* @return int 响应码
|
||||
*/
|
||||
int SDF_HashInit(Pointer phSessionHandle, int uiAlgID, byte[] pucPublicKey, String pucID, int pucIDlength);
|
||||
int SDF_HashInit(Pointer phSessionHandle, int uiAlgID, byte[] pucPublicKey, byte[] pucID, int pucIDlength);
|
||||
|
||||
/**
|
||||
* 多包杂凑运算
|
||||
|
@ -8,7 +8,6 @@ import com.sun.jna.ptr.PointerByReference;
|
||||
*/
|
||||
public interface SunyardSdfLibrary extends SdfLibrary {
|
||||
|
||||
|
||||
/**
|
||||
* 打开设备
|
||||
* @param phDeviceHandle 设备句柄
|
||||
|
@ -1,8 +1,14 @@
|
||||
package com.sunyard.chsm.sdf.model;
|
||||
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -25,6 +31,9 @@ public class EccCipher {
|
||||
// 密文数据
|
||||
private byte[] C;
|
||||
|
||||
public String getC1C3C2Hex() {
|
||||
return CodecUtils.encodeHex(getC1C3C2Bytes());
|
||||
}
|
||||
|
||||
public byte[] getC1C3C2Bytes() {
|
||||
int xLen = x.length;
|
||||
@ -41,4 +50,44 @@ public class EccCipher {
|
||||
return rawData;
|
||||
}
|
||||
|
||||
public byte[] toSdfData() {
|
||||
byte[] d = new byte[164 + L];
|
||||
byte[] l = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(L).array();
|
||||
System.arraycopy(x, 0, d, 32, 32);
|
||||
System.arraycopy(y, 0, d, 96, 32);
|
||||
System.arraycopy(M, 0, d, 128, 32);
|
||||
System.arraycopy(l, 0, d, 160, 4);
|
||||
System.arraycopy(C, 0, d, 164, L);
|
||||
return d;
|
||||
}
|
||||
|
||||
public static EccCipher fromHex(String hex) {
|
||||
return fromBytes(CodecUtils.decodeHex(hex));
|
||||
}
|
||||
|
||||
public static EccCipher fromBytes(byte[] cipher) {
|
||||
Assert.notNull(cipher, "Ecc密文数据不能为null");
|
||||
if (cipher[0] == 0x04) {
|
||||
cipher = Arrays.copyOfRange(cipher, 1, cipher.length);
|
||||
}
|
||||
Assert.isTrue(cipher.length > 96, "Ecc加密数据格式错误");
|
||||
if (cipher.length > 164
|
||||
&& Arrays.equals(Arrays.copyOf(cipher, 32), new byte[32])
|
||||
&& Arrays.equals(Arrays.copyOfRange(cipher, 64, 96), new byte[32])) {
|
||||
int L = cipher.length - 164;
|
||||
byte[] x = Arrays.copyOfRange(cipher, 32, 64);
|
||||
byte[] y = Arrays.copyOfRange(cipher, 96, 128);
|
||||
byte[] m = Arrays.copyOfRange(cipher, 128, 160);
|
||||
byte[] c = Arrays.copyOfRange(cipher, 164, cipher.length);
|
||||
return new EccCipher(x, y, m, L, c);
|
||||
}
|
||||
|
||||
int L = cipher.length - 96;
|
||||
byte[] x = Arrays.copyOfRange(cipher, 0, 32);
|
||||
byte[] y = Arrays.copyOfRange(cipher, 32, 64);
|
||||
byte[] m = Arrays.copyOfRange(cipher, 64, 96);
|
||||
byte[] c = Arrays.copyOfRange(cipher, 96, cipher.length);
|
||||
return new EccCipher(x, y, m, L, c);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,11 @@ package com.sunyard.chsm.sdf.model;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -18,5 +23,24 @@ public class EccPriKey {
|
||||
// 私 钥
|
||||
private byte[] D;
|
||||
|
||||
public byte[] toSdfData() {
|
||||
byte[] sdf = new byte[68];
|
||||
System.arraycopy(Hex.decode("00010000"), 0, sdf, 0, 4);
|
||||
System.arraycopy(D, 0, sdf, 36, 32);
|
||||
return sdf;
|
||||
}
|
||||
|
||||
public static EccPriKey fromBytes(byte[] priKey) {
|
||||
Assert.notNull(priKey, "私钥数据不能为null");
|
||||
if (Objects.equals(priKey.length, 68)) {
|
||||
// 00010000
|
||||
priKey = Arrays.copyOfRange(priKey, 4, priKey.length);
|
||||
}
|
||||
Assert.isTrue(Objects.equals(priKey.length, 32) || Objects.equals(priKey.length, 64), "Ecc私钥数据格式错误");
|
||||
if (Objects.equals(priKey.length, 32)) {
|
||||
return new EccPriKey(256, priKey);
|
||||
}
|
||||
return new EccPriKey(256, Arrays.copyOfRange(priKey, 32, 64));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,11 @@ package com.sunyard.chsm.sdf.model;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -20,6 +25,9 @@ public class EccPubKey {
|
||||
// 公 钥 y 坐 标
|
||||
private byte[] y;
|
||||
|
||||
public String getPubKeyHex() {
|
||||
return Hex.toHexString(getPubKeyBytes());
|
||||
}
|
||||
|
||||
public byte[] getPubKeyBytes() {
|
||||
byte[] rawKey = new byte[x.length + y.length];
|
||||
@ -28,4 +36,38 @@ public class EccPubKey {
|
||||
return rawKey;
|
||||
}
|
||||
|
||||
public byte[] toSdfData() {
|
||||
byte[] sdf = new byte[132];
|
||||
System.arraycopy(Hex.decode("00010000"), 0, sdf, 0, 4);
|
||||
System.arraycopy(x, 0, sdf, 36, 32);
|
||||
System.arraycopy(y, 0, sdf, 100, 32);
|
||||
return sdf;
|
||||
}
|
||||
|
||||
public static EccPubKey fromHex(String pubKeyHex) {
|
||||
return fromBytes(Hex.decode(pubKeyHex));
|
||||
}
|
||||
|
||||
public static EccPubKey fromBytes(byte[] pubKey) {
|
||||
Assert.notNull(pubKey, "公钥数据不能为null");
|
||||
if (Objects.equals(pubKey.length, 65)) {
|
||||
Assert.isTrue(Objects.equals(pubKey[0], 0x04), "Ecc公钥数据格式错误,必须04开头");
|
||||
pubKey = Arrays.copyOfRange(pubKey, 1, pubKey.length);
|
||||
} else if (Objects.equals(pubKey.length, 132)) {
|
||||
// 00010000
|
||||
pubKey = Arrays.copyOfRange(pubKey, 4, pubKey.length);
|
||||
}
|
||||
Assert.isTrue(Objects.equals(pubKey.length, 64) || Objects.equals(pubKey.length, 128), "Ecc公钥数据格式错误");
|
||||
byte[] x = new byte[32];
|
||||
byte[] y = new byte[32];
|
||||
if (Objects.equals(pubKey.length, 64)) {
|
||||
System.arraycopy(pubKey, 0, x, 0, 32);
|
||||
System.arraycopy(pubKey, 32, y, 0, 32);
|
||||
} else if (Objects.equals(pubKey.length, 128)) {
|
||||
System.arraycopy(pubKey, 32, x, 0, 32);
|
||||
System.arraycopy(pubKey, 96, y, 0, 32);
|
||||
}
|
||||
return new EccPubKey(256, x, y);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
package com.sunyard.chsm.sdf.model;
|
||||
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @version V1.0
|
||||
@ -20,6 +25,19 @@ public class EccSignature {
|
||||
// 签名的 s 部分
|
||||
private byte[] s;
|
||||
|
||||
|
||||
public String getRawSignHex() {
|
||||
return CodecUtils.encodeHex(getRawSignBytes());
|
||||
}
|
||||
|
||||
public byte[] getDerSignBytes() {
|
||||
try {
|
||||
return BCSM2Utils.encodeSM2SignToDER(getRawSignBytes());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("ECC签名数据格式错误");
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getRawSignBytes() {
|
||||
byte[] signData = new byte[r.length + s.length];
|
||||
System.arraycopy(r, 0, signData, 0, r.length);
|
||||
@ -27,5 +45,32 @@ public class EccSignature {
|
||||
return signData;
|
||||
}
|
||||
|
||||
public byte[] toSdfData() {
|
||||
byte[] res = new byte[128];
|
||||
System.arraycopy(r, 0, res, 32, 32);
|
||||
System.arraycopy(s, 0, res, 96, 32);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static EccSignature fromBytes(byte[] sign) {
|
||||
if (sign == null || sign.length == 0) {
|
||||
throw new IllegalArgumentException("ECC签名数据格式错误");
|
||||
}
|
||||
if (sign.length >= 70 && sign.length <= 75) {
|
||||
sign = BCSM2Utils.decodeDERSM2Sign(sign);
|
||||
}
|
||||
|
||||
if (sign.length == 64) {
|
||||
byte[] r = Arrays.copyOfRange(sign, 0, 32);
|
||||
byte[] s = Arrays.copyOfRange(sign, 32, 64);
|
||||
return new EccSignature(r, s);
|
||||
} else if (sign.length == 128) {
|
||||
byte[] r = Arrays.copyOfRange(sign, 32, 64);
|
||||
byte[] s = Arrays.copyOfRange(sign, 96, 128);
|
||||
return new EccSignature(r, s);
|
||||
} else {
|
||||
throw new IllegalArgumentException("ECC签名数据格式错误");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,20 @@ import java.util.Arrays;
|
||||
*/
|
||||
public abstract class LangUtils {
|
||||
|
||||
|
||||
public static byte[] removeLastZero(byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
return bytes;
|
||||
}
|
||||
int len = bytes.length;
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
if (bytes[i] != 0) {
|
||||
return Arrays.copyOfRange(bytes, 0, i + 1);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] merge(byte[]... bytes) {
|
||||
int newLen = Arrays.stream(bytes).mapToInt(it -> it.length).sum();
|
||||
byte[] res = new byte[newLen];
|
||||
|
@ -1,10 +1,38 @@
|
||||
package com.sunyard.chsm.sdf.util;
|
||||
|
||||
import com.sunyard.chsm.enums.Padding;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
*/
|
||||
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) {
|
||||
if (content == null || blockSize < 8)
|
||||
throw new IllegalStateException("parameter error");
|
||||
@ -22,7 +50,7 @@ public abstract class PaddingUtil {
|
||||
return padded;
|
||||
}
|
||||
|
||||
public static byte[] PKCS7Padding(byte[] content) {
|
||||
public static byte[] PKCS7Padding(byte[] content) {
|
||||
try {
|
||||
return PKCS7Padding(content, 16);
|
||||
} catch (Exception e) {
|
||||
@ -30,14 +58,13 @@ public abstract class PaddingUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] PKCS5Padding(byte[] content) throws Exception {
|
||||
public static byte[] PKCS5Padding(byte[] content) {
|
||||
return PKCS7Padding(content, 8);
|
||||
}
|
||||
|
||||
public static byte[] PKCS7Unpadding(byte[] content, int blockSize)
|
||||
throws Exception {
|
||||
public static byte[] PKCS7Unpadding(byte[] content, int blockSize) {
|
||||
if (blockSize < 8 || content == null || content.length % blockSize != 0)
|
||||
throw new Exception("parameter error");
|
||||
throw new IllegalStateException("parameter error");
|
||||
return PKCS7Unpadding(content);
|
||||
}
|
||||
|
||||
@ -57,9 +84,8 @@ public abstract class PaddingUtil {
|
||||
return unpadded;
|
||||
}
|
||||
|
||||
public static byte[] PKCS5Unpadding(byte[] content) throws Exception {
|
||||
public static byte[] PKCS5Unpadding(byte[] content) {
|
||||
return PKCS7Unpadding(content, 8);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,9 @@ public interface KeyInfoService {
|
||||
|
||||
Page<KeyInfoDTO.KeyView> selectPageList(KeyInfoDTO.Query query);
|
||||
|
||||
Long save(KeyInfoDTO.KeySave save);
|
||||
KeyInfoDTO.KeyView selectById(Long id);
|
||||
|
||||
List<Long> save(KeyInfoDTO.KeySave save);
|
||||
|
||||
void update(KeyInfoDTO.KeyUpdate update);
|
||||
|
||||
|
@ -0,0 +1,405 @@
|
||||
package com.sunyard.chsm.service;
|
||||
|
||||
import com.sunyard.chsm.constant.ParamConfKeyConstant;
|
||||
import com.sunyard.chsm.enums.DeviceTmkStatus;
|
||||
import com.sunyard.chsm.mapper.ParamConfMapper;
|
||||
import com.sunyard.chsm.mapper.SpDeviceMapper;
|
||||
import com.sunyard.chsm.model.dto.DeviceCheckRes;
|
||||
import com.sunyard.chsm.model.dto.TmkStatus;
|
||||
import com.sunyard.chsm.model.entity.Device;
|
||||
import com.sunyard.chsm.model.entity.ParamConf;
|
||||
import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
|
||||
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.sdf.model.DeviceInfo;
|
||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||
import com.sunyard.chsm.sdf.model.EccPubKey;
|
||||
import com.sunyard.chsm.sdf.util.LangUtils;
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM3Utils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/10
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TmkService {
|
||||
|
||||
private final SpDeviceMapper spDeviceMapper;
|
||||
private final ParamConfMapper paramConfMapper;
|
||||
|
||||
@Transactional
|
||||
public void initTmk() {
|
||||
boolean tmkInit = isTmkInit();
|
||||
Assert.isTrue(!tmkInit, "主密钥已经初始化");
|
||||
|
||||
Device device = getOneByStatus(DeviceTmkStatus.available);
|
||||
Assert.notNull(device, "没有可以用于初始化主密钥的设备");
|
||||
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
|
||||
|
||||
String hd = sdfApi.openDevice();
|
||||
String hs = sdfApi.openSession(hd);
|
||||
byte[] rk = sdfApi.generateRandom(hs, 16);
|
||||
EccPubKey pubKey = sdfApi.exportEncPublicKeyECC(hs, device.getEncKeyIdx());
|
||||
EccCipher cipher = sdfApi.externalEncryptECC(hs, pubKey, rk);
|
||||
sdfApi.getPrivateKeyAccessRight(hs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
|
||||
String hk = sdfApi.importKeyWithISKECC(hs, device.getEncKeyIdx(), cipher);
|
||||
byte[] encrk = sdfApi.symEncrypt(hs, hk, AlgId.SGD_SM4_ECB, new byte[0], rk);
|
||||
byte[] prk = sdfApi.symDecrypt(hs, hk, AlgId.SGD_SM4_ECB, new byte[0], encrk);
|
||||
Assert.isTrue(Arrays.equals(rk, prk), "密码机加解密异常");
|
||||
|
||||
byte[] hash = BCSM3Utils.hash(rk);
|
||||
|
||||
sdfApi.destroyKey(hs, hk);
|
||||
sdfApi.closeSession(hs);
|
||||
sdfApi.closeDevice(hd);
|
||||
|
||||
if (Objects.equals(device.getManufacturerModel(), BouncyCastleProvider.PROVIDER_NAME)) {
|
||||
updateSoftDeviceEncTmk(cipher.getC1C3C2Bytes());
|
||||
} else {
|
||||
Device up = new Device();
|
||||
up.setId(device.getId());
|
||||
up.setEncTmk(cipher.getC1C3C2Hex());
|
||||
up.setTmkStatus(DeviceTmkStatus.finished.name());
|
||||
spDeviceMapper.updateById(up);
|
||||
}
|
||||
updateTmkInit(true, hash);
|
||||
|
||||
}
|
||||
|
||||
public TmkStatus getTMKStatus() {
|
||||
TmkStatus status = new TmkStatus();
|
||||
boolean init = isTmkInit();
|
||||
if (init) {
|
||||
status.setHasDevice(true);
|
||||
status.setTmkInit(true);
|
||||
ParamConf paramConf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_CHECK_VALUE);
|
||||
if (paramConf != null) {
|
||||
status.setCheckValue(paramConf.getValue());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
status.setTmkInit(false);
|
||||
Device device = getOneByStatus(DeviceTmkStatus.available);
|
||||
status.setHasDevice(device != null);
|
||||
return status;
|
||||
}
|
||||
|
||||
public String backup(String pubKey) {
|
||||
|
||||
boolean tmkInit = isTmkInit();
|
||||
Assert.isTrue(tmkInit, "主密钥未初始化");
|
||||
Device device = getOneByStatus(DeviceTmkStatus.finished);
|
||||
Assert.notNull(device, "没有可以用于备份主密钥的设备");
|
||||
byte[] xy;
|
||||
try {
|
||||
String pubBase6 = pubKey.replace("-----BEGIN ECDSA PUBLIC KEY-----", "").replace("-----END ECDSA PUBLIC KEY-----", "")
|
||||
.replace("\n", "");
|
||||
byte[] pubBytes = CodecUtils.decodeBase64(pubBase6);
|
||||
SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pubBytes);
|
||||
BCECPublicKey key = BCECUtils.createPublicKeyFromSubjectPublicKeyInfo(keyInfo);
|
||||
xy = LangUtils.merge(key.getQ().getXCoord().getEncoded(), key.getQ().getYCoord().getEncoded());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
|
||||
|
||||
String hd = sdfApi.openDevice();
|
||||
String hs = sdfApi.openSession(hd);
|
||||
sdfApi.getPrivateKeyAccessRight(hs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
|
||||
|
||||
EccCipher cipher = sdfApi.exchangeDigitEnvelopeBaseOnECC(hs, device.getEncKeyIdx(), EccPubKey.fromBytes(xy), EccCipher.fromHex(device.getEncTmk()));
|
||||
|
||||
try {
|
||||
byte[] der = BCSM2Utils.encodeSM2CipherToDER(LangUtils.merge(new byte[]{0x04}, cipher.getC1C3C2Bytes()));
|
||||
return CodecUtils.encodeBase64(der);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Pair<Long, String> getDevicePubKey() {
|
||||
Device device = getOneByStatus(DeviceTmkStatus.available);
|
||||
Assert.notNull(device, "没有可以用于导入主密钥的设备");
|
||||
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
|
||||
String hd = sdfApi.openDevice();
|
||||
String hs = sdfApi.openSession(hd);
|
||||
EccPubKey pubKey = sdfApi.exportEncPublicKeyECC(hs, device.getEncKeyIdx());
|
||||
|
||||
BCECPublicKey publicKey = BCECUtils.createPublicKey(pubKey.getPubKeyHex());
|
||||
return Pair.of(device.getId(), CodecUtils.encodeBase64(publicKey.getEncoded()));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void importTmk(Long deviceId, String encTmk) {
|
||||
boolean tmkInit = isTmkInit();
|
||||
Assert.isTrue(!tmkInit, "主密钥已经初始化");
|
||||
|
||||
Device device = getOneByStatus(DeviceTmkStatus.available);
|
||||
Assert.notNull(device, "没有可以用于导入主密钥的设备");
|
||||
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
|
||||
String hd = sdfApi.openDevice();
|
||||
String hs = sdfApi.openSession(hd);
|
||||
byte[] rk = CodecUtils.decodeBase64(encTmk);
|
||||
EccPubKey pubKey = sdfApi.exportEncPublicKeyECC(hs, device.getEncKeyIdx());
|
||||
EccCipher cipher = sdfApi.externalEncryptECC(hs, pubKey, rk);
|
||||
sdfApi.getPrivateKeyAccessRight(hs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
|
||||
String hk = sdfApi.importKeyWithISKECC(hs, device.getEncKeyIdx(), cipher);
|
||||
byte[] encrk = sdfApi.symEncrypt(hs, hk, AlgId.SGD_SM4_ECB, new byte[0], rk);
|
||||
byte[] prk = sdfApi.symDecrypt(hs, hk, AlgId.SGD_SM4_ECB, new byte[0], encrk);
|
||||
Assert.isTrue(Arrays.equals(rk, prk), "密码机加解密异常");
|
||||
|
||||
byte[] hash = BCSM3Utils.hash(rk);
|
||||
|
||||
sdfApi.destroyKey(hs, hk);
|
||||
sdfApi.closeSession(hs);
|
||||
sdfApi.closeDevice(hd);
|
||||
|
||||
if (Objects.equals(device.getManufacturerModel(), BouncyCastleProvider.PROVIDER_NAME)) {
|
||||
updateSoftDeviceEncTmk(cipher.getC1C3C2Bytes());
|
||||
} else {
|
||||
Device up = new Device();
|
||||
up.setId(device.getId());
|
||||
up.setEncTmk(cipher.getC1C3C2Hex());
|
||||
up.setTmkStatus(DeviceTmkStatus.finished.name());
|
||||
spDeviceMapper.updateById(up);
|
||||
}
|
||||
updateTmkInit(true, hash);
|
||||
}
|
||||
|
||||
|
||||
public DeviceCheckRes checkDevice(Device check) {
|
||||
|
||||
log.debug("==========> begin check device : {}", check);
|
||||
DeviceCheckRes res = new DeviceCheckRes();
|
||||
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(check.getManufacturerModel(), check.getServiceIp(), check.getServicePort());
|
||||
String hd = sdfApi.openDevice();
|
||||
String hs = null;
|
||||
|
||||
try {
|
||||
hs = sdfApi.openSession(hd);
|
||||
DeviceInfo info = sdfApi.getDeviceInfo(hs);
|
||||
log.debug("get DeviceInfo: {}", info);
|
||||
res.setDeviceSerial(info.getDeviceSerial());
|
||||
res.setStatus(DeviceTmkStatus.key_error);
|
||||
} catch (Exception e) {
|
||||
log.warn("check device connect error: {}:{}", check.getServiceIp(), check.getServicePort(), e);
|
||||
res.setHasError(true);
|
||||
res.setStatus(DeviceTmkStatus.device_error);
|
||||
res.setMessage(e.getMessage());
|
||||
}
|
||||
if (res.isHasError()) {
|
||||
Optional.ofNullable(hs).ifPresent(sdfApi::closeSession);
|
||||
Optional.ofNullable(hd).ifPresent(sdfApi::closeDevice);
|
||||
log.debug("==========> end check device : {}", res);
|
||||
return res;
|
||||
}
|
||||
try {
|
||||
EccPubKey pubKey = sdfApi.exportEncPublicKeyECC(hs, check.getEncKeyIdx());
|
||||
if (Arrays.equals(pubKey.getPubKeyBytes(), new byte[64])) {
|
||||
throw new IllegalArgumentException("加密密钥索引不正确");
|
||||
}
|
||||
res.setPubKey(pubKey.getPubKeyHex());
|
||||
log.debug("export enc pubKey at index: {}, {}", check.getEncKeyIdx(), res.getPubKey());
|
||||
sdfApi.getPrivateKeyAccessRight(hs, check.getEncKeyIdx(), check.getAccessCredentials().getBytes());
|
||||
if (isTmkInit()) {
|
||||
log.debug("tmk is init...");
|
||||
EccCipher cipher;
|
||||
if (Objects.equals(check.getDeviceSerial(), res.getDeviceSerial())
|
||||
&& Objects.equals(check.getPubKey(), res.getPubKey())
|
||||
&& StringUtils.hasText(check.getEncTmk())) {
|
||||
cipher = EccCipher.fromHex(check.getEncTmk());
|
||||
log.debug("device serial, pubKey not changed, use origin device enc tmk");
|
||||
} else {
|
||||
if (DeviceTmkStatus.finished.name().equals(check.getTmkStatus())) {
|
||||
res.setStatus(DeviceTmkStatus.available);
|
||||
return res;
|
||||
}
|
||||
log.debug("device serial, pubKey is changed, or no tmk in origin device");
|
||||
Device device = getOneByStatus(DeviceTmkStatus.finished);
|
||||
Assert.notNull(device, "系统主密钥设备异常,请联系管理员排查");
|
||||
log.debug("get device from finished: {}", device);
|
||||
SdfApiAdapter tmkAdapter = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
|
||||
String tmkHd = tmkAdapter.openDevice();
|
||||
String tmkHs = tmkAdapter.openSession(tmkHd);
|
||||
tmkAdapter.getPrivateKeyAccessRight(tmkHs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
|
||||
cipher = tmkAdapter.exchangeDigitEnvelopeBaseOnECC(tmkHs, device.getEncKeyIdx(), pubKey, EccCipher.fromHex(device.getEncTmk()));
|
||||
log.debug("exchanged envelope from finished success ...");
|
||||
}
|
||||
String hk = sdfApi.importKeyWithISKECC(hs, check.getEncKeyIdx(), cipher);
|
||||
sdfApi.destroyKey(hs, hk);
|
||||
if (!Objects.equals(check.getEncTmk(), cipher.getC1C3C2Hex())) {
|
||||
res.setEncTmk(cipher.getC1C3C2Hex());
|
||||
}
|
||||
res.setStatus(DeviceTmkStatus.finished);
|
||||
} else {
|
||||
log.debug("tmk not init, start encrypt and import test ...");
|
||||
byte[] random = sdfApi.generateRandom(hs, 16);
|
||||
EccCipher eccCipher = sdfApi.externalEncryptECC(hs, pubKey, random);
|
||||
String hk = sdfApi.importKeyWithISKECC(hs, check.getEncKeyIdx(), eccCipher);
|
||||
sdfApi.destroyKey(hs, hk);
|
||||
res.setStatus(DeviceTmkStatus.available);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("check device encKey error: {}:{}", check.getServiceIp(), check.getServicePort(), e);
|
||||
res.setHasError(true);
|
||||
res.setStatus(DeviceTmkStatus.key_error);
|
||||
res.setMessage(e.getMessage());
|
||||
}
|
||||
Optional.ofNullable(hs).ifPresent(sdfApi::closeSession);
|
||||
Optional.ofNullable(hd).ifPresent(sdfApi::closeDevice);
|
||||
res.setSdfApiAdapter(sdfApi);
|
||||
log.debug("==========> end check device : {}", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
public void checkSoftDeviceTmk() {
|
||||
if (!isTmkInit() || !isEnableSoftDevice()) {
|
||||
return;
|
||||
}
|
||||
if (Objects.nonNull(getSoftDeviceEncTmk())) {
|
||||
return;
|
||||
}
|
||||
log.warn("enabled soft device but no tmk in soft");
|
||||
Device device = getOneByStatus(DeviceTmkStatus.finished);
|
||||
if (device == null || Objects.equals(device.getManufacturerModel(), BouncyCastleProvider.PROVIDER_NAME)) {
|
||||
log.warn("data error, no tmk found in system");
|
||||
return;
|
||||
}
|
||||
SdfApiAdapter softAdapter = SdfApiAdapterFactory.getBcAdapter();
|
||||
EccPubKey pubKey = softAdapter.exportEncPublicKeyECC("", 1);
|
||||
|
||||
SdfApiAdapter tmkAdapter = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
|
||||
String tmkHd = tmkAdapter.openDevice();
|
||||
String tmkHs = tmkAdapter.openSession(tmkHd);
|
||||
tmkAdapter.getPrivateKeyAccessRight(tmkHs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
|
||||
EccCipher cipher = tmkAdapter.exchangeDigitEnvelopeBaseOnECC(tmkHs, device.getEncKeyIdx(), pubKey, EccCipher.fromHex(device.getEncTmk()));
|
||||
|
||||
updateSoftDeviceEncTmk(cipher.getC1C3C2Bytes());
|
||||
}
|
||||
|
||||
private Device getOneByStatus(DeviceTmkStatus status) {
|
||||
Device device = spDeviceMapper.selectOneByStatus(status);
|
||||
if (Objects.nonNull(device)) {
|
||||
return device;
|
||||
}
|
||||
if (isEnableSoftDevice()) {
|
||||
device = new Device();
|
||||
device.setId(0L);
|
||||
device.setManufacturerModel(BouncyCastleProvider.PROVIDER_NAME);
|
||||
device.setEncKeyIdx(1);
|
||||
device.setServiceIp("127.0.0.1");
|
||||
device.setServicePort(8889);
|
||||
device.setAccessCredentials("543");
|
||||
if (isTmkInit()) {
|
||||
device.setEncTmk(CodecUtils.encodeHex(getSoftDeviceEncTmk()));
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean tmkInit;
|
||||
private boolean enableSoftDevice;
|
||||
private byte[] softEncTmk;
|
||||
|
||||
public synchronized boolean isTmkInitCached() {
|
||||
if (tmkInit) {
|
||||
return true;
|
||||
}
|
||||
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
|
||||
tmkInit = conf != null && String.valueOf(true).equals(conf.getValue());
|
||||
return tmkInit;
|
||||
}
|
||||
|
||||
public boolean isTmkInit() {
|
||||
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
|
||||
return conf != null && String.valueOf(true).equals(conf.getValue());
|
||||
}
|
||||
|
||||
public boolean isEnableSoftDevice() {
|
||||
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.ENABLE_SOFT_DEVICE);
|
||||
return conf != null && String.valueOf(true).equals(conf.getValue());
|
||||
}
|
||||
|
||||
private void updateTmkInit(boolean value, byte[] hash) {
|
||||
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
|
||||
if (conf == null) {
|
||||
conf = new ParamConf();
|
||||
conf.setValue(String.valueOf(value));
|
||||
conf.setKey(ParamConfKeyConstant.TMK_INIT);
|
||||
conf.setCreatTime(LocalDateTime.now());
|
||||
paramConfMapper.insert(conf);
|
||||
} else {
|
||||
conf.setValue(String.valueOf(value));
|
||||
paramConfMapper.updateById(conf);
|
||||
}
|
||||
|
||||
ParamConf check = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_CHECK_VALUE);
|
||||
if (check == null) {
|
||||
check = new ParamConf();
|
||||
check.setValue(CodecUtils.encodeHex(hash));
|
||||
check.setKey(ParamConfKeyConstant.TMK_CHECK_VALUE);
|
||||
check.setCreatTime(LocalDateTime.now());
|
||||
paramConfMapper.insert(check);
|
||||
} else {
|
||||
check.setValue(CodecUtils.encodeHex(hash));
|
||||
paramConfMapper.updateById(check);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized byte[] getSoftDeviceEncTmk() {
|
||||
boolean tmkInit = isTmkInit();
|
||||
Assert.isTrue(tmkInit, "主密钥未初始化");
|
||||
boolean enabled = isEnableSoftDevice();
|
||||
Assert.isTrue(enabled, "未启用软设备");
|
||||
if (softEncTmk != null) {
|
||||
return softEncTmk;
|
||||
}
|
||||
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.SOFT_ENC_TMK);
|
||||
if (conf == null || ObjectUtils.isEmpty(conf.getValue())) {
|
||||
return null;
|
||||
}
|
||||
softEncTmk = CodecUtils.decodeBase64(conf.getValue());
|
||||
return softEncTmk;
|
||||
}
|
||||
|
||||
private void updateSoftDeviceEncTmk(byte[] encTmk) {
|
||||
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.SOFT_ENC_TMK);
|
||||
if (conf == null) {
|
||||
conf = new ParamConf();
|
||||
conf.setValue(CodecUtils.encodeBase64(encTmk));
|
||||
conf.setKey(ParamConfKeyConstant.SOFT_ENC_TMK);
|
||||
conf.setCreatTime(LocalDateTime.now());
|
||||
paramConfMapper.insert(conf);
|
||||
} else {
|
||||
conf.setValue(CodecUtils.encodeBase64(encTmk));
|
||||
paramConfMapper.updateById(conf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ import com.sunyard.chsm.mapper.AppCertMapper;
|
||||
import com.sunyard.chsm.mapper.ApplicationMapper;
|
||||
import com.sunyard.chsm.mapper.KeyInfoMapper;
|
||||
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
|
||||
import com.sunyard.chsm.model.Subject;
|
||||
import com.sunyard.chsm.model.dto.CertDTO;
|
||||
import com.sunyard.chsm.model.entity.AppCert;
|
||||
import com.sunyard.chsm.model.entity.Application;
|
||||
@ -24,12 +25,7 @@ import com.sunyard.chsm.utils.gm.BCSM4Utils;
|
||||
import com.sunyard.chsm.utils.gm.cert.BCSM2CertUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bouncycastle.asn1.ASN1BitString;
|
||||
import org.bouncycastle.asn1.ASN1Integer;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.ASN1OctetString;
|
||||
import org.bouncycastle.asn1.ASN1Sequence;
|
||||
import org.bouncycastle.asn1.DLSequence;
|
||||
import org.bouncycastle.asn1.*;
|
||||
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
@ -49,12 +45,7 @@ import java.nio.ByteBuffer;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -154,6 +145,8 @@ public class AppCertServiceImpl implements AppCertService {
|
||||
|
||||
AppCert exist = appCertMapper.selectBySN(x509Cert.getSerialNumber().toString());
|
||||
Assert.isNull(exist, "此证书已经存在");
|
||||
exist = appCertMapper.selectByTypeAndDn(importCert.getCertType(), x509Cert.getSubjectX500Principal().getName());
|
||||
Assert.isNull(exist, "此证书主题已经存在");
|
||||
|
||||
AppCert cert = genCert(x509Cert, keyInfo.getApplicationId(), record, importCert);
|
||||
appCertMapper.insert(cert);
|
||||
@ -229,7 +222,8 @@ public class AppCertServiceImpl implements AppCertService {
|
||||
cert.setCertText(importCert.getCertText());
|
||||
|
||||
cert.setVersion(String.valueOf(x509Cert.getVersion()));
|
||||
cert.setSubject(x509Cert.getSubjectX500Principal().getName());
|
||||
Subject subject = Subject.fromDN(x509Cert.getSubjectX500Principal().getName());
|
||||
cert.setSubject(subject.getDN());
|
||||
cert.setSerialNumber(x509Cert.getSerialNumber().toString());
|
||||
cert.setIssuerDn(x509Cert.getIssuerX500Principal().getName());
|
||||
cert.setNotBefore(x509Cert.getNotBefore());
|
||||
|
@ -159,7 +159,36 @@ public class KeyInfoServiceImpl implements KeyInfoService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long save(KeyInfoDTO.KeySave save) {
|
||||
public KeyInfoDTO.KeyView selectById(Long id) {
|
||||
KeyInfo keyInfo = keyInfoMapper.selectById(id);
|
||||
Assert.notNull(keyInfo, "密钥ID不存在");
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
KeyInfoDTO.KeyView view = new KeyInfoDTO.KeyView();
|
||||
BeanUtils.copyProperties(keyInfo, view);
|
||||
Optional.of(keyInfo.getKeyType()).map(KeyCategory::of).map(KeyCategory::getDesc).ifPresent(view::setKeyTypeText);
|
||||
Map<String, String> usageMap = KeyUsage.getUsage(keyInfo.getKeyUsage())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(KeyUsage::getCode, KeyUsage::getDesc));
|
||||
view.setKeyUsages(new ArrayList<>(usageMap.keySet()));
|
||||
view.setKeyUsageText(String.join(",", usageMap.values()));
|
||||
KeyStatus keyStatus = KeyStatus.of(keyInfo.getStatus());
|
||||
if (KeyStatus.ENABLED == keyStatus) {
|
||||
if (now.isBefore(keyInfo.getEffectiveTime())) {
|
||||
view.setStatus(KeyStatus.WAIT_ENABLED.getCode());
|
||||
view.setStatusText(KeyStatus.WAIT_ENABLED.getDesc());
|
||||
} else if (now.isAfter(keyInfo.getExpiredTime())) {
|
||||
view.setStatus(KeyStatus.EXPIRED.getCode());
|
||||
view.setStatusText(KeyStatus.EXPIRED.getDesc());
|
||||
}
|
||||
}
|
||||
if (ObjectUtils.isEmpty(view.getStatusText())) {
|
||||
view.setStatusText(keyStatus.getDesc());
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> save(KeyInfoDTO.KeySave save) {
|
||||
|
||||
KeyTemplate keyTemplate = keyTemplateMapper.selectOne(
|
||||
new LambdaQueryWrapper<KeyTemplate>()
|
||||
@ -171,7 +200,7 @@ public class KeyInfoServiceImpl implements KeyInfoService {
|
||||
Assert.isTrue(EnableStatus.ENABLED.getCode().equals(app.getStatus()), "应用不是启用状态");
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
List<Long> ids = new ArrayList<>();
|
||||
for (int i = 0; i < save.getGenNumber(); i++) {
|
||||
|
||||
// 密钥信息
|
||||
@ -200,9 +229,11 @@ public class KeyInfoServiceImpl implements KeyInfoService {
|
||||
info.setCheckValue(record.getCheckValue());
|
||||
keyInfoMapper.insert(info);
|
||||
spKeyRecordMapper.insert(record);
|
||||
|
||||
ids.add(info.getId());
|
||||
}
|
||||
|
||||
return 0L;
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -218,7 +249,7 @@ public class KeyInfoServiceImpl implements KeyInfoService {
|
||||
|
||||
List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
|
||||
if (CollectionUtils.isEmpty(keyInfos)) {
|
||||
log.warn("enableKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
log.warn("updateKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
return;
|
||||
}
|
||||
List<String> unNormalCodes = keyInfos.stream()
|
||||
|
@ -0,0 +1,74 @@
|
||||
package com.sunyard.chsm.utils;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1Integer;
|
||||
import org.bouncycastle.asn1.ASN1Sequence;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import static com.sunyard.chsm.utils.SydCmsUtil.readContentInfo;
|
||||
|
||||
|
||||
/**
|
||||
* @author zyh
|
||||
* @since 2024/12/19
|
||||
*/
|
||||
public class CertUtil {
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static X509Certificate convertToX509Cert(String certificateString) {
|
||||
String pemCert;
|
||||
if(certificateString.contains("-----BEGIN CERTIFICATE-----")){
|
||||
pemCert =certificateString;
|
||||
}else {
|
||||
String cert_begin = "-----BEGIN CERTIFICATE-----\n";
|
||||
String end_cert = "\n-----END CERTIFICATE-----";
|
||||
pemCert = cert_begin + certificateString + end_cert;
|
||||
}
|
||||
try {
|
||||
CertificateFactory CF = CertificateFactory.getInstance("X.509", "BC"); // 从证书工厂中获取X.509的单例类
|
||||
return (X509Certificate) CF.generateCertificate(new ByteArrayInputStream(pemCert.getBytes())); // 将文件流的证书转化为证书类
|
||||
} catch (CertificateException | NoSuchProviderException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String getSn(String cert) {
|
||||
try {
|
||||
|
||||
if(cert.contains("-----BEGIN CERTIFICATE-----")){
|
||||
String[] lines = cert.split("\n");
|
||||
// 构建新的证书字符串,去掉第一行和最后一行
|
||||
StringBuilder cleanedCertificate = new StringBuilder();
|
||||
for (int i = 1; i < lines.length - 1; i++) {
|
||||
cleanedCertificate.append(lines[i]);
|
||||
}
|
||||
cert = cleanedCertificate.toString();
|
||||
}
|
||||
//从证书中获取sn
|
||||
Object pkContent = readContentInfo(CodecUtils.decodeBase64(cert));
|
||||
|
||||
//逐级从结构体中获取需要的sn
|
||||
ASN1Sequence pkSeq = ASN1Sequence.getInstance(pkContent);
|
||||
ASN1Sequence pkSeq0 = (ASN1Sequence) pkSeq.getObjectAt(0).toASN1Primitive();
|
||||
ASN1Integer sn = (ASN1Integer) pkSeq0.getObjectAt(1).toASN1Primitive();
|
||||
String serNum = CodecUtils.encodeHex(sn.getEncoded());
|
||||
|
||||
//去掉长度
|
||||
return serNum.substring(4);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.sunyard.chsm.utils;
|
||||
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/6
|
||||
*/
|
||||
public abstract class CodecUtils {
|
||||
|
||||
public static String encodeBase64(byte[] data) {
|
||||
return Base64.getEncoder().encodeToString(data);
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Base64.getDecoder().decode(str);
|
||||
} catch (Exception var3) {
|
||||
if (str.length() > 32) {
|
||||
str = str.substring(0, 32) + "...";
|
||||
}
|
||||
|
||||
String format = String.format("参数[%s]不是正确的base64格式", str);
|
||||
throw new IllegalArgumentException(format);
|
||||
}
|
||||
}
|
||||
|
||||
public static String encodeHex(byte[] data) {
|
||||
if (data == null) {
|
||||
return "";
|
||||
}
|
||||
return Hex.toHexString(data);
|
||||
}
|
||||
|
||||
public static byte[] decodeHex(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Hex.decode(str);
|
||||
} catch (Exception var3) {
|
||||
if (str.length() > 32) {
|
||||
str = str.substring(0, 32) + "...";
|
||||
}
|
||||
String format = String.format("参数[%s]不是正确的hex格式", str);
|
||||
throw new IllegalArgumentException(format);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package com.sunyard.chsm.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -18,4 +20,10 @@ public abstract class DateFormat {
|
||||
public static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern(YYYY_MM_DD);
|
||||
public static final DateTimeFormatter DATE_TIME = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
|
||||
|
||||
|
||||
public static String formDate(Date date) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
package com.sunyard.chsm.utils;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public class IpUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 获取真实ip地址,避免获取代理ip
|
||||
*/
|
||||
public static String getIpAddress(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return "unknown";
|
||||
}
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
// 多次反向代理后会有多个IP值,第一个为真实IP。
|
||||
int index = ip.indexOf(',');
|
||||
if (index != -1) {
|
||||
ip = ip.substring(0, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
if("0:0:0:0:0:0:0:1".equals(ip)){
|
||||
return "127.0.0.1";
|
||||
}else {
|
||||
if(ip.equals("127.0.0.1") || ip.equalsIgnoreCase("localhost") && ObjectUtils.isEmpty(request.getRemoteAddr())){
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package com.sunyard.chsm.utils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.bouncycastle.asn1.*;
|
||||
import org.bouncycastle.asn1.cms.EncryptedContentInfo;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* SignedAndEnvelopedData ::=SEQUENCE {
|
||||
* version Version,
|
||||
* recipientInfos RecipientInfos,
|
||||
* digestAlgorithms DigestAlgorithmIdentifiers
|
||||
* encryptedContentInfo EncryptedContentInfo,
|
||||
* certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
|
||||
* crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
|
||||
* signerInfos SignerInfos
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author liulu
|
||||
* @since 2024/12/25
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class SignedAndEnvelopedData extends ASN1Object {
|
||||
|
||||
private ASN1Integer version;
|
||||
private ASN1Set recipientInfos;
|
||||
private ASN1Set digestAlgorithms;
|
||||
private EncryptedContentInfo encryptedContentInfo;
|
||||
private ASN1Set certificates;
|
||||
private ASN1Set crls;
|
||||
private ASN1Set signerInfos;
|
||||
|
||||
|
||||
public static SignedAndEnvelopedData getInstance(
|
||||
Object obj) {
|
||||
if (obj instanceof SignedAndEnvelopedData) {
|
||||
return (SignedAndEnvelopedData) obj;
|
||||
}
|
||||
|
||||
if (obj != null) {
|
||||
return new SignedAndEnvelopedData(ASN1Sequence.getInstance(obj));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SignedAndEnvelopedData(ASN1Sequence seq) {
|
||||
Enumeration e = seq.getObjects();
|
||||
|
||||
version = ASN1Integer.getInstance(e.nextElement());
|
||||
recipientInfos = ((ASN1Set) e.nextElement());
|
||||
digestAlgorithms = ((ASN1Set) e.nextElement());
|
||||
encryptedContentInfo = EncryptedContentInfo.getInstance(e.nextElement());
|
||||
|
||||
ASN1Set sigInfs = null;
|
||||
while (e.hasMoreElements()) {
|
||||
ASN1Primitive o = (ASN1Primitive) e.nextElement();
|
||||
if (o instanceof ASN1TaggedObject) {
|
||||
ASN1TaggedObject tagged = (ASN1TaggedObject) o;
|
||||
switch (tagged.getTagNo()) {
|
||||
case 0:
|
||||
certificates = ASN1Set.getInstance(tagged, false);
|
||||
break;
|
||||
case 1:
|
||||
crls = ASN1Set.getInstance(tagged, false);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
|
||||
}
|
||||
} else {
|
||||
if (!(o instanceof ASN1Set)) {
|
||||
throw new IllegalArgumentException("SET expected, not encountered");
|
||||
}
|
||||
sigInfs = (ASN1Set) o;
|
||||
}
|
||||
}
|
||||
|
||||
if (sigInfs == null) {
|
||||
throw new IllegalArgumentException("signerInfos not set");
|
||||
}
|
||||
signerInfos = sigInfs;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ASN1Primitive toASN1Primitive() {
|
||||
ASN1EncodableVector v = new ASN1EncodableVector(7);
|
||||
v.add(version);
|
||||
v.add(recipientInfos);
|
||||
v.add(digestAlgorithms);
|
||||
v.add(encryptedContentInfo);
|
||||
if (certificates != null) {
|
||||
v.add(new DERTaggedObject(false, 0, certificates));
|
||||
}
|
||||
if (crls != null) {
|
||||
v.add(new DERTaggedObject(false, 1, crls));
|
||||
}
|
||||
v.add(signerInfos);
|
||||
return new DLSequence(v);
|
||||
}
|
||||
}
|
140
chsm-common/src/main/java/com/sunyard/chsm/utils/SydCmsUtil.java
Normal file
140
chsm-common/src/main/java/com/sunyard/chsm/utils/SydCmsUtil.java
Normal file
@ -0,0 +1,140 @@
|
||||
package com.sunyard.chsm.utils;
|
||||
|
||||
import org.bouncycastle.asn1.*;
|
||||
import org.bouncycastle.asn1.cms.ContentInfo;
|
||||
import org.bouncycastle.asn1.cms.EncryptedContentInfo;
|
||||
import org.bouncycastle.asn1.cms.OriginatorInfo;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.cms.CMSException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class SydCmsUtil {
|
||||
|
||||
public static Object readContentInfo(
|
||||
byte[] input) {
|
||||
ASN1InputStream is = new ASN1InputStream(input);
|
||||
Object v = null;
|
||||
try {
|
||||
v = is.readObject();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, Object> getEncryptedContentInfo(ASN1Encodable obj){
|
||||
Map<String, Object>map = new HashMap<String, Object>();
|
||||
ASN1Sequence seq = ASN1Sequence.getInstance(obj );
|
||||
if (seq.size() < 2)
|
||||
{
|
||||
throw new IllegalArgumentException("Truncated Sequence Found");
|
||||
}
|
||||
|
||||
ASN1ObjectIdentifier contentType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
|
||||
map.put( "contentType", contentType );
|
||||
|
||||
AlgorithmIdentifier contentEncryptionAlgorithm = AlgorithmIdentifier.getInstance(
|
||||
seq.getObjectAt(1));
|
||||
|
||||
map.put( "contentEncryptionAlgorithm", contentEncryptionAlgorithm );
|
||||
|
||||
if (seq.size() > 2)
|
||||
{
|
||||
ASN1OctetString encryptedContent = ASN1OctetString.getInstance(
|
||||
(ASN1TaggedObject)seq.getObjectAt(2), false);
|
||||
map.put( "encryptedContent", encryptedContent );
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static Map<String, Object> getOriginatorInfo(ASN1TaggedObject obj,
|
||||
boolean explicit){
|
||||
Map<String, Object>map = new HashMap<String, Object>();
|
||||
ASN1Sequence seq = ASN1Sequence.getInstance(obj, explicit);
|
||||
switch (seq.size())
|
||||
{
|
||||
case 0: // empty
|
||||
break;
|
||||
case 1:
|
||||
ASN1TaggedObject o = (ASN1TaggedObject)seq.getObjectAt(0);
|
||||
switch (o.getTagNo())
|
||||
{
|
||||
case 0 :
|
||||
ASN1Set certs = ASN1Set.getInstance(o, false);
|
||||
map.put("certs", certs);
|
||||
break;
|
||||
case 1 :
|
||||
ASN1Set crls = ASN1Set.getInstance(o, false);
|
||||
map.put("crls", crls);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Bad tag in OriginatorInfo: " + o.getTagNo());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
ASN1Set certs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(0), false);
|
||||
map.put("certs", certs);
|
||||
ASN1Set crls = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(1), false);
|
||||
map.put("crls", crls);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("OriginatorInfo too big");
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// public static ContentInfo getContentInfo( byte[] input ) {
|
||||
// Object content = readContentInfo( input );
|
||||
// ASN1Sequence seq = ASN1Sequence.getInstance( content );
|
||||
// return new ContentInfo(seq);
|
||||
//
|
||||
// }
|
||||
|
||||
public static Map<String, Object> readCmsInfo( byte[] input ) throws CMSException {
|
||||
Map<String, Object>map = new HashMap<String, Object>();
|
||||
|
||||
Object content = readContentInfo( input );
|
||||
ASN1Sequence seq = ASN1Sequence.getInstance( content );
|
||||
ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) seq.getObjectAt(0);
|
||||
map.put("oid", oid);
|
||||
|
||||
seq = ASN1Sequence.getInstance((ASN1TaggedObject) seq.getObjectAt(1), true);
|
||||
|
||||
int index = 0;
|
||||
|
||||
ASN1Integer version = (ASN1Integer)seq.getObjectAt(index++);
|
||||
map.put( "version", version );
|
||||
|
||||
Object tmp = seq.getObjectAt(index++);
|
||||
|
||||
if (tmp instanceof ASN1TaggedObject)
|
||||
{
|
||||
OriginatorInfo originatorInfo = OriginatorInfo.getInstance((ASN1TaggedObject)tmp, false);
|
||||
// Map<String, Object>originatorInfo = getOriginatorInfo((ASN1TaggedObject)tmp, false);
|
||||
map.put( "originatorInfo", originatorInfo );
|
||||
tmp = seq.getObjectAt(index++);
|
||||
}
|
||||
|
||||
ASN1Set recipientInfos = ASN1Set.getInstance(tmp);
|
||||
map.put( "recipientInfos", recipientInfos );
|
||||
|
||||
EncryptedContentInfo encryptedContentInfo = EncryptedContentInfo.getInstance( seq.getObjectAt(index++) );
|
||||
// Map<String, Object> encryptedContentInfo = getEncryptedContentInfo(seq.getObjectAt(index++));
|
||||
map.put( "encryptedContentInfo", encryptedContentInfo );
|
||||
|
||||
if(seq.size() > index)
|
||||
{
|
||||
ASN1Set unprotectedAttrs = ASN1Set.getInstance((ASN1TaggedObject)seq.getObjectAt(index), false);
|
||||
map.put( "unprotectedAttrs", unprotectedAttrs );
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
@ -61,7 +61,7 @@ import java.util.Arrays;
|
||||
/**
|
||||
* 这个工具类的方法,也适用于其他基于BC库的ECC算法
|
||||
*/
|
||||
public class BCECUtils {
|
||||
public class BCECUtils extends GMBaseUtil {
|
||||
private static final String ALGO_NAME_EC = "EC";
|
||||
private static final String PEM_STRING_PUBLIC = "PUBLIC KEY";
|
||||
private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY";
|
||||
|
@ -0,0 +1,294 @@
|
||||
package com.sunyard.chsm.utils.gm;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1EncodableVector;
|
||||
import org.bouncycastle.asn1.ASN1Encoding;
|
||||
import org.bouncycastle.asn1.ASN1Integer;
|
||||
import org.bouncycastle.asn1.ASN1Primitive;
|
||||
import org.bouncycastle.asn1.ASN1Sequence;
|
||||
import org.bouncycastle.asn1.DERSequence;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.CryptoException;
|
||||
import org.bouncycastle.crypto.CryptoServicesRegistrar;
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithID;
|
||||
import org.bouncycastle.crypto.params.ParametersWithRandom;
|
||||
import org.bouncycastle.crypto.signers.DSAKCalculator;
|
||||
import org.bouncycastle.crypto.signers.RandomDSAKCalculator;
|
||||
import org.bouncycastle.math.ec.ECAlgorithms;
|
||||
import org.bouncycastle.math.ec.ECConstants;
|
||||
import org.bouncycastle.math.ec.ECFieldElement;
|
||||
import org.bouncycastle.math.ec.ECMultiplier;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 有的国密需求是用户可以自己做预处理,签名验签只是对预处理的结果进行签名和验签
|
||||
*/
|
||||
public class SM2PreprocessSigner implements ECConstants {
|
||||
private static final int DIGEST_LENGTH = 32; // bytes
|
||||
|
||||
private final DSAKCalculator kCalculator = new RandomDSAKCalculator();
|
||||
private Digest digest = null;
|
||||
|
||||
private ECDomainParameters ecParams;
|
||||
private ECPoint pubPoint;
|
||||
private ECKeyParameters ecKey;
|
||||
private byte[] userID;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param forSigning true表示用于签名,false表示用于验签
|
||||
* @param param
|
||||
*/
|
||||
public void init(boolean forSigning, CipherParameters param) {
|
||||
init(forSigning, new SM3Digest(), param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param forSigning true表示用于签名,false表示用于验签
|
||||
* @param digest SM2算法的话,一般是采用SM3摘要算法
|
||||
* @param param
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public void init(boolean forSigning, Digest digest, CipherParameters param) throws RuntimeException {
|
||||
CipherParameters baseParam;
|
||||
|
||||
if (digest.getDigestSize() != DIGEST_LENGTH) {
|
||||
throw new RuntimeException("Digest size must be " + DIGEST_LENGTH);
|
||||
}
|
||||
this.digest = digest;
|
||||
|
||||
if (param instanceof ParametersWithID) {
|
||||
baseParam = ((ParametersWithID) param).getParameters();
|
||||
userID = ((ParametersWithID) param).getID();
|
||||
} else {
|
||||
baseParam = param;
|
||||
userID = Hex.decode("31323334353637383132333435363738"); // the default value
|
||||
}
|
||||
|
||||
if (forSigning) {
|
||||
if (baseParam instanceof ParametersWithRandom) {
|
||||
ParametersWithRandom rParam = (ParametersWithRandom) baseParam;
|
||||
|
||||
ecKey = (ECKeyParameters) rParam.getParameters();
|
||||
ecParams = ecKey.getParameters();
|
||||
kCalculator.init(ecParams.getN(), rParam.getRandom());
|
||||
} else {
|
||||
ecKey = (ECKeyParameters) baseParam;
|
||||
ecParams = ecKey.getParameters();
|
||||
kCalculator.init(ecParams.getN(), CryptoServicesRegistrar.getSecureRandom());
|
||||
}
|
||||
pubPoint = createBasePointMultiplier().multiply(ecParams.getG(), ((ECPrivateKeyParameters) ecKey).getD()).normalize();
|
||||
} else {
|
||||
ecKey = (ECKeyParameters) baseParam;
|
||||
ecParams = ecKey.getParameters();
|
||||
pubPoint = ((ECPublicKeyParameters) ecKey).getQ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预处理,辅助方法
|
||||
* ZA=H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥yG ∥ xA ∥ yA)。
|
||||
* M=ZA ∥ M;
|
||||
* e = Hv(M)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public byte[] preprocess(byte[] m) {
|
||||
return preprocess(m, 0, m.length);
|
||||
}
|
||||
|
||||
|
||||
public byte[] preprocess(byte[] m, int off, int len) {
|
||||
byte[] z = getZ(userID);
|
||||
digest.update(z, 0, z.length);
|
||||
digest.update(m, off, len);
|
||||
byte[] eHash = new byte[DIGEST_LENGTH];
|
||||
digest.doFinal(eHash, 0);
|
||||
return eHash;
|
||||
}
|
||||
|
||||
public boolean verifySignature(byte[] eHash, byte[] signature) {
|
||||
try {
|
||||
BigInteger[] rs = derDecode(signature);
|
||||
if (rs != null) {
|
||||
return verifySignature(eHash, rs[0], rs[1]);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
digest.reset();
|
||||
}
|
||||
|
||||
public byte[] generateSignature(byte[] eHash) throws CryptoException {
|
||||
BigInteger n = ecParams.getN();
|
||||
BigInteger e = calculateE(eHash);
|
||||
BigInteger d = ((ECPrivateKeyParameters) ecKey).getD();
|
||||
|
||||
BigInteger r, s;
|
||||
|
||||
ECMultiplier basePointMultiplier = createBasePointMultiplier();
|
||||
|
||||
// 5.2.1 Draft RFC: SM2 Public Key Algorithms
|
||||
do // generate s
|
||||
{
|
||||
BigInteger k;
|
||||
do // generate r
|
||||
{
|
||||
// A3
|
||||
k = kCalculator.nextK();
|
||||
|
||||
// A4
|
||||
ECPoint p = basePointMultiplier.multiply(ecParams.getG(), k).normalize();
|
||||
|
||||
// A5
|
||||
r = e.add(p.getAffineXCoord().toBigInteger()).mod(n);
|
||||
}
|
||||
while (r.equals(ZERO) || r.add(k).equals(n));
|
||||
|
||||
// A6
|
||||
BigInteger dPlus1ModN = d.add(ONE).modInverse(n);
|
||||
|
||||
s = k.subtract(r.multiply(d)).mod(n);
|
||||
s = dPlus1ModN.multiply(s).mod(n);
|
||||
}
|
||||
while (s.equals(ZERO));
|
||||
|
||||
// A7
|
||||
try {
|
||||
return derEncode(r, s);
|
||||
} catch (IOException ex) {
|
||||
throw new CryptoException("unable to encode signature: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifySignature(byte[] eHash, BigInteger r, BigInteger s) {
|
||||
BigInteger n = ecParams.getN();
|
||||
|
||||
// 5.3.1 Draft RFC: SM2 Public Key Algorithms
|
||||
// B1
|
||||
if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// B2
|
||||
if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// B3 eHash
|
||||
|
||||
// B4
|
||||
BigInteger e = calculateE(eHash);
|
||||
|
||||
// B5
|
||||
BigInteger t = r.add(s).mod(n);
|
||||
if (t.equals(ZERO)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// B6
|
||||
ECPoint q = ((ECPublicKeyParameters) ecKey).getQ();
|
||||
ECPoint x1y1 = ECAlgorithms.sumOfTwoMultiplies(ecParams.getG(), s, q, t).normalize();
|
||||
if (x1y1.isInfinity()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// B7
|
||||
BigInteger expectedR = e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n);
|
||||
|
||||
return expectedR.equals(r);
|
||||
}
|
||||
|
||||
private byte[] digestDoFinal() {
|
||||
byte[] result = new byte[digest.getDigestSize()];
|
||||
digest.doFinal(result, 0);
|
||||
|
||||
reset();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] getZ(byte[] userID) {
|
||||
digest.reset();
|
||||
|
||||
addUserID(digest, userID);
|
||||
|
||||
addFieldElement(digest, ecParams.getCurve().getA());
|
||||
addFieldElement(digest, ecParams.getCurve().getB());
|
||||
addFieldElement(digest, ecParams.getG().getAffineXCoord());
|
||||
addFieldElement(digest, ecParams.getG().getAffineYCoord());
|
||||
addFieldElement(digest, pubPoint.getAffineXCoord());
|
||||
addFieldElement(digest, pubPoint.getAffineYCoord());
|
||||
|
||||
byte[] result = new byte[digest.getDigestSize()];
|
||||
|
||||
digest.doFinal(result, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addUserID(Digest digest, byte[] userID) {
|
||||
int len = userID.length * 8;
|
||||
digest.update((byte) (len >> 8 & 0xFF));
|
||||
digest.update((byte) (len & 0xFF));
|
||||
digest.update(userID, 0, userID.length);
|
||||
}
|
||||
|
||||
private void addFieldElement(Digest digest, ECFieldElement v) {
|
||||
byte[] p = v.getEncoded();
|
||||
digest.update(p, 0, p.length);
|
||||
}
|
||||
|
||||
protected ECMultiplier createBasePointMultiplier() {
|
||||
return new FixedPointCombMultiplier();
|
||||
}
|
||||
|
||||
protected BigInteger calculateE(byte[] message) {
|
||||
return new BigInteger(1, message);
|
||||
}
|
||||
|
||||
protected BigInteger[] derDecode(byte[] encoding)
|
||||
throws IOException {
|
||||
ASN1Sequence seq = ASN1Sequence.getInstance(ASN1Primitive.fromByteArray(encoding));
|
||||
if (seq.size() != 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BigInteger r = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue();
|
||||
BigInteger s = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
|
||||
|
||||
byte[] expectedEncoding = derEncode(r, s);
|
||||
if (!Arrays.constantTimeAreEqual(expectedEncoding, encoding)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BigInteger[]{r, s};
|
||||
}
|
||||
|
||||
protected byte[] derEncode(BigInteger r, BigInteger s)
|
||||
throws IOException {
|
||||
|
||||
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||
v.add(new ASN1Integer(r));
|
||||
v.add(new ASN1Integer(s));
|
||||
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
|
||||
}
|
||||
}
|
@ -67,7 +67,8 @@ public class CommonCertUtils extends GMBaseUtil {
|
||||
}
|
||||
|
||||
public static PKCS10CertificationRequest createCSR(X500Name subject, BCECPublicKey pubKey, PrivateKey priKey) throws OperatorCreationException {
|
||||
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, pubKey);
|
||||
SM2PublicKey sm2SubPub = new SM2PublicKey(pubKey.getAlgorithm(), pubKey);
|
||||
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, sm2SubPub);
|
||||
ContentSigner signerBuilder = new JcaContentSignerBuilder("SM3withSM2")
|
||||
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build(priKey);
|
||||
return csrBuilder.build(signerBuilder);
|
||||
|
@ -0,0 +1,44 @@
|
||||
package com.sunyard.chsm.utils.gm.cert;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.ASN1OctetString;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.asn1.x9.X9ECPoint;
|
||||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
|
||||
|
||||
public class SM2PublicKey extends BCECPublicKey {
|
||||
public static final ASN1ObjectIdentifier ID_SM2_PUBKEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301");
|
||||
|
||||
private boolean withCompression;
|
||||
|
||||
public SM2PublicKey(BCECPublicKey key) {
|
||||
super(key.getAlgorithm(), key);
|
||||
this.withCompression = false;
|
||||
}
|
||||
|
||||
public SM2PublicKey(String algorithm, BCECPublicKey key) {
|
||||
super(algorithm, key);
|
||||
this.withCompression = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
ASN1OctetString p = ASN1OctetString.getInstance(
|
||||
new X9ECPoint(getQ(), withCompression).toASN1Primitive());
|
||||
|
||||
// stored curve is null if ImplicitlyCa
|
||||
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
|
||||
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ID_SM2_PUBKEY_PARAM),
|
||||
p.getOctets());
|
||||
|
||||
return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPointFormat(String style) {
|
||||
withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
|
||||
}
|
||||
}
|
@ -0,0 +1,283 @@
|
||||
package com.sunyard.chsm.utils.gm.cert;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.DERIA5String;
|
||||
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
|
||||
import org.bouncycastle.asn1.x500.RDN;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
import org.bouncycastle.asn1.x500.style.IETFUtils;
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints;
|
||||
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||
import org.bouncycastle.asn1.x509.KeyPurposeId;
|
||||
import org.bouncycastle.asn1.x509.KeyUsage;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class SM2X509CertMaker {
|
||||
|
||||
private static enum CertLevel {
|
||||
RootCA,
|
||||
SubCA,
|
||||
EndEntity
|
||||
} // class CertLevel
|
||||
|
||||
public static final String SIGN_ALGO_SM3WITHSM2 = "SM3withSM2";
|
||||
|
||||
private long certExpire;
|
||||
private X500Name issuerDN;
|
||||
// private CertSNAllocator snAllocator;
|
||||
private KeyPair issuerKeyPair;
|
||||
|
||||
/**
|
||||
* @param issuerKeyPair 证书颁发者的密钥对。
|
||||
* 其实一般的CA的私钥都是要严格保护的。
|
||||
* 一般CA的私钥都会放在加密卡/加密机里,证书的签名由加密卡/加密机完成。
|
||||
* 这里仅是为了演示BC库签发证书的用法,所以暂时不作太多要求。
|
||||
* @param certExpire 证书有效时间,单位毫秒
|
||||
* @param issuer 证书颁发者信息
|
||||
* @param snAllocator 维护/分配证书序列号的实例,证书序列号应该递增且不重复
|
||||
*/
|
||||
public SM2X509CertMaker(KeyPair issuerKeyPair, long certExpire, X500Name issuer)
|
||||
// , CertSNAllocator snAllocator)
|
||||
{
|
||||
this.issuerKeyPair = issuerKeyPair;
|
||||
this.certExpire = certExpire;
|
||||
this.issuerDN = issuer;
|
||||
// this.snAllocator = snAllocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成根CA证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeRootCACert(byte[] csr)
|
||||
throws Exception {
|
||||
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
|
||||
return makeCertificate(CertLevel.RootCA, null, csr, usage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成SubCA证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeSubCACert(byte[] csr)
|
||||
throws Exception {
|
||||
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
|
||||
return makeCertificate(CertLevel.SubCA, 0, csr, usage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成SSL用户证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeSSLEndEntityCert(byte[] csr)
|
||||
throws Exception {
|
||||
return makeEndEntityCert(csr,
|
||||
new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth});
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成用户证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @param extendedKeyUsages 扩展指数用途。
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeEndEntityCert(byte[] csr,
|
||||
KeyPurposeId[] extendedKeyUsages)
|
||||
throws Exception {
|
||||
KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyAgreement
|
||||
| KeyUsage.dataEncipherment | KeyUsage.keyEncipherment);
|
||||
return makeCertificate(CertLevel.EndEntity, null, csr, usage, extendedKeyUsages);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isCA 是否是颁发给CA的证书
|
||||
* @param keyUsage 证书用途
|
||||
* @param csr CSR
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private X509Certificate makeCertificate(CertLevel certLevel, Integer pathLenConstrain,
|
||||
byte[] csr, KeyUsage keyUsage, KeyPurposeId[] extendedKeyUsages)
|
||||
throws Exception {
|
||||
if (certLevel == CertLevel.EndEntity) {
|
||||
if (keyUsage.hasUsages(KeyUsage.keyCertSign)) {
|
||||
throw new IllegalArgumentException(
|
||||
"keyusage keyCertSign is not allowed in EndEntity Certificate");
|
||||
}
|
||||
}
|
||||
|
||||
PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr);
|
||||
SubjectPublicKeyInfo subPub = request.getSubjectPublicKeyInfo();
|
||||
|
||||
PrivateKey issPriv = issuerKeyPair.getPrivate();
|
||||
PublicKey issPub = issuerKeyPair.getPublic();
|
||||
|
||||
X500Name subject = request.getSubject();
|
||||
String email = null;
|
||||
String commonName = null;
|
||||
/*
|
||||
* RFC 5280 §4.2.1.6 Subject
|
||||
* Conforming implementations generating new certificates with
|
||||
* electronic mail addresses MUST use the rfc822Name in the subject
|
||||
* alternative name extension (Section 4.2.1.6) to describe such
|
||||
* identities. Simultaneous inclusion of the emailAddress attribute in
|
||||
* the subject distinguished name to support legacy implementations is
|
||||
* deprecated but permitted.
|
||||
*/
|
||||
RDN[] rdns = subject.getRDNs();
|
||||
List<RDN> newRdns = new ArrayList<>(rdns.length);
|
||||
for (int i = 0; i < rdns.length; i++) {
|
||||
RDN rdn = rdns[i];
|
||||
|
||||
AttributeTypeAndValue atv = rdn.getFirst();
|
||||
ASN1ObjectIdentifier type = atv.getType();
|
||||
if (BCStyle.EmailAddress.equals(type)) {
|
||||
email = IETFUtils.valueToString(atv.getValue());
|
||||
} else {
|
||||
if (BCStyle.CN.equals(type)) {
|
||||
commonName = IETFUtils.valueToString(atv.getValue());
|
||||
}
|
||||
newRdns.add(rdn);
|
||||
}
|
||||
}
|
||||
|
||||
List<GeneralName> subjectAltNames = new LinkedList<>();
|
||||
if (email != null) {
|
||||
subject = new X500Name(newRdns.toArray(new RDN[0]));
|
||||
subjectAltNames.add(
|
||||
new GeneralName(GeneralName.rfc822Name,
|
||||
new DERIA5String(email, true)));
|
||||
}
|
||||
|
||||
boolean selfSignedEECert = false;
|
||||
switch (certLevel) {
|
||||
case RootCA:
|
||||
if (issuerDN.equals(subject)) {
|
||||
subject = issuerDN;
|
||||
} else {
|
||||
throw new IllegalArgumentException("subject != issuer for certLevel " + CertLevel.RootCA);
|
||||
}
|
||||
break;
|
||||
case SubCA:
|
||||
if (issuerDN.equals(subject)) {
|
||||
throw new IllegalArgumentException(
|
||||
"subject MUST not equals issuer for certLevel " + certLevel);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (issuerDN.equals(subject)) {
|
||||
selfSignedEECert = true;
|
||||
subject = issuerDN;
|
||||
}
|
||||
}
|
||||
|
||||
// BigInteger serialNumber = snAllocator.nextSerialNumber();
|
||||
BigInteger serialNumber = new BigInteger(IdWorker.getIdStr());
|
||||
Date notBefore = new Date();
|
||||
Date notAfter = new Date(notBefore.getTime() + certExpire);
|
||||
X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
|
||||
issuerDN, serialNumber,
|
||||
notBefore, notAfter,
|
||||
subject, subPub);
|
||||
|
||||
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
||||
v3CertGen.addExtension(Extension.subjectKeyIdentifier, false,
|
||||
extUtils.createSubjectKeyIdentifier(subPub));
|
||||
if (certLevel != CertLevel.RootCA && !selfSignedEECert) {
|
||||
v3CertGen.addExtension(Extension.authorityKeyIdentifier, false,
|
||||
extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(issPub.getEncoded())));
|
||||
}
|
||||
|
||||
// RFC 5280 §4.2.1.9 Basic Constraints:
|
||||
// Conforming CAs MUST include this extension in all CA certificates
|
||||
// that contain public keys used to validate digital signatures on
|
||||
// certificates and MUST mark the extension as critical in such
|
||||
// certificates.
|
||||
BasicConstraints basicConstraints;
|
||||
if (certLevel == CertLevel.EndEntity) {
|
||||
basicConstraints = new BasicConstraints(false);
|
||||
} else {
|
||||
basicConstraints = pathLenConstrain == null
|
||||
? new BasicConstraints(true) : new BasicConstraints(pathLenConstrain.intValue());
|
||||
}
|
||||
v3CertGen.addExtension(Extension.basicConstraints, true, basicConstraints);
|
||||
|
||||
// RFC 5280 §4.2.1.3 Key Usage: When present, conforming CAs SHOULD mark this extension as critical.
|
||||
v3CertGen.addExtension(Extension.keyUsage, true, keyUsage);
|
||||
|
||||
if (extendedKeyUsages != null) {
|
||||
ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages);
|
||||
v3CertGen.addExtension(Extension.extendedKeyUsage, false, xku);
|
||||
|
||||
boolean forSSLServer = false;
|
||||
for (KeyPurposeId purposeId : extendedKeyUsages) {
|
||||
if (KeyPurposeId.id_kp_serverAuth.equals(purposeId)) {
|
||||
forSSLServer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (forSSLServer) {
|
||||
if (commonName == null) {
|
||||
throw new IllegalArgumentException("commonName must not be null");
|
||||
}
|
||||
GeneralName name = new GeneralName(GeneralName.dNSName,
|
||||
new DERIA5String(commonName, true));
|
||||
subjectAltNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!subjectAltNames.isEmpty()) {
|
||||
v3CertGen.addExtension(Extension.subjectAlternativeName, false,
|
||||
new GeneralNames(subjectAltNames.toArray(new GeneralName[0])));
|
||||
}
|
||||
|
||||
JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
|
||||
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME)
|
||||
.getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv)));
|
||||
cert.verify(issPub);
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
private JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) throws Exception {
|
||||
if (issPub.getAlgorithm().equals("EC")) {
|
||||
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(SIGN_ALGO_SM3WITHSM2);
|
||||
contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
|
||||
return contentSignerBuilder;
|
||||
}
|
||||
throw new Exception("Unsupported PublicKey Algorithm:" + issPub.getAlgorithm());
|
||||
}
|
||||
}
|
@ -16,5 +16,13 @@
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@ -1,36 +1,36 @@
|
||||
package com.sunyard.chsm.enums;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* 算法的轮模式
|
||||
* @author Cheney
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AlgMode {
|
||||
ECB("ECB", "ECB"),
|
||||
CBC( "CBC", "CBC"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
|
||||
public static AlgMode of(String code) {
|
||||
if (code == null || code.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.stream(AlgMode.values())
|
||||
.filter(it -> Objects.equals(it.getCode(), code))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
package com.sunyard.chsm.enums;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* 算法的轮模式
|
||||
* @author Cheney
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AlgMode {
|
||||
ECB("ECB", "ECB"),
|
||||
CBC( "CBC", "CBC"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
|
||||
public static AlgMode of(String code) {
|
||||
if (code == null || code.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.stream(AlgMode.values())
|
||||
.filter(it -> Objects.equals(it.getCode(), code))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.sunyard.chsm.enums;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/10/22
|
||||
*/
|
||||
public enum HashAlg {
|
||||
SM3,
|
||||
;
|
||||
}
|
@ -1,34 +1,36 @@
|
||||
package com.sunyard.chsm.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据的填充模式
|
||||
* @author Cheney
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Padding {
|
||||
NOPadding("NoPadding", "NoPadding"),
|
||||
PCKS5Padding( "PKCS5Padding", "PKCS5Padding"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
|
||||
public static Padding of(String code) {
|
||||
if (code == null || code.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.stream(Padding.values())
|
||||
.filter(it -> Objects.equals(it.getCode(), code))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
package com.sunyard.chsm.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据的填充模式
|
||||
*
|
||||
* @author Cheney
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Padding {
|
||||
NOPadding("NoPadding", "NoPadding"),
|
||||
PCKS5Padding("PKCS5Padding", "PKCS5Padding"),
|
||||
PCKS7Padding("PKCS7Padding", "PKCS7Padding"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
|
||||
public static Padding of(String code) {
|
||||
if (code == null || code.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.stream(Padding.values())
|
||||
.filter(it -> Objects.equals(it.getCode(), code))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/11/13
|
||||
*/
|
||||
@Data
|
||||
public class AppTokenReq {
|
||||
|
||||
/**
|
||||
* 平台分配的appKey
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(min = 16, max = 64, message = "appKey长度必须为16-64")
|
||||
private String appKey;
|
||||
/**
|
||||
* 时间戳毫秒数,与服务器时间不能超过5分钟
|
||||
*/
|
||||
@NotNull
|
||||
private Long timestamp;
|
||||
/**
|
||||
* 使用appKey+random+appSecret作为原文,使用appSecret作为key,根据HmacSM3算法计算得到hmac值,转为Hex编码
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(min = 32, max = 64, message = "hmac长度在60-64之间")
|
||||
private String hmac;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/11/13
|
||||
*/
|
||||
@Data
|
||||
public class AppTokenResp {
|
||||
/**
|
||||
* 获取的token的值
|
||||
*/
|
||||
private String token;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* @author Wangjingcheng
|
||||
* @Version 1.0.0
|
||||
* @Date 2023/8/4 9:56
|
||||
*/
|
||||
@Data
|
||||
public class AsymDecryptReq {
|
||||
// 密钥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;
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymDecryptResp {
|
||||
// 密钥ID
|
||||
private Long keyId;
|
||||
// 密钥索引
|
||||
private String keyIndex;
|
||||
// 明文,使用Base64编码
|
||||
private String plainData;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class AsymEncryptReq {
|
||||
|
||||
// 密钥ID
|
||||
@NotNull(message = "密钥ID不能为空")
|
||||
private Long keyId;
|
||||
|
||||
// 明文,使用Base64编码
|
||||
@NotBlank(message = "明文不能为空")
|
||||
private String plainData;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymEncryptResp {
|
||||
|
||||
// 密钥ID
|
||||
private Long keyId;
|
||||
|
||||
// 密钥索引
|
||||
private String keyIndex;
|
||||
|
||||
// 密文C1C3C2,使用Base64编码
|
||||
private String cipherData;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeSealReq {
|
||||
|
||||
// 签名证书主题
|
||||
private String signSubject;
|
||||
|
||||
// 接收者加密证书
|
||||
@NotBlank(message = "加密证书不能为空")
|
||||
private String encCert;
|
||||
|
||||
// 明文,使用Base64编码
|
||||
@NotBlank(message = "明文不能为空")
|
||||
private String plainData;
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeSealResp {
|
||||
|
||||
// 数字信封。使用Base64编码
|
||||
private String envelopeData;
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeUnsealReq {
|
||||
|
||||
/**
|
||||
* 加密证书的主题
|
||||
*/
|
||||
// @NotBlank(message = "加密证书不能为空")
|
||||
// private String subject;
|
||||
|
||||
// 数字信封。使用Base64编码
|
||||
@NotBlank(message = "数字信封不能为空")
|
||||
private String envelopeData;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymEnvelopeUnsealResp {
|
||||
|
||||
|
||||
// 明文,使用Base64编码
|
||||
private String plainData;
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class AsymSignP7Req {
|
||||
|
||||
/**
|
||||
* 签名证书的主题
|
||||
*/
|
||||
@NotBlank(message = "签名证书不能为空")
|
||||
private String subject;
|
||||
|
||||
// 原文,使用Base64编码
|
||||
@NotBlank(message = "原文不能为空")
|
||||
private String plainData;
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymSignP7Resp {
|
||||
|
||||
// 签名值 Base64编码
|
||||
private String signData;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class AsymSignRawReq {
|
||||
|
||||
// 密钥ID
|
||||
@NotNull(message = "密钥ID不能为空")
|
||||
private Long keyId;
|
||||
|
||||
// 原文,使用Base64编码
|
||||
@NotBlank(message = "明文不能为空")
|
||||
private String plainData;
|
||||
|
||||
// 是否进行预处理 默认是
|
||||
private boolean preProcess = true;
|
||||
|
||||
// 预处理的userId,使用Base64编码
|
||||
private String userId;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AsymSignRawResp {
|
||||
|
||||
// 密钥ID
|
||||
private Long keyId;
|
||||
|
||||
// 密钥索引
|
||||
private String keyIndex;
|
||||
|
||||
// 签名值 Base64编码
|
||||
private String signData;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class AsymVerifyP7Req {
|
||||
|
||||
/**
|
||||
* 签名证书的主题
|
||||
*/
|
||||
// @NotBlank(message = "签名证书不能为空")
|
||||
// private String subject;
|
||||
|
||||
// 原文,使用Base64编码
|
||||
private String plainData;
|
||||
|
||||
// 签名值, 使用Base64编码
|
||||
@NotBlank(message = "签名值不能为空")
|
||||
private String signData;
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
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 AsymVerifyRawReq {
|
||||
|
||||
// 密钥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 signData;
|
||||
|
||||
// 原文,使用Base64编码
|
||||
@NotBlank(message = "明文不能为空")
|
||||
private String plainData;
|
||||
|
||||
// 是否进行预处理 默认是
|
||||
private boolean preProcess = true;
|
||||
|
||||
// 预处理的userId,使用Base64编码
|
||||
private String userId;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zyh
|
||||
* @since 2024/12/20
|
||||
*/
|
||||
@Data
|
||||
public class CertCheckResp {
|
||||
|
||||
/*
|
||||
证书有效期
|
||||
*/
|
||||
private String NotBefore;
|
||||
|
||||
/*
|
||||
证书过期时间
|
||||
*/
|
||||
private String NotAfter;
|
||||
|
||||
/*
|
||||
证书签名
|
||||
* /
|
||||
*/
|
||||
private String Signature;
|
||||
|
||||
|
||||
/*
|
||||
CRL
|
||||
*/
|
||||
private String CRL;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zyh
|
||||
* @since 2024/12/19
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class CertExinfoResp {
|
||||
|
||||
// /*
|
||||
// 密钥用法
|
||||
// */
|
||||
// private String SubjectDN;
|
||||
|
||||
/*
|
||||
签名算法OID
|
||||
*/
|
||||
private String SigAlgOID;
|
||||
|
||||
/*
|
||||
证书签名
|
||||
*/
|
||||
private String Signature;
|
||||
|
||||
// /*
|
||||
// 证书约束
|
||||
// */
|
||||
// private String SubjectType;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zyh
|
||||
* @since 2024/12/19
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class CertInfoResp {
|
||||
|
||||
/*
|
||||
证书主题
|
||||
*/
|
||||
private String SubjectDN;
|
||||
|
||||
/*
|
||||
证书颁发者
|
||||
*/
|
||||
private String IssuerDN;
|
||||
|
||||
/*
|
||||
证书序列号
|
||||
*/
|
||||
private String SerialNumber;
|
||||
|
||||
/*
|
||||
证书有效期
|
||||
*/
|
||||
private String NotBefore;
|
||||
|
||||
/*
|
||||
证书过期时间
|
||||
*/
|
||||
private String NotAfter;
|
||||
|
||||
/*
|
||||
证书签名算法
|
||||
*/
|
||||
private String SigAlgName;
|
||||
|
||||
/*
|
||||
证书公钥 hex格式
|
||||
*/
|
||||
private String PublicKey;
|
||||
|
||||
/*
|
||||
证书版本
|
||||
*/
|
||||
private int Version;
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ExportCertReq {
|
||||
|
||||
/**
|
||||
* 证书的主题
|
||||
*/
|
||||
@NotBlank(message = "证书主题不能为空")
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 证书类型: encrypt_decrypt 加密证书, sign_verify 签名证书
|
||||
*/
|
||||
@NotBlank(message = "证书类型不能为空")
|
||||
private String certType;
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ExportCertResp {
|
||||
|
||||
// 证书内容, base64编码
|
||||
private String certText;
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ExportPubKeyReq {
|
||||
|
||||
// 密钥ID
|
||||
@NotNull(message = "密钥ID不能为空")
|
||||
private Long keyId;
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ExportPubKeyResp {
|
||||
|
||||
// 密钥ID
|
||||
private Long keyId;
|
||||
|
||||
// 密钥索引
|
||||
private String keyIndex;
|
||||
|
||||
// 公钥:x+y, base64编码
|
||||
private String pubKey;
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/25
|
||||
*/
|
||||
@Data
|
||||
public class GenRandomReq {
|
||||
|
||||
// 随机数长度, 最大10240
|
||||
@NotNull(message = "随机数长度不能为空")
|
||||
@Min(value = 1L, message = "随机数长度最小为1")
|
||||
@Max(value = 1024L, message = "随机数长度最大为1024")
|
||||
private Integer length;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/25
|
||||
*/
|
||||
@Data
|
||||
public class GenRandomResp {
|
||||
|
||||
// 随机数, base64编码
|
||||
private String random;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import com.sunyard.chsm.enums.HashAlg;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class HashReq {
|
||||
|
||||
// 明文,使用Base64编码
|
||||
private String plainData;
|
||||
|
||||
// 句柄, 多包计算Init后返回
|
||||
private String handle;
|
||||
// Hash算法, 默认SM3
|
||||
private HashAlg alg = HashAlg.SM3;
|
||||
|
||||
// 公钥,使用Base64编码
|
||||
private String pubKey;
|
||||
// userId,使用Base64编码
|
||||
private String userId;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class HashResp {
|
||||
|
||||
// 句柄, 多包计算Init后返回
|
||||
private String handle;
|
||||
|
||||
// mac值,使用Base64编码
|
||||
private String hash;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ImportCertReq {
|
||||
/**
|
||||
* 密钥算法 目前支持: SM2
|
||||
*/
|
||||
@NotEmpty(message = "密钥算法不能为空")
|
||||
private String keyAlg;
|
||||
/**
|
||||
* 是否单证,
|
||||
*/
|
||||
@NotNull(message = "单双证不能为空")
|
||||
private Boolean single;
|
||||
/**
|
||||
* 证书类型: encrypt_decrypt 加密证书, sign_verify 签名证书
|
||||
*/
|
||||
private String certType;
|
||||
/**
|
||||
* 单证证书内容
|
||||
*/
|
||||
private String certText;
|
||||
/**
|
||||
* 签名证书内容
|
||||
*/
|
||||
private String signCertText;
|
||||
/**
|
||||
* 加密证书内容
|
||||
*/
|
||||
private String encCertText;
|
||||
/**
|
||||
* 加密密钥信封
|
||||
*/
|
||||
private String envelopedKey;
|
||||
private String remark;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/17
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class KeyCreateReq {
|
||||
|
||||
/**
|
||||
* 密钥模版编码
|
||||
*/
|
||||
@NotNull(message = "密钥模版不能为空")
|
||||
private String keyTemplateCode;
|
||||
/**
|
||||
* 生成数量
|
||||
*/
|
||||
@NotNull(message = "生成数量不能为空")
|
||||
@Max(value = 100, message = "一次最多生成100个密钥")
|
||||
private Integer genNumber;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import com.sunyard.chsm.enums.KeyCategory;
|
||||
import com.sunyard.chsm.enums.KeyStatus;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/17
|
||||
*/
|
||||
@Data
|
||||
public class KeyInfoQuery {
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
private int pageNumber = 1;
|
||||
/**
|
||||
* 每页大小
|
||||
*/
|
||||
private int pageSize = 10;
|
||||
/**
|
||||
* 密钥状态
|
||||
*/
|
||||
private KeyStatus status;
|
||||
/**
|
||||
* 密钥类型
|
||||
*/
|
||||
private KeyCategory keyType;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.sunyard.chsm.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/12/17
|
||||
*/
|
||||
@Data
|
||||
public class KeyInfoResp {
|
||||
|
||||
/**
|
||||
* 密钥ID
|
||||
*/
|
||||
private Long KeyId;
|
||||
/**
|
||||
* 密钥算法
|
||||
*/
|
||||
private String keyAlg;
|
||||
/**
|
||||
* 密钥类型
|
||||
*/
|
||||
private String keyType;
|
||||
private String keyTypeText;
|
||||
/**
|
||||
* 密钥用途
|
||||
*/
|
||||
private List<String> keyUsages;
|
||||
private String keyUsageText;
|
||||
/**
|
||||
* 校验算法
|
||||
*/
|
||||
private String checkAlg;
|
||||
/**
|
||||
* 校验值
|
||||
*/
|
||||
private String checkValue;
|
||||
/**
|
||||
* 密钥状态
|
||||
*/
|
||||
private String status;
|
||||
private String statusText;
|
||||
/**
|
||||
* 生效时间
|
||||
*/
|
||||
private LocalDateTime effectiveTime;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private LocalDateTime expiredTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user