Merge remote-tracking branch 'origin/V1.00' into V1.00

This commit is contained in:
cheney
2026-05-20 15:40:18 +08:00
58 changed files with 3966 additions and 99 deletions
+39 -5
View File
@@ -20,6 +20,7 @@ mvn spring-boot:run
完整部署手册:
- [docs/deployment/tms-deployment.md](/Users/waner/Work/CISD/文档/tms-framework/docs/deployment/tms-deployment.md)
- [docs/deployment/tms-init-admin-permission-checklist.md](/Users/waner/Work/CISD/文档/tms-framework/docs/deployment/tms-init-admin-permission-checklist.md)
- [docs/deployment/tms-root-admin-actions.md](/Users/waner/Work/CISD/文档/tms-framework/docs/deployment/tms-root-admin-actions.md)
1. 构建 jar 包:
@@ -39,6 +40,9 @@ cp scripts/tms.sh /home/tms/scripts/tms.sh
cp scripts/standard-init/*.sh /home/tms/bin/
sudo cp scripts/standard-init/root-sbin/tms-run-standard-vendor-step /usr/local/sbin/tms-run-standard-vendor-step
sudo cp scripts/standard-init/root-sbin/tms-prepare-standard-permissions /usr/local/sbin/tms-prepare-standard-permissions
sudo cp scripts/standard-init/root-sbin/tms-start-standard-apps /usr/local/sbin/tms-start-standard-apps
sudo cp scripts/standard-init/root-sbin/tms-stop-standard-apps /usr/local/sbin/tms-stop-standard-apps
sudo cp scripts/standard-init/root-sbin/tms-rabbitmqctl /usr/local/sbin/tms-rabbitmqctl
cp scripts/resource-restore/*.sh /home/tms/bin/resource-restore/
cp config/application.yml.example /home/tms/config/application.yml
chmod +x /home/tms/bin/*.sh
@@ -48,6 +52,12 @@ sudo chown root:root /usr/local/sbin/tms-run-standard-vendor-step
sudo chmod 755 /usr/local/sbin/tms-run-standard-vendor-step
sudo chown root:root /usr/local/sbin/tms-prepare-standard-permissions
sudo chmod 755 /usr/local/sbin/tms-prepare-standard-permissions
sudo chown root:root /usr/local/sbin/tms-start-standard-apps
sudo chmod 755 /usr/local/sbin/tms-start-standard-apps
sudo chown root:root /usr/local/sbin/tms-stop-standard-apps
sudo chmod 755 /usr/local/sbin/tms-stop-standard-apps
sudo chown root:root /usr/local/sbin/tms-rabbitmqctl
sudo chmod 755 /usr/local/sbin/tms-rabbitmqctl
```
3. 启动 / 停止 / 状态:
@@ -67,6 +77,30 @@ sudo chmod 755 /usr/local/sbin/tms-prepare-standard-permissions
- 标准 CISD 初始化辅助脚本位于 `scripts/standard-init/`,部署时需复制到 `/home/tms/bin/`。
- `scripts/standard-init/root-sbin/tms-run-standard-vendor-step` 是标准版 vendor 客户化脚本的 root 受控入口,部署到 `/usr/local/sbin/` 后通过 sudoers 授权给 `tms` 使用。
- `scripts/standard-init/root-sbin/tms-prepare-standard-permissions` 是标准版介质权限预处理脚本,由 `root` 在初始化前手工执行,用于修正 `/home/cmep4i/cpackage`、license、SQL load、CMSP/CMTP 目录权限。
- `scripts/standard-init/root-sbin/tms-start-standard-apps` 和 `tms-stop-standard-apps` 是标准版 CMSP/CMTP 的 root 受控停启入口,恢复默认通过它们确保 `cmep4i` 进程能被可靠停止。
- `scripts/standard-init/root-sbin/tms-rabbitmqctl` 是 RabbitMQ CLI 的 root 受控入口,初始化和资源恢复默认通过 `sudo -n /usr/local/sbin/tms-rabbitmqctl` 执行,避免 `tms` 用户 Erlang cookie 与 RabbitMQ 服务端不一致。
- 管理员增加 wrapper 权限的最小命令如下,完整说明见部署文档:
```bash
cat > /etc/sudoers.d/tms-standard-vendor <<'EOF'
Defaults:tms !requiretty
tms ALL=(root) NOPASSWD: \
/usr/local/sbin/tms-rabbitmqctl *, \
/usr/local/sbin/tms-start-standard-apps, \
/usr/local/sbin/tms-stop-standard-apps, \
/usr/local/sbin/tms-run-standard-vendor-step setfraq, \
/usr/local/sbin/tms-run-standard-vendor-step setftq, \
/usr/local/sbin/tms-run-standard-vendor-step settlq, \
/usr/local/sbin/tms-run-standard-vendor-step setsptp
EOF
chown root:root /etc/sudoers.d/tms-standard-vendor
chmod 440 /etc/sudoers.d/tms-standard-vendor
visudo -cf /etc/sudoers.d/tms-standard-vendor
su - tms -c 'sudo -n -l | grep -E "tms-start-standard-apps|tms-stop-standard-apps|tms-rabbitmqctl"'
```
- 资源恢复辅助脚本位于 `scripts/resource-restore/`,部署时需复制到 `/home/tms/bin/resource-restore/`。
- `apply_standard_db.sh` 依赖预置环境变量,例如 `DB_USER`、`DB_PASSWORD`,不要直接写入 `application.yml`。
- 运行目录结构、配置项说明、文件上传 `fileId` 流程和故障排查,请查看上面的完整部署手册。
@@ -99,8 +133,8 @@ java -jar tms-framework.jar --tms.crypto.decrypt
- 本地构建和测试统一使用 JDK 17,与项目和 CI 运行时保持一致。
Open:
- Swagger UI: http://localhost:8080/swagger-ui.html
- Internal API docs JSON: http://localhost:8080/v3/api-docs/internal-api
- Swagger UI: http://localhost:8088/swagger-ui.html
- Internal API docs JSON: http://localhost:8088/v3/api-docs/internal-api
- Runtime-generated Swagger/OpenAPI docs are the source of truth for `/api/**`.
- Internal health API: `GET /api/v1/device/status`
- Internal sign preview API: `POST /api/v1/sign/preview`
@@ -208,8 +242,8 @@ Open:
- `/home/tms/bin/stop_standard_rabbitmq.sh`
- `/home/tms/bin/apply_standard_db.sh`
- `tms.init.executor.standard-db-username/password` 已正确配置
- `tms.init.executor.rabbitmq-ctl-command` 指向可执行的 `rabbitmqctl`
- 运行时 Swagger 文档以 `http://<ip>:8080/swagger-ui.html` 和 `http://<ip>:8080/v3/api-docs/internal-api` 为准
- `tms.init.executor.rabbitmq-ctl-command` 默认 `sudo -n /usr/local/sbin/tms-rabbitmqctl`
- 运行时 Swagger 文档以 `http://<ip>:8088/swagger-ui.html` 和 `http://<ip>:8088/v3/api-docs/internal-api` 为准
## Offline Upgrade
@@ -563,7 +597,7 @@ mvn -q test
Example:
```bash
export BASE_URL=http://127.0.0.1:8080
export BASE_URL=http://127.0.0.1:8088
export INTERNAL_TOKEN=change-me-internal-token
export ALG_SM3=1
export ALG_SM4_ECB=1025
+5 -4
View File
@@ -61,13 +61,14 @@ tms:
standard-web-target-dir: /usr/local/nginx/html
standard-web-organization-json-path: /usr/local/nginx/html/organization.json
standard-run-user: cmep4i
standard-app-start-command: /home/tms/bin/start_standard_apps.sh
standard-app-stop-command: /home/tms/bin/stop_standard_apps.sh
standard-app-start-command: sudo -n /usr/local/sbin/tms-start-standard-apps
standard-app-stop-command: sudo -n /usr/local/sbin/tms-stop-standard-apps
standard-nginx-start-command: /home/tms/bin/start_standard_nginx.sh
standard-nginx-stop-command: /home/tms/bin/stop_standard_nginx.sh
standard-rabbit-stop-command: /home/tms/bin/stop_standard_rabbitmq.sh
standard-post-check-command: /home/tms/bin/check_standard_runtime.sh
standard-vendor-step-runner-command: /usr/local/sbin/tms-run-standard-vendor-step
rabbitmq-ctl-command: sudo -n /usr/local/sbin/tms-rabbitmqctl
upload-base-dir: /home/tms/uploads
staging-root-dir: /tmp/tms-init-staging
direct-receiver-license-target-path: /home/cmep4d/cmep/license/cmep.license
@@ -93,7 +94,7 @@ tms:
- ENTERPRISE
- INDIRECT
- code: STANDARD_APPS
command: /home/tms/bin/stop_standard_apps.sh
command: sudo -n /usr/local/sbin/tms-stop-standard-apps
required: false
product-types:
- ENTERPRISE
@@ -107,7 +108,7 @@ tms:
command: /home/tms/scripts/tms.sh start
required: true
- code: STANDARD_APPS
command: /home/tms/bin/start_standard_apps.sh
command: sudo -n /usr/local/sbin/tms-start-standard-apps
required: true
product-types:
- ENTERPRISE
+312 -10
View File
@@ -364,12 +364,12 @@ java -version
### 3.3 网络与端口
TMS 默认监听端口:
- `8080`
- `8088`
检查命令:
```bash
ss -lntp | grep 8080
ss -lntp | grep 8088
```
### 3.4 建议具备的系统命令
@@ -398,12 +398,33 @@ TMS 统一部署到 `/home/tms`:
│ ├── apply_standard_db.sh
│ ├── start_standard_apps.sh
│ ├── start_standard_nginx.sh
│ └── check_standard_runtime.sh
│ ├── stop_standard_apps.sh
│ ├── stop_standard_nginx.sh
│ ├── stop_standard_rabbitmq.sh
│ ├── check_standard_runtime.sh
│ └── resource-restore
│ ├── apply-resource-backup.sh
│ ├── restore-db.sh
│ └── replay-mq.sh
├── config
│ └── application.yml
├── gateway
│ ├── gm
│ │ ├── start.sh
│ │ ├── nginx.conf
│ │ └── config/http/tms.conf
│ ├── tls13
│ │ ├── start.sh
│ │ ├── nginx.conf
│ │ └── conf.d/tms.conf
│ └── logs
├── logs
│ ├── tms-framework.out.log
│ └── tms-framework.err.log
├── root-sbin
│ ├── tms-prepare-standard-permissions
│ ├── tms-rabbitmqctl
│ └── tms-run-standard-vendor-step
├── run
│ └── tms-framework.pid
├── scripts
@@ -418,7 +439,9 @@ TMS 统一部署到 `/home/tms`:
目录用途:
- `/home/tms/bin`:初始化后置辅助脚本目录,由 `tms.init.executor.*command` 引用
- `/home/tms/config`:Spring Boot 外部配置目录
- `/home/tms/gateway`:TMS 独立网关配置、启动脚本和日志目录
- `/home/tms/logs`:TMS 运行日志
- `/home/tms/root-sbin`:root 受控脚本的部署包暂存目录,实际执行位置为 `/usr/local/sbin`
- `/home/tms/run`:PID 文件目录
- `/home/tms/scripts`:TMS 启停控制脚本目录
- `/home/tms/tmp`:初始化执行日志和临时目录
@@ -432,10 +455,30 @@ TMS 统一部署到 `/home/tms`:
| `/home/tms/scripts/tms.sh` | `scripts/tms.sh` |
| `/home/tms/bin/apply_standard_db.sh` | `scripts/standard-init/apply_standard_db.sh` |
| `/home/tms/bin/start_standard_apps.sh` | `scripts/standard-init/start_standard_apps.sh` |
| `/home/tms/bin/stop_standard_apps.sh` | `scripts/standard-init/stop_standard_apps.sh` |
| `/home/tms/bin/start_standard_nginx.sh` | `scripts/standard-init/start_standard_nginx.sh` |
| `/home/tms/bin/stop_standard_nginx.sh` | `scripts/standard-init/stop_standard_nginx.sh` |
| `/home/tms/bin/stop_standard_rabbitmq.sh` | `scripts/standard-init/stop_standard_rabbitmq.sh` |
| `/home/tms/bin/check_standard_runtime.sh` | `scripts/standard-init/check_standard_runtime.sh` |
| `/home/tms/bin/resource-restore/apply-resource-backup.sh` | `scripts/resource-restore/apply-resource-backup.sh` |
| `/home/tms/bin/resource-restore/restore-db.sh` | `scripts/resource-restore/restore-db.sh` |
| `/home/tms/bin/resource-restore/replay-mq.sh` | `scripts/resource-restore/replay-mq.sh` |
| `/home/tms/root-sbin/tms-run-standard-vendor-step` | `scripts/standard-init/root-sbin/tms-run-standard-vendor-step` |
| `/home/tms/root-sbin/tms-prepare-standard-permissions` | `scripts/standard-init/root-sbin/tms-prepare-standard-permissions` |
| `/home/tms/root-sbin/tms-rabbitmqctl` | `scripts/standard-init/root-sbin/tms-rabbitmqctl` |
| `/usr/local/sbin/tms-run-standard-vendor-step` | 由 `/home/tms/root-sbin/tms-run-standard-vendor-step` 复制安装 |
| `/usr/local/sbin/tms-prepare-standard-permissions` | 由 `/home/tms/root-sbin/tms-prepare-standard-permissions` 复制安装 |
| `/usr/local/sbin/tms-rabbitmqctl` | 由 `/home/tms/root-sbin/tms-rabbitmqctl` 复制安装 |
| `/home/tms/gateway/tls13/start.sh` | `deployment/tms-gateway/tls13/start.sh` |
| `/home/tms/gateway/tls13/nginx.conf` | `deployment/tms-gateway/tls13/nginx.conf` |
| `/home/tms/gateway/tls13/conf.d/tms.conf` | `deployment/tms-gateway/tls13/conf.d/tms.conf` |
| `/home/tms/gateway/gm/start.sh` | `deployment/tms-gateway/gm/start.sh` |
| `/home/tms/gateway/gm/nginx.conf` | `deployment/tms-gateway/gm/nginx.conf` |
| `/home/tms/gateway/gm/config/http/tms.conf` | `deployment/tms-gateway/gm/config/http/tms.conf` |
| `/home/tms/config/application.yml` | 由 `config/application.yml.example` 复制生成 |
`scripts/cisd_init_precheck.sh` 和 `scripts/pcie_realcard_smoke.sh` 属于诊断脚本,不作为 TMS 常驻运行脚本默认部署。现场需要时可复制到 `/home/tms/tools/` 临时执行。
## 6. 部署步骤
### 6.1 构建 jar 包
@@ -448,7 +491,9 @@ mvn -q -DskipTests package
### 6.2 创建运行目录
```bash
sudo mkdir -p /home/tms/{bin,config,scripts,logs,run,tmp,uploads,libs}
sudo mkdir -p /home/tms/{bin,config,scripts,logs,run,tmp,uploads,libs,root-sbin,tools}
sudo mkdir -p /home/tms/bin/resource-restore
sudo mkdir -p /home/tms/gateway/{tls13,gm,logs/tls13,logs/gm}
sudo chown -R "$(whoami)":"$(whoami)" /home/tms
```
@@ -458,9 +503,16 @@ sudo chown -R "$(whoami)":"$(whoami)" /home/tms
cp target/tms-framework-*.jar /home/tms/tms-framework.jar
cp scripts/tms.sh /home/tms/scripts/tms.sh
cp scripts/standard-init/*.sh /home/tms/bin/
cp scripts/resource-restore/*.sh /home/tms/bin/resource-restore/
cp scripts/standard-init/root-sbin/* /home/tms/root-sbin/
cp -R deployment/tms-gateway/tls13/* /home/tms/gateway/tls13/
cp -R deployment/tms-gateway/gm/* /home/tms/gateway/gm/
cp config/application.yml.example /home/tms/config/application.yml
chmod +x /home/tms/scripts/tms.sh
chmod +x /home/tms/bin/*.sh
chmod +x /home/tms/bin/resource-restore/*.sh
chmod +x /home/tms/gateway/tls13/start.sh
chmod +x /home/tms/gateway/gm/start.sh
```
### 6.4 检查部署结果
@@ -516,7 +568,7 @@ tms:
### 7.3 标准版初始化关键配置
标准版初始化重点配置项:
- `standard-home-dir`:默认 `/home/cemp4i`
- `standard-home-dir`:默认 `/home/cmep4i`
- `standard-cpconfig-path`:标准版 `cpconfig.cfg` 路径
- `standard-db-script-base-dir`:默认 `${standard-home-dir}/cpackage`
- `standard-db-load-dir`:默认 `${standard-home-dir}/mysql/loadfilepath`
@@ -524,6 +576,8 @@ tms:
- `standard-web-front-zip-path`:默认 `${standard-home-dir}/cpackage/front/front.zip`
- `standard-web-target-dir`:默认 `/usr/local/nginx/html`
- `standard-web-organization-json-path`:默认 `/usr/local/nginx/html/organization.json`
- `standard-vendor-step-runner-command`:默认 `/usr/local/sbin/tms-run-standard-vendor-step`,用于 `SET_RABBITMQ`、`SET_TLQ`、`TLQ_CONF_APPLY`、`DEPLOY_RECEIVER`
- `rabbitmq-ctl-command`:默认 `sudo -n /usr/local/sbin/tms-rabbitmqctl`,初始化和资源恢复统一通过 root 受控 wrapper 调用 RabbitMQ CLI
### 7.4 不建议直接写入 application.yml 的凭据
@@ -559,7 +613,7 @@ tms:
- `DB_HOST`,默认 `127.0.0.1`
- `DB_PORT`,默认 `4000`
- `DB_NAME`,默认 `CMEP`
- `LOAD_DIR`,默认 `/home/cemp4i/mysql/loadfilepath`
- `LOAD_DIR`,默认 `/home/cmep4i/mysql/loadfilepath`
示例:
@@ -579,8 +633,8 @@ export DB_PASSWORD='secret'
- 启动 CMTP
依赖:
- `/home/cemp4i/cmsp/start_cmsp.sh`
- `/home/cemp4i/cmtp/start_cmtp.sh`
- `/home/cmep4i/cmsp/start_cmsp.sh`
- `/home/cmep4i/cmtp/start_cmtp.sh`
- 用户 `cmep4i`
### 8.3 `start_standard_nginx.sh`
@@ -608,6 +662,50 @@ export DB_PASSWORD='secret'
该脚本仅用于初始化后的结果校验,不负责修复。
### 8.5 `tms-run-standard-vendor-step`
来源:
- [tms-run-standard-vendor-step](/Users/waner/Work/CISD/文档/tms-framework/scripts/standard-init/root-sbin/tms-run-standard-vendor-step)
部署位置:
- `/usr/local/sbin/tms-run-standard-vendor-step`
作用:
- 作为 root 拥有的受控入口,按固定参数执行标准 vendor 脚本
- 内部切换到 `cmep4i` 用户后执行 `/home/cmep4i/cpackage/toolsh/*.sh`
- 当前只允许 `setfraq`、`setftq`、`settlq`、`setsptp`
部署命令:
```bash
sudo cp scripts/standard-init/root-sbin/tms-run-standard-vendor-step /usr/local/sbin/tms-run-standard-vendor-step
sudo chown root:root /usr/local/sbin/tms-run-standard-vendor-step
sudo chmod 755 /usr/local/sbin/tms-run-standard-vendor-step
```
sudoers 建议:
```sudoers
Defaults:tms !requiretty
tms ALL=(root) NOPASSWD: \
/usr/local/sbin/tms-rabbitmqctl *, \
/usr/local/sbin/tms-run-standard-vendor-step setfraq, \
/usr/local/sbin/tms-run-standard-vendor-step setftq, \
/usr/local/sbin/tms-run-standard-vendor-step settlq, \
/usr/local/sbin/tms-run-standard-vendor-step setsptp
```
验证:
```bash
su - tms
sudo -n /usr/local/sbin/tms-run-standard-vendor-step setsptp
ls -l /home/cmep4i/cmsp /home/cmep4i/cmtp
```
执行后 `/home/cmep4i` 下由 vendor 脚本生成或替换的文件应保持为 `cmep4i` 可访问,不应再由 `tms` 直接写成 `tms:tms`。
## 9. 文件上传与 `fileId` 模型
### 9.1 核心原则
@@ -683,6 +781,207 @@ tail -n 100 /home/tms/logs/tms-framework.out.log
tail -n 100 /home/tms/logs/tms-framework.err.log
```
## 10.6 TMS 独立网关(可选)
当现场需要为 `tms-framework` 单独提供 HTTPS 入口时,建议新增独立 TMS 网关,而不是接管标准收发器现有 `/usr/local/nginx`。
这套独立网关的定位是:
- 只服务 TMS 前端和 TMS API
- 独立于标准收发器现有 Nginx
- 不参与 `START_NGINX`、`STOP_NGINX`、`POST_CHECK`
标准收发器现有 Nginx 仍然保持:
- 配置路径:`/usr/local/nginx/conf/nginx.conf`
- Web 发布目录:`/usr/local/nginx/html`
- 默认标准 Web 验收入口:`8000`
### 10.6.1 推荐端口
推荐保留如下边界:
- 标准收发器现有 Web:`8000`
- TMS TLS 1.3 网关:`443`
- TMS 国密网关:`8443`
- TMS 后端:`127.0.0.1:8088`
因此在启用独立网关前,建议先检查:
```bash
ss -lntp | grep -E ':443|:8443|:8000|:8088'
```
### 10.6.2 推荐目录
建议按如下结构部署:
```text
/home/tms
├── web
│ └── dist
└── gateway
├── tls13
├── gm
└── logs
├── tls13
└── gm
```
说明:
- `/home/tms/web/dist`:TMS 前端构建产物
- `/home/tms/gateway/tls13`:TLS 1.3 网关配置
- `/home/tms/gateway/gm`:国密网关配置
- `/home/tms/gateway/logs/*`:对应网关日志
### 10.6.3 仓库模板位置
仓库已提供两套模板:
- TLS 1.3:
- `deployment/tms-gateway/tls13/nginx.conf`
- `deployment/tms-gateway/tls13/conf.d/tms.conf`
- `deployment/tms-gateway/tls13/start.sh`
- `deployment/tms-gateway/tls13/README.md`
- 国密:
- `deployment/tms-gateway/gm/nginx.conf`
- `deployment/tms-gateway/gm/config/http/tms.conf`
- `deployment/tms-gateway/gm/start.sh`
- `deployment/tms-gateway/gm/README.md`
部署时建议将模板复制到:
- `/home/tms/gateway/tls13`
- `/home/tms/gateway/gm`
### 10.6.4 前端发布目录
TMS 前端建议统一发布到:
```text
/home/tms/web/dist
```
不建议继续沿用其他项目使用过的 `/app/hsm/vue` 命名。
网关容器建议统一将:
```text
/home/tms/web
```
挂载到容器:
```text
/app
```
这样容器内静态资源根目录仍保持:
```text
/app/dist
```
### 10.6.5 后端代理约定
独立网关统一代理到:
```text
http://127.0.0.1:8088
```
需要放行的核心路由:
- `/`
- `/api/v1/`
- `/swagger-ui.html`
- `/swagger-ui/`
- `/v3/api-docs/`
特别注意:
- TMS 接口本身挂在 `/api/v1/**`
- 网关 `proxy_pass` 不应把 `/api/v1/` 前缀错误改写掉
- 若网关误把 `/api/v1/device/status` 转成 `/device/status`,后端会返回 404
因此后续修改网关配置时,应保持“原始请求路径透传到 TMS 后端”这一约束。
### 10.6.6 证书与镜像
TLS 1.3 网关:
- 默认使用 `nginx:latest`
- 证书目录:`/home/tms/gateway/tls13/certs`
- 默认文件名:
- `rsa.dev.pem`
- `rsa.dev.key`
国密网关:
- 默认使用 `gateway.lib:latest`
- 证书目录:`/home/tms/gateway/gm/certs`
- 默认文件名:
- `sm2.sig.pem`
- `sm2.sig.key`
- `sm2.enc.pem`
- `sm2.enc.key`
- `rsa.dev.pem`
- `rsa.dev.key`
补充说明:
- TLS 1.3 网关默认读取自己的 `/home/tms/gateway/tls13/certs`
- 国密网关默认读取自己的 `/home/tms/gateway/gm/certs`
- 如果现场希望 TLS 1.3 直接复用国密目录中的 RSA 证书,可在启动 TLS 1.3 网关时额外指定 `CERT_DIR=/home/tms/gateway/gm/certs`
如果现场为离线部署,可参考:
```bash
docker load -i /Users/waner/Work/CISD/文档/manager/tls13/tls13.tar
docker load -i /Users/waner/Work/CISD/文档/manager/gateway.tar
```
### 10.6.7 启动与验收
TLS 1.3 网关:
```bash
cd /home/tms/gateway/tls13
bash start.sh
```
国密网关:
```bash
cd /home/tms/gateway/gm
bash start.sh
```
TLS 1.3 验收可先检查:
```bash
curl -k https://127.0.0.1/
curl -k https://127.0.0.1/swagger-ui.html
curl -k https://127.0.0.1/api/v1/device/status
```
国密入口建议使用现场指定的国密兼容浏览器或客户端进行验证。
### 10.6.8 与标准收发器 Nginx 的边界
本独立网关方案不改变以下既有边界:
- 不写入 `/usr/local/nginx/conf/nginx.conf`
- 不使用 `/usr/local/nginx/html`
- 不替代标准收发器 Web 发布流程
- 不改变标准收发器初始化和 reset 当前逻辑
换句话说:
- 标准收发器 Nginx 继续服务标准产品自身的 Web
- TMS 独立网关只负责 TMS 自己的前端和 API
## 11. 初始化前置检查
预检查脚本:
@@ -859,8 +1158,8 @@ pgrep -f '/usr/local/nginx/sbin/nginx'
```bash
/home/tms/scripts/tms.sh status
ss -lntp | grep 8080
curl -s http://127.0.0.1:8080/api/v1/device/status
ss -lntp | grep 8088
curl -s http://127.0.0.1:8088/api/v1/device/status
find /home/tms/uploads -maxdepth 2 -type f | sort
ls -l /home/tms/bin
ls -l /home/tms/config/application.yml
@@ -871,7 +1170,10 @@ ls -l /home/tms/config/application.yml
```bash
bash -n /home/tms/bin/apply_standard_db.sh
bash -n /home/tms/bin/start_standard_apps.sh
bash -n /home/tms/bin/stop_standard_apps.sh
bash -n /home/tms/bin/start_standard_nginx.sh
bash -n /home/tms/bin/stop_standard_nginx.sh
bash -n /home/tms/bin/stop_standard_rabbitmq.sh
bash -n /home/tms/bin/check_standard_runtime.sh
```
+66
View File
@@ -0,0 +1,66 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cisd</groupId>
<artifactId>openapi-sdk</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.11</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.83</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>2.2.28</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,15 @@
package com.cisd.openapi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan
public class OpenapiSdkApplication
{
public static void main( String[] args ) {
SpringApplication.run(OpenapiSdkApplication.class, args);
}
}
@@ -0,0 +1,468 @@
package com.cisd.openapi;
import com.cisd.openapi.config.SignServerProperties;
import com.cisd.openapi.dto.*;
import com.cisd.openapi.exception.BusinessException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.UUID;
@Component
public class SignClient {
private static final MediaType JSON_MEDIA_TYPE =
MediaType.parse("application/json; charset=utf-8");
private final OkHttpClient okHttpClient;
private final ObjectMapper objectMapper;
private final SignServerProperties properties;
private final ThreadLocal<Integer> lastReturnCode = new ThreadLocal<>();
public SignClient(OkHttpClient okHttpClient,
ObjectMapper objectMapper,
SignServerProperties properties) {
this.okHttpClient = okHttpClient;
this.objectMapper = objectMapper;
this.properties = properties;
}
public String rawSign(byte[] origBytes, String dn) {
String sessionId = UUID.randomUUID().toString().replace("-", "");
if (origBytes == null) {
lastReturnCode.set(-100100);
throw new IllegalArgumentException("origBytes不能为空");
}
if (dn == null || dn.isBlank()) {
lastReturnCode.set(-100100);
throw new IllegalArgumentException("dn不能为空");
}
if (origBytes.length > 4096){
lastReturnCode.set(-100100);
throw new IllegalArgumentException("数据过大");
}
String origBase64 = Base64.getEncoder().encodeToString(origBytes);
RawSignRequest rawSignRequest = new RawSignRequest(
origBase64,
dn
);
rawSignRequest.setSessionId(sessionId);
String requestJson;
try {
requestJson = objectMapper.writeValueAsString(rawSignRequest);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("serialize failed", e);
}
RequestBody requestBody = RequestBody.create(
requestJson,
JSON_MEDIA_TYPE
);
Request request = new Request.Builder()
.url(buildUrl(properties.getRawSignPath()))
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-Key", properties.getApiKey())
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
String responseBody = response.body() == null ? "" : response.body().string();
JsonNode root = objectMapper.readTree(responseBody);
int code = root.get("code").asInt();
System.out.println(root.toPrettyString());
if (code == 200){
RawSignResponse data = objectMapper.treeToValue(root.get("data"), RawSignResponse.class);
return data.getSignature();
} else if (code == 400){
ErrorDataResponse errorData = objectMapper.treeToValue(root.get("data"), ErrorDataResponse.class);
lastReturnCode.set(errorData.getCode());
throw new BusinessException("参数错误或签名失败(具体见返回码)", errorData);
} else if (code == 401){
throw new RuntimeException("未认证或会话无效");
} else{
throw new RuntimeException("会话不存在");
}
} catch (IOException e) {
throw new RuntimeException("调用rawSign异常", e);
}
}
public boolean rawVerify(byte[] origBytes, String certStr, String dn) {
String sessionId = UUID.randomUUID().toString().replace("-", "");
if (origBytes == null) {
lastReturnCode.set(-100100);
throw new IllegalArgumentException("origBytes不能为空");
}
if (dn == null || dn.isBlank()) {
lastReturnCode.set(-100100);
throw new IllegalArgumentException("dn不能为空");
}
if (certStr == null || certStr.isBlank()) {
lastReturnCode.set(-100100);
throw new IllegalArgumentException("certStr签名串不能为空");
}
if (origBytes.length > 4096) {
lastReturnCode.set(-100100);
throw new IllegalArgumentException("数据过大");
}
String origBase64 = Base64.getEncoder().encodeToString(origBytes);
// String origBase64 = "SGVsbG8gV29ybGQ=";
RawVerifyRequest rawVerifyRequest = new RawVerifyRequest(
origBase64,
dn,
certStr
);
rawVerifyRequest.setSessionId(sessionId);
String requestJson;
try {
requestJson = objectMapper.writeValueAsString(rawVerifyRequest);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("serialize failed", e);
}
RequestBody requestBody = RequestBody.create(
requestJson,
JSON_MEDIA_TYPE
);
Request request = new Request.Builder()
.url(buildUrl(properties.getRawVerifyPath()))
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-Key", properties.getApiKey())
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
String responseBody = response.body() == null ? "" : response.body().string();
JsonNode root = objectMapper.readTree(responseBody);
int code = root.get("code").asInt();
if (code == 200) {
RawVerifyResponse data = objectMapper.treeToValue(root.get("data"), RawVerifyResponse.class);
return data.getVerified();
} else if (code == 400) {
ErrorDataResponse errorData = objectMapper.treeToValue(root.get("data"), ErrorDataResponse.class);
lastReturnCode.set(errorData.getCode());
throw new BusinessException("参数错误或签名失败(具体见返回码)", errorData);
} else if (code == 401) {
throw new RuntimeException("未认证或会话无效");
} else {
throw new RuntimeException("会话不存在");
}
} catch (IOException e) {
throw new RuntimeException("调用rawVerify异常", e);
}
}
public String dettachedSign(byte[] origBytes, String dn) {
String sessionId = UUID.randomUUID().toString().replace("-", "");
if (origBytes == null) {
throw new IllegalArgumentException("origBytes不能为空");
}
if (dn == null || dn.isBlank()) {
throw new IllegalArgumentException("dn不能为空");
}
if (origBytes.length > 4096){
throw new IllegalArgumentException("数据过大大");
}
String origBase64 = Base64.getEncoder().encodeToString(origBytes);
DettachedSignRequest dettachedSignRequest = new DettachedSignRequest(
sessionId,
origBase64,
dn
);
String requestJson;
try {
requestJson = objectMapper.writeValueAsString(dettachedSignRequest);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("serialize failed", e);
}
RequestBody requestBody = RequestBody.create(
requestJson,
JSON_MEDIA_TYPE
);
Request request = new Request.Builder()
.url(buildUrl(properties.getDettachedSignPath()))
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-Key", properties.getApiKey())
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
String responseBody = response.body() == null ? "" : response.body().string();
JsonNode root = objectMapper.readTree(responseBody);
int code = root.get("code").asInt();
if (code == 200){
DettachedSignResponse data = objectMapper.treeToValue(root.get("data"), DettachedSignResponse.class);
return data.getSignature();
} else if (code == 400){
ErrorDataResponse errorData = objectMapper.treeToValue(root.get("data"), ErrorDataResponse.class);
throw new BusinessException("参数错误或签名失败(具体见返回码)", errorData);
} else if (code == 401){
throw new RuntimeException("未认证或会话无效");
} else{
throw new RuntimeException("会话不存在");
}
} catch (IOException e) {
throw new RuntimeException("调用dettachedSign异常", e);
}
}
public GenericCertificate dettachedVerify(byte[] origBytes, String certStr) {
String sessionId = UUID.randomUUID().toString().replace("-", "");
if (origBytes == null) {
throw new IllegalArgumentException("origBytes不能为空");
}
if (certStr == null || certStr.isBlank()) {
throw new IllegalArgumentException("certStr签名串不能为空");
}
if (origBytes.length > 4096){
throw new IllegalArgumentException("数据过大");
}
String origBase64 = Base64.getEncoder().encodeToString(origBytes);
DettachedVerifyRequest dettachedVerifyRequest = new DettachedVerifyRequest(
origBase64,
certStr
);
dettachedVerifyRequest.setSessionId(sessionId);
String requestJson;
try {
requestJson = objectMapper.writeValueAsString(dettachedVerifyRequest);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("serialize failed", e);
}
RequestBody requestBody = RequestBody.create(
requestJson,
JSON_MEDIA_TYPE
);
Request request = new Request.Builder()
.url(buildUrl(properties.getDettachedVerifyPath()))
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-Key", properties.getApiKey())
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
String responseBody = response.body() == null ? "" : response.body().string();
JsonNode root = objectMapper.readTree(responseBody);
int code = root.get("code").asInt();
System.out.println(root.toPrettyString());
if (code == 200){
DettachedVerifyResponse data = objectMapper.treeToValue(root.get("data"), DettachedVerifyResponse.class);
return data.getGenericCertificate();
} else if (code == 400){
ErrorDataResponse errorData = objectMapper.treeToValue(root.get("data"), ErrorDataResponse.class);
// System.out.println(errorData.getCode());
// System.out.println(errorData.getMessage());
throw new BusinessException("参数错误或签名失败(具体见返回码)", errorData);
} else if (code == 401){
throw new RuntimeException("未认证或会话无效");
} else{
throw new RuntimeException("会话不存在");
}
} catch (IOException e) {
throw new RuntimeException("调用dettachedVerify异常", e);
}
}
public GenericCertificate dettachedVerifySimple(byte[] origBytes, String certStr) {
String sessionId = UUID.randomUUID().toString().replace("-", "");
if (origBytes == null) {
throw new IllegalArgumentException("origBytes不能为空");
}
if (certStr == null || certStr.isBlank()) {
throw new IllegalArgumentException("certStr签名串不能为空");
}
if (origBytes.length > 4096){
throw new IllegalArgumentException("数据过大");
}
String origBase64 = Base64.getEncoder().encodeToString(origBytes);
DettachedVerifySimpleRequest dettachedVerifySimpleRequest = new DettachedVerifySimpleRequest(
origBase64,
certStr
);
dettachedVerifySimpleRequest.setSessionId(sessionId);
String requestJson;
try {
requestJson = objectMapper.writeValueAsString(dettachedVerifySimpleRequest);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("serialize failed", e);
}
RequestBody requestBody = RequestBody.create(
requestJson,
JSON_MEDIA_TYPE
);
Request request = new Request.Builder()
.url(buildUrl(properties.getDettachedVerifySimplePath()))
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-Key", properties.getApiKey())
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
String responseBody = response.body() == null ? "" : response.body().string();
JsonNode root = objectMapper.readTree(responseBody);
int code = root.get("code").asInt();
System.out.println(root.toPrettyString());
if (code == 200){
DettachedVerifySimpleResponse data = objectMapper.treeToValue(root.get("data"), DettachedVerifySimpleResponse.class);
return data.getGenericCertificate();
} else if (code == 400){
ErrorDataResponse errorData = objectMapper.treeToValue(root.get("data"), ErrorDataResponse.class);
throw new BusinessException("参数错误或签名失败(具体见返回码)", errorData);
} else if (code == 401){
throw new RuntimeException("未认证或会话无效");
} else{
throw new RuntimeException("会话不存在");
}
} catch (IOException e) {
throw new RuntimeException("调用dettachedVerifySimple异常", e);
}
}
public boolean uploadCert(String bankCode, X509Certificate certificate){
String sessionId = UUID.randomUUID().toString().replace("-", "");
if (bankCode == null || bankCode.isBlank()) {
throw new IllegalArgumentException("origBytes不能为空");
}
if (certificate == null){
throw new IllegalArgumentException("certificate不能为空");
}
UploadCertRequest uploadCertRequest = new UploadCertRequest(
bankCode,
certificate
);
uploadCertRequest.setSessionId(sessionId);
String requestJson;
try {
requestJson = objectMapper.writeValueAsString(uploadCertRequest);
}catch (JsonProcessingException e){
throw new IllegalArgumentException("serialize failed", e);
}
RequestBody requestBody = RequestBody.create(
requestJson,
JSON_MEDIA_TYPE
);
Request request = new Request.Builder()
.url(buildUrl(properties.getUploadCertPath()))
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-Key", properties.getApiKey())
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
String responseBody = response.body() == null ? "" : response.body().string();
JsonNode root = objectMapper.readTree(responseBody);
int code = root.get("code").asInt();
if (code == 200){
UploadCertResponse data = objectMapper.treeToValue(root.get("data"), UploadCertResponse.class);
return data.getSuccess();
} else if (code == 400){
ErrorDataResponse errorData = objectMapper.treeToValue(root.get("data"), ErrorDataResponse.class);
throw new BusinessException("参数错误或签名失败(具体见返回码)", errorData);
} else if (code == 401){
throw new RuntimeException("未认证或会话无效");
} else{
throw new RuntimeException("会话不存在");
}
} catch (IOException e) {
throw new RuntimeException("调用uploadCert异常", e);
}
}
public Integer lastReturnCode(){
return lastReturnCode.get();
}
private String buildUrl(String path) {
String baseUrl = properties.getBaseUrl();
if (baseUrl.endsWith("/") && path.startsWith("/")) {
return baseUrl.substring(0, baseUrl.length() - 1) + path;
}
if (!baseUrl.endsWith("/") && !path.startsWith("/")) {
return baseUrl + "/" + path;
}
return baseUrl + path;
}
}
@@ -0,0 +1,20 @@
package com.cisd.openapi.config;
import okhttp3.OkHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
public class OkHttpConfig {
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.connectTimeout(Duration.ofSeconds(5))
.writeTimeout(Duration.ofSeconds(10))
.readTimeout(Duration.ofSeconds(30))
.build();
}
}
@@ -0,0 +1,29 @@
package com.cisd.openapi.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "sign-server")
@Data
public class SignServerProperties {
private String baseUrl;
private String apiKey;
private String rawSignPath;
private String rawVerifyPath;
private String dettachedSignPath;
private String dettachedVerifyPath;
private String dettachedVerifySimplePath;
private String uploadCertPath;
private String lastReturnCodePath;
}
@@ -0,0 +1,10 @@
package com.cisd.openapi.dto;
public class CertificateInfo {
private String subjectDN;
private String issuerDN;
private String serialNumber;
private String notBefore;
private String notAfter;
private String base64Encoded;
}
@@ -0,0 +1,19 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class DettachedSignRequest {
private String sessionId;
private String origBytes;
private String dn;
public DettachedSignRequest(String sessionId, String origBytes, String dn) {
this.sessionId = sessionId;
this.origBytes = origBytes;
this.dn = dn;
}
}
@@ -0,0 +1,9 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class DettachedSignResponse {
private String signature;
}
@@ -0,0 +1,20 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class DettachedVerifyRequest {
private String sessionId;
private String origBytes;
private String signature;
public DettachedVerifyRequest(String origBytes, String signature) {
this.origBytes = origBytes;
this.signature = signature;
}
}
@@ -0,0 +1,9 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class DettachedVerifyResponse {
private GenericCertificate genericCertificate;
}
@@ -0,0 +1,19 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class DettachedVerifySimpleRequest {
private String sessionId;
private String origBytes;
private String signature;
public DettachedVerifySimpleRequest(String origBytes, String signature) {
this.origBytes = origBytes;
this.signature = signature;
}
}
@@ -0,0 +1,9 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class DettachedVerifySimpleResponse {
private GenericCertificate genericCertificate;
}
@@ -0,0 +1,19 @@
package com.cisd.openapi.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class DeviceActivateInfoResponse {
@Schema(description = "设备型号", example = "SYD7108")
private String deviceModel;
@Schema(description = "设备名称", example = "跨境支付终端一体机")
private String deviceName;
@Schema(description = "设备版本", example = "1.0.0")
private String version;
@Schema(description = "设备序列号", example = "1234567890ABCDEF")
private String serialNumber;
@Schema(description = "设备状态", example = "INITIALIZED")
private String deviceStatus;
}
@@ -0,0 +1,11 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class DeviceActivateRequest {
private String userId;
private String credential;
private String activationCode;
}
@@ -0,0 +1,9 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class ErrorDataResponse {
Integer code;
String message;
}
@@ -0,0 +1,123 @@
package com.cisd.openapi.dto;
import com.cisd.openapi.util.Base64;
import com.cisd.openapi.util.NetSignAgentUtil;
import com.cisd.openapi.util.PBCUtil;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
public class GenericCertificate {
private String bankcode;
private String subject;
private String ser_number;
private String issuer_subject;
private String start_time;
private String end_time;
private X509Certificate cert;
public GenericCertificate() {
}
public GenericCertificate(X509Certificate cert) {
this.setCert(cert);
}
public String getBankcode() {
return this.bankcode;
}
public void setBankcode(String bankcode) {
this.bankcode = bankcode;
}
public String getSubject() {
return this.subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getSer_number() {
return this.ser_number;
}
public void setSer_number(String ser_number) {
this.ser_number = ser_number;
}
public String getIssuer_subject() {
return this.issuer_subject;
}
public void setIssuer_subject(String issuer_subject) {
this.issuer_subject = issuer_subject;
}
public String getStart_time() {
return this.start_time;
}
public void setStart_time(String start_time) {
this.start_time = start_time;
}
public String getEnd_time() {
return this.end_time;
}
public void setEnd_time(String end_time) {
this.end_time = end_time;
}
public X509Certificate getCert() {
return this.cert;
}
public String getBase64Cert() {
if (this.cert == null) {
return null;
} else {
try {
return Base64.encode(this.cert.getEncoded());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public void setCert(X509Certificate cert) {
this.cert = cert;
this.subject = NetSignAgentUtil.getDN(cert.getSubjectDN().toString());
this.ser_number = NetSignAgentUtil.dealSN(cert.getSerialNumber().toString(16).toUpperCase());
this.issuer_subject = NetSignAgentUtil.getDN(cert.getIssuerDN().toString());
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
this.start_time = sdf.format(cert.getNotBefore());
this.end_time = sdf.format(cert.getNotAfter());
this.bankcode = PBCUtil.getBankID(this.subject);
}
public String toString() {
StringBuffer s = new StringBuffer();
s.append("=============BEGIN GenericCertificate===================\n");
s.append("bankCode:").append(this.bankcode).append("\n");
s.append("issuerSubject:").append(this.issuer_subject).append("\n");
s.append("serialNumber:").append(this.ser_number).append("\n");
s.append("subject:").append(this.subject).append("\n");
s.append("startTime:").append(this.start_time).append("\n");
s.append("endTime:").append(this.end_time).append("\n");
if (this.cert != null) {
try {
s.append("cert:").append(Base64.encode(this.cert.getEncoded())).append("\n");
} catch (Exception var3) {
}
}
s.append("=============END GenericCertificate=====================\n");
return s.toString();
}
}
@@ -0,0 +1,12 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class LastReturnCodeRequest {
private String sessionId;
public LastReturnCodeRequest(String sessionId){
this.sessionId = sessionId;
}
}
@@ -0,0 +1,8 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class LastReturnCodeResponse {
private Integer returnCode;
}
@@ -0,0 +1,19 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class RawSignRequest {
private String sessionId;
private String origBytes;
private String dn;
public RawSignRequest(String origBytes, String dn) {
this.origBytes = origBytes;
this.dn = dn;
}
}
@@ -0,0 +1,8 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class RawSignResponse {
private String signature;
}
@@ -0,0 +1,24 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class RawVerifyRequest {
private String sessionId;
private String origBytes;
private String dn;
private String signature;
public RawVerifyRequest(String origBytes, String dn, String signature) {
this.origBytes = origBytes;
this.dn = dn;
this.signature = signature;
}
}
@@ -0,0 +1,10 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class RawVerifyResponse {
private Boolean verified;
private String message;
}
@@ -0,0 +1,18 @@
package com.cisd.openapi.dto;
import lombok.Data;
import java.security.cert.X509Certificate;
@Data
public class UploadCertRequest {
private String sessionId;
private String bankCode;
private X509Certificate certificate;
public UploadCertRequest(String bankCode, X509Certificate certificate) {
this.bankCode = bankCode;
this.certificate = certificate;
}
}
@@ -0,0 +1,9 @@
package com.cisd.openapi.dto;
import lombok.Data;
@Data
public class UploadCertResponse {
private Boolean success;
}
@@ -0,0 +1,16 @@
package com.cisd.openapi.exception;
import com.cisd.openapi.dto.ErrorDataResponse;
public class BusinessException extends RuntimeException {
private final ErrorDataResponse errorData;
public BusinessException(String message, ErrorDataResponse errorData) {
super(message);
this.errorData = errorData;
}
public ErrorDataResponse getErrorData() {
return errorData;
}
}
@@ -0,0 +1,324 @@
package com.cisd.openapi.util;
import java.util.Arrays;
public class Base64 {
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
private static final int[] IA = new int[256];
static {
Arrays.fill(IA, -1);
int i = 0;
for (int iS = CA.length; i < iS; IA[CA[i]] = i++) {
}
IA[61] = 0;
}
public static final String encode(byte[] sArr) {
return new String(encodeToString(sArr, false));
}
private static final char[] encodeToChar(byte[] sArr, boolean lineSep) {
int sLen = sArr != null ? sArr.length : 0;
if (sLen == 0) {
return new char[0];
} else {
int eLen = sLen / 3 * 3;
int cCnt = (sLen - 1) / 3 + 1 << 2;
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0);
char[] dArr = new char[dLen];
int s = 0;
int d = 0;
int cc = 0;
while (s < eLen) {
int i = (sArr[s++] & 255) << 16 | (sArr[s++] & 255) << 8 | sArr[s++] & 255;
dArr[d++] = CA[i >>> 18 & 63];
dArr[d++] = CA[i >>> 12 & 63];
dArr[d++] = CA[i >>> 6 & 63];
dArr[d++] = CA[i & 63];
if (lineSep) {
++cc;
if (cc == 19 && d < dLen - 2) {
dArr[d++] = '\r';
dArr[d++] = '\n';
cc = 0;
}
}
}
s = sLen - eLen;
if (s > 0) {
d = (sArr[eLen] & 255) << 10 | (s == 2 ? (sArr[sLen - 1] & 255) << 2 : 0);
dArr[dLen - 4] = CA[d >> 12];
dArr[dLen - 3] = CA[d >>> 6 & 63];
dArr[dLen - 2] = s == 2 ? CA[d & 63] : 61;
dArr[dLen - 1] = '=';
}
return dArr;
}
}
public static final byte[] decode(char[] sArr) {
char[] tmp = new char[sArr.length];
int t = 0;
int i = 0;
for (int length = sArr.length; i < length; ++i) {
if (sArr[i] >= 'A' && sArr[i] <= 'Z' || sArr[i] >= 'a' && sArr[i] <= 'z' || sArr[i] >= '0' && sArr[i] <= '9' || sArr[i] == '+' || sArr[i] == '/' || sArr[i] == '=') {
tmp[t] = sArr[i];
++t;
}
}
char[] tmp2 = new char[t];
System.arraycopy(tmp, 0, tmp2, 0, t);
tmp = (char[]) null;
return decodeFast(tmp2);
}
private static final byte[] decodeFast(char[] sArr) {
int sLen = sArr.length;
if (sLen == 0) {
return new byte[0];
} else {
int sIx = 0;
int eIx;
for (eIx = sLen - 1; sIx < eIx && IA[sArr[sIx]] < 0; ++sIx) {
}
while (eIx > 0 && IA[sArr[eIx]] < 0) {
--eIx;
}
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0;
int cCnt = eIx - sIx + 1;
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
int len = ((cCnt - sepCnt) * 6 >> 3) - pad;
byte[] dArr = new byte[len];
int d = 0;
int cc = 0;
int eLen = len / 3 * 3;
while (d < eLen) {
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
dArr[d++] = (byte) (i >> 16);
dArr[d++] = (byte) (i >> 8);
dArr[d++] = (byte) i;
if (sepCnt > 0) {
++cc;
if (cc == 19) {
sIx += 2;
cc = 0;
}
}
}
if (d < len) {
cc = 0;
for (int j = 0; sIx <= eIx - pad; ++j) {
cc |= IA[sArr[sIx++]] << 18 - j * 6;
}
for (int r = 16; d < len; r -= 8) {
dArr[d++] = (byte) (cc >> r);
}
}
return dArr;
}
}
private static final byte[] encodeToByte(byte[] sArr, boolean lineSep) {
int sLen = sArr != null ? sArr.length : 0;
if (sLen == 0) {
return new byte[0];
} else {
int eLen = sLen / 3 * 3;
int cCnt = (sLen - 1) / 3 + 1 << 2;
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0);
byte[] dArr = new byte[dLen];
int s = 0;
int d = 0;
int cc = 0;
while (s < eLen) {
int i = (sArr[s++] & 255) << 16 | (sArr[s++] & 255) << 8 | sArr[s++] & 255;
dArr[d++] = (byte) CA[i >>> 18 & 63];
dArr[d++] = (byte) CA[i >>> 12 & 63];
dArr[d++] = (byte) CA[i >>> 6 & 63];
dArr[d++] = (byte) CA[i & 63];
if (lineSep) {
++cc;
if (cc == 19 && d < dLen - 2) {
dArr[d++] = 13;
dArr[d++] = 10;
cc = 0;
}
}
}
s = sLen - eLen;
if (s > 0) {
d = (sArr[eLen] & 255) << 10 | (s == 2 ? (sArr[sLen - 1] & 255) << 2 : 0);
dArr[dLen - 4] = (byte) CA[d >> 12];
dArr[dLen - 3] = (byte) CA[d >>> 6 & 63];
dArr[dLen - 2] = s == 2 ? (byte) CA[d & 63] : 61;
dArr[dLen - 1] = 61;
}
return dArr;
}
}
public static final byte[] decode(byte[] sArr) {
byte[] tmp = new byte[sArr.length];
int t = 0;
int i = 0;
for (int length = sArr.length; i < length; ++i) {
if (sArr[i] >= 65 && sArr[i] <= 90 || sArr[i] >= 97 && sArr[i] <= 122 || sArr[i] >= 48 && sArr[i] <= 57 || sArr[i] == 43 || sArr[i] == 47 || sArr[i] == 61) {
tmp[t] = sArr[i];
++t;
}
}
byte[] tmp2 = new byte[t];
System.arraycopy(tmp, 0, tmp2, 0, t);
tmp = (byte[]) null;
return decodeFast(tmp2);
}
private static final byte[] decodeFast(byte[] sArr) {
int sLen = sArr.length;
if (sLen == 0) {
return new byte[0];
} else {
int sIx = 0;
int eIx;
for (eIx = sLen - 1; sIx < eIx && IA[sArr[sIx] & 255] < 0; ++sIx) {
}
while (eIx > 0 && IA[sArr[eIx] & 255] < 0) {
--eIx;
}
int pad = sArr[eIx] == 61 ? (sArr[eIx - 1] == 61 ? 2 : 1) : 0;
int cCnt = eIx - sIx + 1;
int sepCnt = sLen > 76 ? (sArr[76] == 13 ? cCnt / 78 : 0) << 1 : 0;
int len = ((cCnt - sepCnt) * 6 >> 3) - pad;
byte[] dArr = new byte[len];
int d = 0;
int cc = 0;
int eLen = len / 3 * 3;
while (d < eLen) {
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
dArr[d++] = (byte) (i >> 16);
dArr[d++] = (byte) (i >> 8);
dArr[d++] = (byte) i;
if (sepCnt > 0) {
++cc;
if (cc == 19) {
sIx += 2;
cc = 0;
}
}
}
if (d < len) {
cc = 0;
for (int j = 0; sIx <= eIx - pad; ++j) {
cc |= IA[sArr[sIx++]] << 18 - j * 6;
}
for (int r = 16; d < len; r -= 8) {
dArr[d++] = (byte) (cc >> r);
}
}
return dArr;
}
}
private static final String encodeToString(byte[] sArr, boolean lineSep) {
return new String(encodeToChar(sArr, lineSep));
}
public static final byte[] decode(String sArr) {
return decode(sArr.getBytes());
}
private static final byte[] decodeFast(String s) {
int sLen = s.length();
if (sLen == 0) {
return new byte[0];
} else {
int sIx = 0;
int eIx;
for (eIx = sLen - 1; sIx < eIx && IA[s.charAt(sIx) & 255] < 0; ++sIx) {
}
while (eIx > 0 && IA[s.charAt(eIx) & 255] < 0) {
--eIx;
}
int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0;
int cCnt = eIx - sIx + 1;
int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
int len = ((cCnt - sepCnt) * 6 >> 3) - pad;
byte[] dArr = new byte[len];
int d = 0;
int cc = 0;
int eLen = len / 3 * 3;
while (d < eLen) {
int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)];
dArr[d++] = (byte) (i >> 16);
dArr[d++] = (byte) (i >> 8);
dArr[d++] = (byte) i;
if (sepCnt > 0) {
++cc;
if (cc == 19) {
sIx += 2;
cc = 0;
}
}
}
if (d < len) {
cc = 0;
for (int j = 0; sIx <= eIx - pad; ++j) {
cc |= IA[s.charAt(sIx++)] << 18 - j * 6;
}
for (int r = 16; d < len; r -= 8) {
dArr[d++] = (byte) (cc >> r);
}
}
return dArr;
}
}
public static void main(String[] args) {
String a = "abcabc";
String b64 = encode(a.getBytes());
System.out.println(b64);
b64 = "YWJj\r\nYWJj\r\nYWJj\r\n";
byte[] x = decode(b64.getBytes());
System.out.println(encode(x));
byte[] plain = new byte[]{18, 52};
char[] var10000 = new char[]{'\u0012', '4'};
System.out.println(encode(plain));
}
}
@@ -0,0 +1,117 @@
package com.cisd.openapi.util;
import java.util.Stack;
import java.util.StringTokenizer;
public class DNItemReverseUtil {
private static final String[] dNObjectsForward = new String[]{"1.2.840.113549.1.9.8", "1.2.840.113549.1.9.2", "emailaddress", "e", "email", "uid", "cn", "sn", "serialnumber", "gn", "givenname", "initials", "surname", "t", "ou", "o", "l", "st", "dc", "c"};
private static final String[] dNObjectsReverse = new String[]{"c", "dc", "st", "l", "o", "ou", "t", "surname", "initials", "givenname", "gn", "serialnumber", "sn", "cn", "uid", "email", "e", "emailaddress", "1.2.840.113549.1.9.2", "1.2.840.113549.1.9.8"};
private static final String[] dNObjects;
static {
dNObjects = dNObjectsForward;
}
public static String reverse(String dn) {
StringTokenizer st = new StringTokenizer(dn, ",");
Stack s = new Stack();
while(st.hasMoreTokens()) {
s.push(st.nextToken());
}
int len = s.size();
StringBuffer buffer = new StringBuffer();
for(int i = 0; i < len; ++i) {
if (i > 0) {
buffer.append(",");
}
buffer.append((String)s.pop());
}
return buffer.toString();
}
// public static String reverseDN(String dn) {
// String ret = null;
// if (dn != null) {
// BasicX509NameTokenizer xt = new BasicX509NameTokenizer(dn);
// StringBuffer buf = new StringBuffer();
//
// String o;
// for(boolean first = true; xt.hasMoreTokens(); buf.insert(0, o)) {
// o = xt.nextToken();
// if (!first) {
// buf.insert(0, ",");
// } else {
// first = false;
// }
// }
//
// if (buf.length() > 0) {
// ret = buf.toString();
// }
// }
//
// return ret;
// }
public static String reverseDN2(String dn) {
if (dn == null) {
return null;
} else {
StringBuffer sb = new StringBuffer();
String[] tmp = dn.split("(?<!\\\\)[,;]");
for(int i = tmp.length - 1; i >= 0; --i) {
sb.append(tmp[i]);
if (i > 0) {
sb.append(",");
}
}
return sb.toString();
}
}
public static boolean isDNReversed(String dn) {
boolean ret = false;
if (dn != null) {
String first = null;
String last = null;
X509NameTokenizer xt = new X509NameTokenizer(dn);
if (xt.hasMoreTokens()) {
first = xt.nextToken();
}
while(xt.hasMoreTokens()) {
last = xt.nextToken();
}
if (first != null && last != null) {
first = first.substring(0, first.indexOf(61));
last = last.substring(0, last.indexOf(61));
int firsti = 0;
int lasti = 0;
for(int i = 0; i < dNObjects.length; ++i) {
if (first.toLowerCase().equals(dNObjectsForward[i])) {
firsti = i;
}
if (last.toLowerCase().equals(dNObjectsForward[i])) {
lasti = i;
}
}
if (lasti < firsti) {
ret = true;
}
}
}
return ret;
}
}
@@ -0,0 +1,629 @@
package com.cisd.openapi.util;
//
//import cn.com.infosec.netsign.agent.communication.APIToServer;
//import cn.com.infosec.netsign.agent.exception.NetSignAgentException;
//import cn.com.infosec.netsign.agent.newcommunitor.CommunitorManager;
//import cn.com.infosec.netsign.agent.service.NSService;
//import cn.com.infosec.netsign.crypto.util.SoftCryptoImpl;
//import cn.com.infosec.netsign.frame.config.ExtendedConfig;
//import cn.com.infosec.netsign.logger.ConsoleLogger;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
public class NetSignAgentRes {
private static ResourceBundle resBundle = null;
static boolean hasDone = false;
private static int communicateMode = 1;
private static boolean zipMode = false;
private static int minZipSize = 200;
private static byte[] secKey = null;
private static byte[] keyHash = null;
private static byte[] encSecKey = null;
private static String clientId = null;
private static int seq = 0;
private static String encoding;
private static int orderDN = 0;
private static int snmode = 1;
public static final int CONNECTION_MODE_CONCENTRATE = 1;
public static final int CONNECTION_MODE_HASH = 2;
public static final int CONNECTION_MODE_ORDER = 3;
public static final int CONNECTION_MODE_POLLING = 4;
private static int connectionMode = 1;
private static boolean autoTest = false;
private static long testInterval = 30000L;
private static boolean logConnection = false;
private static boolean poolFlag = false;
private static int maxPoolSize = 20;
private static int retryTimes = 1;
private static int retryInterval = 500;
private static int encLock = 8;
private static int maxWorkingThread = -1;
static {
initNewConnectionParameters();
}
public static void initNewConnectionParameters() {
String str = System.getProperty("cn.com.infosec.netsign.agent.servicetest.interval");
if (str != null) {
try {
testInterval = Long.parseLong(str);
} catch (Exception var3) {
}
}
str = System.getProperty("cn.com.infosec.netsign.agent.servicetest.isautotest");
if (str != null) {
autoTest = str.equals("yes");
}
str = System.getProperty("cn.com.infosec.netsign.agent.connection.mode");
if (str != null) {
try {
connectionMode = Integer.parseInt(str);
} catch (Exception var2) {
}
}
}
// static synchronized void initialize(ResourceBundle res) throws NetSignAgentException {
// if (!hasDone) {
// System.out.println("NetSignServerAPI initialize start...");
// if (resBundle == null) {
// resBundle = getResourceBundle(res);
// }
//
// initConnections();
// if (initPooledFlag().equals("true") && initHeartBeat()) {
// if (APIToServer.timeout == 0) {
// boolean var10000 = true;
// } else {
// int var4 = APIToServer.timeout / 2;
// }
// }
//
// long gc = initGC();
// if (gc != 0L) {
// AutoGC tgc = new AutoGC(gc);
// tgc.start();
// }
//
// clientId = initClientId();
// communicateMode = initCommunicateMode();
// zipMode = initZipMode();
// if (zipMode) {
// minZipSize = initMinZipSize();
// }
//
// if (communicateMode == 0) {
// initSecKey();
// }
//
// encoding = initEncoding() != null && !"".equals(initEncoding()) ? initEncoding() : encoding;
// if (encoding != null && !"".equals(encoding)) {
// ExtendedConfig.setEncoding(encoding);
// } else {
// ExtendedConfig.setEncoding("GBK");
// }
//
// orderDN = initOrderDN();
// snmode = initSNMode();
// hasDone = true;
// encLock = initEncryptLock();
// maxWorkingThread = initMaxParallelThread();
// System.out.println("NetSignServerAPI initialize finished...");
// }
//
// }
public static void setMaxWorkingThread(int count) {
maxWorkingThread = count;
}
public static int getMaxParallelThread() {
return maxWorkingThread;
}
public static int getEncLock() {
return encLock;
}
static int initMaxParallelThread() {
try {
String tmp = resBundle.getString("maxparallelthread");
return Integer.parseInt(tmp);
} catch (Exception var1) {
return maxWorkingThread;
}
}
static int initEncryptLock() {
try {
String tmp = resBundle.getString("encryptlock");
return Integer.parseInt(tmp);
} catch (Exception var1) {
return encLock;
}
}
// static synchronized void initialize(NetSignService service) throws NetSignAgentException {
// if (!hasDone) {
// System.out.println("NetSignServerAPI initialize start...");
// initConnections(service.getHost(), service.getPort(), service.getPoolSize(), service.getTimeout());
// hasDone = true;
// System.out.println("NetSignServerAPI initialize finished...");
// }
//
// }
//
// private static String initServerIP() throws NetSignAgentException {
// try {
// return resBundle.getString("ServerIP").trim();
// } catch (MissingResourceException var1) {
// throw new NetSignAgentException("ServerIP can not be null");
// }
// }
//
// private static String initTimeout() throws NetSignAgentException {
// try {
// return resBundle.getString("Timeout").trim();
// } catch (Exception var1) {
// return "30000";
// }
// }
//
// private static String initConnectTimeout() throws NetSignAgentException {
// try {
// return resBundle.getString("ConnectTimeout").trim();
// } catch (Exception var1) {
// return "30000";
// }
// }
//
// private static String initServerPort() throws NetSignAgentException {
// try {
// return resBundle.getString("ServerPort").trim();
// } catch (MissingResourceException var1) {
// throw new NetSignAgentException("ServerPort can not be null");
// }
// }
//
// private static int initCommunicateMode() throws NetSignAgentException {
// try {
// return Integer.parseInt(resBundle.getString("CommunicateMode").trim());
// } catch (MissingResourceException var1) {
// throw new NetSignAgentException("CommunicateMode can not be null");
// } catch (NumberFormatException var2) {
// throw new NetSignAgentException("Invalid CommunicateMode value");
// }
// }
//
// private static String initMaxConnections() throws NetSignAgentException {
// try {
// String max = resBundle.getString("MaxConnections").trim();
// maxPoolSize = Integer.parseInt(max);
// return max;
// } catch (MissingResourceException var1) {
// maxPoolSize = 20;
// return "20";
// }
// }
//
// private static boolean initZipMode() throws NetSignAgentException {
// try {
// return Boolean.valueOf(resBundle.getString("ZipMode").trim());
// } catch (MissingResourceException var1) {
// throw new NetSignAgentException("ZipMode can not be null");
// }
// }
//
// private static int initMinZipSize() throws NetSignAgentException {
// try {
// return 1024 * Integer.parseInt(resBundle.getString("MinZipSize").trim());
// } catch (MissingResourceException var1) {
// throw new NetSignAgentException("MinZipSize can not be null");
// } catch (NumberFormatException var2) {
// throw new NetSignAgentException("Invalid MinZipSize value");
// }
// }
//
// private static void initSecKey() throws NetSignAgentException {
// String certFile = null;
//
// try {
// certFile = resBundle.getString("ServerEncCert").trim();
// } catch (MissingResourceException var17) {
// throw new NetSignAgentException("ServerEncCert can not be null");
// }
//
// FileInputStream in = null;
//
// try {
// CertificateFactory factory = CertificateFactory.getInstance("X.509", "INFOSEC");
// in = new FileInputStream(certFile);
// System.out.println("comm cert:" + certFile + " length:" + in.available());
// X509Certificate cert = (X509Certificate)factory.generateCertificate(in);
// secKey = SoftCryptoImpl.genSecretKey(SoftCryptoImpl.RC4, 128);
// keyHash = SoftCryptoImpl.hash("SHA1", secKey);
// encSecKey = SoftCryptoImpl.encryptWithRSAPubkey(cert.getPublicKey(), secKey);
// } catch (FileNotFoundException var14) {
// throw new NetSignAgentException("Can not found cert file: " + certFile);
// } catch (Exception ex) {
// ConsoleLogger.logException(ex);
// throw new NetSignAgentException("Generate SecKey Error: " + ex.getMessage());
// } finally {
// try {
// if (in != null) {
// in.close();
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
//
// }
//
// }
private static String initClientId() {
try {
return resBundle.getString("ClientID").trim();
} catch (MissingResourceException var1) {
return null;
}
}
private static String initPooledFlag() {
try {
String flag = resBundle.getString("UsedConnectionPool").trim();
poolFlag = Boolean.valueOf(flag);
return flag;
} catch (Exception var1) {
poolFlag = false;
return "false";
}
}
// private static void initConnections() throws NetSignAgentException {
// initPooledFlag();
// initMaxConnections();
// initConnectTimeout();
// initServiceTestIsTest();
// initConnectionMode();
// initConnectionLog();
// initServiceTestInterval();
// initRetryTimes();
// initRetryInerval();
// NetSignAgent.cm = new CommunitorManager(false);
// String ip = initServerIP();
// String port = initServerPort();
// String timeout = initTimeout();
// String[] ips = ip.split(",");
// String[] ports = port.split(",");
// String[] timeouts = timeout.split(",");
//
// for(int i = 0; i < ips.length; ++i) {
// NSService s = new NSService();
// s.setIp(ips[i].trim());
// s.setPort(Integer.parseInt(ports[i].trim()));
// s.setTimeout(Integer.parseInt(timeouts[i].trim()));
// NetSignAgent.cm.addService(s);
// }
//
// }
private static void initConnectionLog() {
try {
String str = resBundle.getString("logconnection");
if (str != null && (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("yes"))) {
logConnection = true;
}
} catch (Exception var1) {
}
}
private static void initRetryTimes() {
try {
String str = resBundle.getString("retrytimes");
if (str != null) {
retryTimes = Integer.parseInt(str);
}
} catch (Exception var1) {
}
}
private static void initRetryInerval() {
try {
String str = resBundle.getString("retryinterval");
if (str != null) {
retryInterval = Integer.parseInt(str);
}
} catch (Exception var1) {
}
}
private static void initServiceTestIsTest() {
try {
String str = resBundle.getString("servicetestistest");
if (str != null) {
autoTest = "yes".equals(str.toLowerCase()) || "true".equals(str.toLowerCase());
}
} catch (Exception var1) {
}
}
private static void initServiceTestInterval() {
try {
String str = resBundle.getString("servicetestinterval");
if (str != null) {
try {
testInterval = (long)Integer.parseInt(str);
} catch (Exception var2) {
}
}
} catch (Exception var3) {
}
}
private static void initConnectionMode() {
try {
String str = resBundle.getString("connectionmode");
connectionMode = Integer.parseInt(str);
} catch (Exception var1) {
}
if (connectionMode <= 0) {
connectionMode = 3;
}
}
private static boolean initHeartBeat() {
try {
String start = resBundle.getString("heartbeat").trim();
return start.equals("true");
} catch (Exception var1) {
return false;
}
}
private static long initGC() {
try {
String gc = resBundle.getString("gc").trim();
return Long.parseLong(gc);
} catch (Exception var1) {
return 60L;
}
}
private static String initEncoding() {
try {
return resBundle.getString("encoding").trim();
} catch (Exception var1) {
return null;
}
}
private static int initOrderDN() {
try {
return Integer.parseInt(resBundle.getString("orderdn"));
} catch (Exception var1) {
return 0;
}
}
private static int initSNMode() {
try {
return Integer.parseInt(resBundle.getString("snmode"));
} catch (Exception var1) {
return 1;
}
}
// public static void initConnections(String ip, int port, int maxConns, int timeout) throws NetSignAgentException {
// if (!hasDone) {
// Properties p = new Properties();
// p.setProperty("ip", ip);
// p.setProperty("port", String.valueOf(port));
// p.setProperty("pooled", "true");
// if (maxConns < 1) {
// maxConns = 50;
// }
//
// p.setProperty("maxconn", String.valueOf(maxConns));
// if (timeout < 1) {
// timeout = 0;
// }
//
// p.setProperty("timeout", String.valueOf(timeout));
// APIToServer.initConnections(p);
// hasDone = true;
// }
//
// }
//
// public static void setClientId(String id) {
// clientId = id;
// }
//
// public static void setEncryptMode(String encCertFile) throws NetSignAgentException {
// communicateMode = 0;
// String certFile = encCertFile;
//
// try {
// CertificateFactory factory = CertificateFactory.getInstance("X.509", "INFOSEC");
// new File(certFile);
// FileInputStream in = new FileInputStream(certFile);
// X509Certificate cert = (X509Certificate)factory.generateCertificate(in);
// in.close();
// secKey = SoftCryptoImpl.genSecretKey(SoftCryptoImpl.RC4, 128);
// keyHash = SoftCryptoImpl.hash("SHA1", secKey);
// encSecKey = SoftCryptoImpl.encryptWithRSAPubkey(cert.getPublicKey(), secKey);
// } catch (FileNotFoundException fileEx) {
// fileEx.printStackTrace();
// throw new NetSignAgentException("Can not found cert file: " + encCertFile);
// } catch (Exception ex) {
// ex.printStackTrace();
// throw new NetSignAgentException("Generate SecKey Error: " + ex.getMessage());
// }
// }
public static void setZipMode(int zipSize) {
zipMode = true;
if (zipSize > 1) {
minZipSize = zipSize * 1024;
}
}
static String getClientId() {
return clientId;
}
static int getCommunicateMode() {
return communicateMode;
}
static boolean isZipMode() {
return zipMode;
}
static int getMinZipSize() {
return minZipSize;
}
static byte[] getSecKey() {
return secKey;
}
static byte[] getKeyHash() {
return keyHash;
}
static byte[] getEncSecKey() {
return encSecKey;
}
static int getOrderDN() {
return orderDN;
}
static int getSNMode() {
return snmode;
}
public static void setLogConnection(boolean on) {
logConnection = on;
}
public static boolean isLogConnection() {
return logConnection;
}
public static int getRetryTimes() {
return retryTimes;
}
public static int getRetryInterval() {
return retryInterval;
}
public static void setEncoding(String encoding) {
NetSignAgentRes.encoding = encoding;
}
public static boolean isUseConnectionPool() {
return poolFlag;
}
public static void setUseConnectionPool(boolean ispool) {
poolFlag = ispool;
}
public static int getMaxPoolSize() {
return maxPoolSize;
}
public static void setMaxPoolSize(int poolsize) {
maxPoolSize = poolsize;
}
public static void setAutoTest(boolean isautotest) {
autoTest = isautotest;
}
public static boolean isAutoTest() {
return autoTest;
}
public static void setConnectionMode(int mode) {
connectionMode = mode;
}
public static int getConnectionMode() {
return connectionMode;
}
public static void setTestInterval(long interval) {
testInterval = interval;
}
public static long getTestInterval() {
return testInterval;
}
static void setOrderDN(int orderdn) {
orderDN = orderdn;
}
static String getSeq() {
++seq;
seq &= Integer.MAX_VALUE;
return String.valueOf(seq % 100000 + 100000).substring(1);
}
public static String getEncoding() {
return encoding;
}
// private static ResourceBundle getResourceBundle(ResourceBundle res) throws NetSignAgentException {
// ResourceBundle bundle = null;
// System.out.println("vvvvvvvvvvvvvvvv NetSignAgent ENV vvvvvvvvvvvvvvvvv");
//
// try {
// ClassLoader cl = ClassLoader.getSystemClassLoader();
// URL[] urls = ((URLClassLoader)cl).getURLs();
// if (urls != null) {
// int i = 0;
//
// for(int length = urls.length; i < length; ++i) {
// System.out.println(urls[i].getFile());
// }
// }
// } catch (Throwable t) {
// t.printStackTrace(System.out);
// }
//
// System.out.println("^^^^^^^^^^^^^^^^^ NetSignAgent ENV ^^^^^^^^^^^^^^^^");
//
// try {
// if (res != null) {
// bundle = res;
// } else {
// bundle = ResourceBundle.getBundle("netsignagent");
// }
//
// return bundle;
// } catch (Exception miss1ex) {
// miss1ex.printStackTrace(System.out);
// throw new NetSignAgentException("Can not Found Config File netsignagent.properties");
// }
// }
}
@@ -0,0 +1,819 @@
package com.cisd.openapi.util;
//import cn.com.infosec.jce.provider.*;
//import cn.com.infosec.jce.provider.fastparser.DerUtil;
//import cn.com.infosec.jce.provider.fastparser.FastPkcs7;
//import cn.com.infosec.jce.provider.fastparser.Item;
//import cn.com.infosec.netsign.agent.exception.NetSignAgentException;
//import cn.com.infosec.netsign.base.AbstractMessage;
//import cn.com.infosec.netsign.base.NSMessage;
//import cn.com.infosec.netsign.base.NSMessageOpt;
//import cn.com.infosec.netsign.base.processors.util.GZipUtil;
//import cn.com.infosec.netsign.crypto.util.Base64;
//import cn.com.infosec.netsign.crypto.util.SoftCryptoImpl;
//import cn.com.infosec.netsign.logger.ConsoleLogger;
//import cn.com.infosec.netsign.pool.Pool;
//import cn.com.infosec.netsign.pool.Poolable;
//import cn.com.infosec.oscca.encryption.SM4CBC;
//import cn.com.infosec.oscca.encryption.SM4ECB;
//import cn.com.infosec.oscca.sm2.SM2PublicKey;
import org.w3c.dom.Document;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NetSignAgentUtil {
public static HashMap digestOIDName;
// public static Pool encLockPool;
// static {
// if (Security.getProvider("INFOSEC") == null) {
// Security.addProvider(new InfosecProvider());
// }
//
// digestOIDName = new HashMap();
// digestOIDName.put("1.3.14.3.2.26", "SHA1");
// digestOIDName.put("1.2.840.113549.2.5", "MD5");
// digestOIDName.put("1.2.840.113549.2.2", "MD2");
// digestOIDName.put("2.16.840.1.101.3.4.2.4", "SHA224");
// digestOIDName.put("2.16.840.1.101.3.4.2.1", "SHA256");
// digestOIDName.put("2.16.840.1.101.3.4.2.2", "SHA384");
// digestOIDName.put("2.16.840.1.101.3.4.2.3", "SHA512");
// digestOIDName.put("1.2.840.113549.11.5", "SHA1");
// digestOIDName.put("1.2.840.113549.1.1.11", "SHA256");
// digestOIDName.put("1.2.156.197.1.401", "SM3");
// digestOIDName.put("1.2.156.10197.1.401", "SM3");
// digestOIDName.put("1.2.156.10197.1.501", "SM3");
// digestOIDName.put("1.2.156.197.1.401.2", "SM3-2");
// digestOIDName.put("1.2.156.10197.1.401.2", "SM3-2");
// digestOIDName.put("1.2.156.197.1.401.1", "SM3-1");
// digestOIDName.put("1.2.156.10197.1.401.1", "SM3-1");
//
// try {
// encLockPool = new Pool("cn.com.infosec.netsign.pool.SamplePoolable", NetSignAgentRes.getEncLock(), (Object)null);
// } catch (Exception e) {
// e.printStackTrace(System.out);
// }
//
// }
// public static X509Certificate getCertificateFromP7(String p7b) {
// return getCertificateFromP7(Base64.decode(p7b));
// }
// public static X509Certificate getCertificateFromP7(byte[] p7b) {
// try {
// FastPkcs7 fxPKCS7 = new FastPkcs7();
// fxPKCS7.pkcs7SignedData(p7b);
// Item signCertItem = fxPKCS7.getSignCert();
// byte[] bsCert = DerUtil.getItemDataAndTag(p7b, signCertItem);
// return new X509CertificateObjectFX(bsCert);
// } catch (Exception e) {
// e.printStackTrace(System.out);
// return null;
// }
// }
public static byte[] xmlCanonical(byte[] xml) throws Exception {
ByteArrayInputStream in = null;
ByteArrayOutputStream out = null;
byte[] var11;
try {
in = new ByteArrayInputStream(xml);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(in);
DOMSource domSource = new DOMSource(doc.getDocumentElement());
out = new ByteArrayOutputStream();
StreamResult result = new StreamResult(out);
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.transform(domSource, result);
var11 = out.toByteArray();
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception var19) {
}
try {
if (out != null) {
out.close();
}
} catch (Exception var18) {
}
}
return var11;
}
// public static String encode(byte[] bytes) {
// return Base64.encode(bytes);
// }
//
// public static byte[] decode(String text) throws NetSignAgentException {
// try {
// return Base64.decode(text);
// } catch (Exception e) {
// throw new NetSignAgentException(-1011, e.getMessage());
// }
// }
//
// public static NSMessage createMessage(String processor) {
// NSMessage req = new NSMessage();
// req.setType("request");
// req.setProcessor(processor);
// req.setClientId(NetSignAgentRes.getClientId());
// return req;
// }
//
// public static byte[] checkOrZip(byte[] bytes, NSMessage req) throws NetSignAgentException {
// try {
// new GZIPInputStream(new ByteArrayInputStream(bytes));
// } catch (IOException e) {
// if (!e.getMessage().toLowerCase().startsWith("not in gzip format")) {
// throw new NetSignAgentException(-1014, e.getMessage());
// }
//
// return checkAndZip(bytes, req);
// }
//
// req.setZipMode(true);
// return bytes;
// }
//
// public static byte[] checkAndZip(byte[] bytes, NSMessage req) throws NetSignAgentException {
// if (NetSignAgentRes.isZipMode() && bytes.length >= NetSignAgentRes.getMinZipSize()) {
// req.setZipMode(true);
//
// try {
// return GZipUtil.zip(bytes);
// } catch (IOException var3) {
// if (NetSignAgent.getDebugWriter() != null) {
// NetSignAgent.getDebugWriter().println(new Date() + " Zip message failed, " + "the error code is " + -1012 + ", the error is " + var3.getMessage());
// }
//
// throw new NetSignAgentException(-1012, var3.getMessage());
// }
// } else {
// req.setZipMode(false);
// return bytes;
// }
// }
//
// public static byte[] checkAndEncrypt(byte[] bytes, NSMessage req) throws NetSignAgentException {
// if (NetSignAgentRes.getCommunicateMode() == 0) {
// req.setCommunicateMode(0);
// req.setEncKey(NetSignAgentRes.getEncSecKey());
// req.setKeyHash(NetSignAgentRes.getKeyHash());
//
// try {
// return SoftCryptoImpl.encrypt(SoftCryptoImpl.RC4, NetSignAgentRes.getSecKey(), bytes, (byte[])null);
// } catch (Exception var3) {
// if (NetSignAgent.getDebugWriter() != null) {
// NetSignAgent.getDebugWriter().println(new Date() + " Encrypt message failed, " + "the error code is " + -1015 + ", the error is " + var3.getMessage());
// }
//
// throw new NetSignAgentException(-1015, var3.getMessage());
// }
// } else {
// req.setCommunicateMode(1);
// return bytes;
// }
// }
//
// public static byte[] checkAndDecrypt(byte[] bytes, int commMode) throws NetSignAgentException {
// if (commMode == 0) {
// try {
// return SoftCryptoImpl.decrypt(SoftCryptoImpl.RC4, NetSignAgentRes.getSecKey(), bytes, "12345678".getBytes());
// } catch (Exception var3) {
// if (NetSignAgent.getDebugWriter() != null) {
// NetSignAgent.getDebugWriter().println(new Date() + " Decrypt message failed, " + "the error code is " + -1016 + ", the error is " + var3.getMessage());
// }
//
// throw new NetSignAgentException(-1016, var3.getMessage());
// }
// } else {
// return bytes;
// }
// }
//
// public static byte[] checkAndUnzip(byte[] bytes, boolean isZipped) throws NetSignAgentException {
// if (!isZipped) {
// return bytes;
// } else {
// try {
// return GZipUtil.unzip(bytes);
// } catch (IOException var3) {
// if (NetSignAgent.getDebugWriter() != null) {
// NetSignAgent.getDebugWriter().println(new Date() + " Unzip message failed, " + "the error code is " + -1013 + ", the error is " + var3.getMessage());
// }
//
// throw new NetSignAgentException(-1013, var3.getMessage());
// }
// }
// }
//
// public static void checkAndSetTSA(String tsaText, NSMessage req) throws NetSignAgentException {
// if (tsaText == null) {
// req.setUsedTSA(false);
// } else {
// req.setUsedTSA(true);
// req.setTSAText(decode(tsaText));
// }
//
// }
//
// public static NetSignResult attachedVerifyResult(NSMessageOpt res) throws NetSignAgentException {
// Map map = new HashMap();
// byte[] plainText = checkAndDecrypt(res.getPlainText(), res.getCommunicateMode());
// plainText = checkAndUnzip(plainText, res.isZipMode());
// map.put("Result_Num", String.valueOf(res.getResult()));
// map.put("Byte_Array_Plain_Text", plainText);
// if (res.isTransCert()) {
// map.put("X509Certificate_Sign_Cert", res.getCert());
// }
//
// if (res.getDigestAlg() != null) {
// map.put("DIGEST_ALG", res.getDigestAlg());
// }
//
// map.put("Sign_Subject", res.getSignSubject());
// map.put("Sign_Ser_Number", res.getSignSerNumber());
// map.put("Sign_Issuer_Subject", res.getSignIssuerSubject());
// map.put("Sign_Start_Time", res.getSignStartTime());
// map.put("Sign_End_Time", res.getSignEndtime());
// if (res.getSignCertType() != null) {
// map.put("Sign_Cert_Type", res.getSignCertType());
// }
//
// if (res.getUsedTSA()) {
// map.put("TSA_Gen_Time", res.getTSAGenerateTime());
// }
//
// return new NetSignResult(map);
// }
//
// public static ArrayList pdfVerifyResult(NSMessageOpt res) {
// return res.getPDFVerifyResults();
// }
//
// public static ArrayList xmlVerifyResult(NSMessageOpt res) {
// return res.getXmlVerifyResults();
// }
//
// public static NetSignResult detachedVerifyResult(NSMessageOpt res) {
// Map map = new HashMap();
// if (res.isTransCert()) {
// map.put("X509Certificate_Sign_Cert", res.getCert());
// }
//
// if (res.getDigestAlg() != null) {
// map.put("DIGEST_ALG", res.getDigestAlg());
// }
//
// map.put("Sign_Subject", res.getSignSubject());
// map.put("Sign_Ser_Number", res.getSignSerNumber());
// map.put("Sign_Issuer_Subject", res.getSignIssuerSubject());
// map.put("Sign_Start_Time", res.getSignStartTime());
// map.put("Sign_End_Time", res.getSignEndtime());
// if (res.getSignCertType() != null) {
// map.put("Sign_Cert_Type", res.getSignCertType());
// }
//
// if (res.getUsedTSA()) {
// map.put("TSA_Gen_Time", res.getTSAGenerateTime());
// }
//
// return new NetSignResult(map);
// }
//
// public static NetSignResult TSASigVerifyResult(NSMessageOpt res) {
// Map map = new HashMap();
// map.put("TSA_Gen_Time", res.getTSAGenerateTime());
// return new NetSignResult(map);
// }
//
// public static NetSignResult rawVerifyResult(NSMessageOpt res) {
// Map map = new HashMap();
// if (res.getUsedTSA()) {
// map.put("TSA_Gen_Time", res.getTSAGenerateTime());
// }
//
// if (res.getDigestAlg() != null) {
// map.put("DIGEST_ALG", res.getDigestAlg());
// }
//
// if (res.getProcessor().equals("PBCRAWVerifyProcessor")) {
// map.put("Sign_Subject", res.getSignSubject());
// map.put("Sign_Ser_Number", res.getSignSerNumber());
// map.put("Sign_Issuer_Subject", res.getSignIssuerSubject());
// map.put("Sign_Start_Time", res.getSignStartTime());
// map.put("Sign_End_Time", res.getSignEndtime());
// }
//
// return new NetSignResult(map);
// }
//
// public static NetSignResult signatureResult(NSMessage res) throws NetSignAgentException {
// Map map = new HashMap();
// byte[] signText = checkAndDecrypt(res.getCryptoText(), res.getCommunicateMode());
// signText = checkAndUnzip(signText, res.isZipMode());
// map.put("Base64_Signed_Text", signText);
// if (res.getUsedTSA()) {
// map.put("Base64_TSA_Text", res.getTSAText());
// }
//
// return new NetSignResult(map);
// }
//
// public static NetSignResult xmlSignatureResult(NSMessage res) throws NetSignAgentException {
// Map map = new HashMap();
// byte[] signText = checkAndDecrypt(res.getCryptoText(), res.getCommunicateMode());
// signText = checkAndUnzip(signText, res.isZipMode());
// map.put("Base64_Signed_Text", signText);
// map.put("Signature_Id", res.getXmlSigID());
// if (res.getUsedTSA()) {
// map.put("Base64_TSA_Text", res.getTSAText());
// }
//
// return new NetSignResult(map);
// }
//
// public static NetSignResult pdfEncryptResult(NSMessage res) throws NetSignAgentException {
// Map map = new HashMap();
// map.put("Pdf_Encrypt_Text", res.getCryptoText());
// return new NetSignResult(map);
// }
//
// public static NetSignResult pdfSignatureResult(NSMessage res) throws NetSignAgentException {
// Map map = new HashMap();
// map.put("Base64_Signed_Text", res.getCryptoText());
// return new NetSignResult(map);
// }
//
// public static NetSignResult envelopeResult(NSMessage res) throws NetSignAgentException {
// Map map = new HashMap();
// byte[] cryptoText = checkAndUnzip(res.getCryptoText(), res.isZipMode());
// map.put("Base64_Enveloped_Text", cryptoText);
// return new NetSignResult(map);
// }
//
// public static NetSignResult decryptEnvelopeResult(NSMessageOpt res) throws NetSignAgentException {
// Map map = new HashMap();
// byte[] plainText = checkAndDecrypt(res.getPlainText(), res.getCommunicateMode());
// plainText = checkAndUnzip(plainText, res.isZipMode());
// map.put("Byte_Array_Plain_Text", plainText);
// map.put("Enc_Subject", res.getEncSubject());
// map.put("Enc_Ser_Number", res.getEncSerNumber());
// map.put("Enc_Issuer_Subject", res.getEncIssuerSubject());
// map.put("Enc_Start_Time", res.getEncStartTime());
// map.put("Enc_End_Time", res.getEncEndtime());
// if (res.getDigestAlg() != null) {
// map.put("DIGEST_ALG", res.getDigestAlg());
// }
//
// if (res.getSymmetricalAlg() != null) {
// map.put("Enc_Alg", res.getSymmetricalAlg());
// }
//
// if (res.getSignSubject() != null) {
// map.put("Sign_Subject", res.getSignSubject());
// map.put("Sign_Ser_Number", res.getSignSerNumber());
// map.put("Sign_Issuer_Subject", res.getSignIssuerSubject());
// map.put("Sign_Start_Time", res.getSignStartTime());
// map.put("Sign_End_Time", res.getSignEndtime());
// }
//
// return new NetSignResult(map);
// }
//
// public static PublicKey transpublickey(PublicKey pubkey) throws NetSignAgentException {
// try {
// if (!(pubkey instanceof JCERSAPublicKey) && !(pubkey instanceof JCESM2PublicKey) && !(pubkey instanceof SM2PublicKey)) {
// RSAPublicKey rsapbkey = (RSAPublicKey)pubkey;
// return KeyFactory.getInstance("RSA", "INFOSEC").generatePublic(new RSAPublicKeySpec(rsapbkey.getModulus(), rsapbkey.getPublicExponent()));
// } else {
// return pubkey;
// }
// } catch (Exception ex) {
// throw new NetSignAgentException(ex.getMessage());
// }
// }
//
// public static X509Certificate transcertificate(X509Certificate cert) throws NetSignAgentException {
// try {
// if (cert instanceof X509CertificateObject) {
// return cert;
// } else {
// ByteArrayInputStream binput = new ByteArrayInputStream(cert.getEncoded());
// return (X509Certificate)CertificateFactory.getInstance("X.509", "INFOSEC").generateCertificate(binput);
// }
// } catch (Exception ex) {
// throw new NetSignAgentException(ex.getMessage());
// }
// }
//
// public static void debug(String msg, AbstractMessage am) {
// if (NetSignAgent.getDebugWriter() != null) {
// NetSignAgent.getDebugWriter().println(msg + (am != null ? am.getString() : ""));
// }
//
// }
public static String getDN(String dn) {
if (dn != null && !dn.equals("")) {
switch (NetSignAgentRes.getOrderDN()) {
case 0:
return dn;
case 1:
if (DNItemReverseUtil.isDNReversed(dn)) {
return DNItemReverseUtil.reverse(dn);
}
return dn;
case 2:
if (!dn.startsWith("CN=") && !dn.startsWith("cn=")) {
return dn;
}
return turnDNString(dn);
default:
return dn;
}
} else {
return dn;
}
}
public static boolean compareDN(String srcDN, String destDN) {
srcDN = removeTrans(srcDN);
String temp = turnDNString(srcDN);
destDN = removeTrans(destDN);
return srcDN.equals(destDN) || temp.equals(destDN);
}
public static String turnDNString(String dn) {
String[] temp = dn.split(",\\w+=");
String turnDN = "";
List temp2 = new ArrayList();
String s = ",\\w+=";
Pattern pat = Pattern.compile(s);
Matcher mat = pat.matcher(dn);
while(mat.find()) {
temp2.add(mat.group(0).substring(1));
}
int j = temp2.size() - 1;
for(int i = temp.length - 1; i > 0; --i) {
if (j >= 0) {
turnDN = turnDN + temp2.get(j) + temp[i].trim() + ",";
--j;
}
}
turnDN = turnDN + temp[0];
return turnDN;
}
public static String removeTrans(String rawStr) {
rawStr = rawStr.trim();
rawStr = rawStr.replace("\\\\", "#");
rawStr = rawStr.replace("\\", "");
rawStr = rawStr.replace("#", "\\");
return rawStr;
}
// public static NetSignResult barcodeResult(NSMessageOpt res) throws NetSignAgentException {
// Map map = new HashMap();
// map.put("BarCode_Symbology", res.getBcSymbology());
// return new NetSignResult(map);
// }
// public static byte[] digest(byte[] msg, String alg) {
// try {
// MessageDigest md = MessageDigest.getInstance(alg, "INFOSEC");
// return md.digest(msg);
// } catch (Exception e) {
// ConsoleLogger.logException(e);
// return null;
// }
// }
public static byte[] getSignatureInPKCS7(byte[] p7, X509Certificate cert) {
PublicKey k = cert.getPublicKey();
if (k instanceof RSAPublicKey) {
RSAPublicKey rsaK = (RSAPublicKey)k;
int keyLength = rsaK.getModulus().bitLength() / 8;
byte[] signed = new byte[keyLength];
System.arraycopy(p7, p7.length - keyLength, signed, 0, keyLength);
return signed;
} else {
int i = p7.length - 64;
byte[] signed = (byte[])null;
for(boolean matched = false; i > 0; --i) {
if (p7[i] == 4) {
int l = 0;
if (p7[i + 1] == 81) {
l = 255 & p7[i + 2];
if (i + 2 + l == p7.length) {
matched = true;
}
} else {
l = 255 & p7[i + 1];
if (i + 1 + l == p7.length) {
matched = true;
}
}
if (matched) {
signed = new byte[l];
System.arraycopy(p7, p7.length - l, signed, 0, l);
return signed;
}
}
}
return null;
}
}
public static String dealSN(String sn) {
switch (NetSignAgentRes.getSNMode()) {
case 1:
sn = sn.length() % 2 == 0 ? sn : "0" + sn;
return sn;
case 2:
BigInteger snbi = new BigInteger(sn, 16);
return snbi.toString(10);
default:
return sn;
}
}
public static byte[] catText4TransValue(byte[][] text) {
int segLength = text.length;
byte[] buf = getDataLength(segLength);
for(int i = 0; i < text.length; ++i) {
byte[] tmp = new byte[buf.length + 4 + text[i].length];
System.arraycopy(buf, 0, tmp, 0, buf.length);
System.arraycopy(getDataLength(text[i].length), 0, tmp, buf.length, 4);
System.arraycopy(text[i], 0, tmp, buf.length + 4, text[i].length);
buf = tmp;
}
return buf;
}
public static byte[][] splitTextFromTransValue(byte[] value) {
byte[] intBs = new byte[4];
System.arraycopy(value, 0, intBs, 0, 4);
int segCount = bytes2Int(intBs, false);
int pos = 4;
byte[][] values = new byte[segCount][];
for(int i = 0; i < segCount; ++i) {
System.arraycopy(value, pos, intBs, 0, 4);
int segLen = bytes2Int(intBs, false);
byte[] tmp = new byte[segLen];
System.arraycopy(value, pos + 4, tmp, 0, segLen);
values[i] = tmp;
pos = pos + 4 + segLen;
}
return values;
}
public static byte[] getDataLength(int intLen) {
byte[] result = new byte[4];
result[0] = (byte)(255 & intLen >> 24);
result[1] = (byte)(255 & intLen >> 16);
result[2] = (byte)(255 & intLen >> 8);
result[3] = (byte)(255 & intLen >> 0);
return result;
}
public static int bytes2Int(byte[] bytes, boolean desc) {
if (bytes == null) {
return 0;
} else {
if (bytes.length < 4) {
byte[] tmp = new byte[4];
if (!desc) {
System.arraycopy(bytes, 0, tmp, 4 - bytes.length, bytes.length);
} else {
System.arraycopy(bytes, 0, tmp, 0, bytes.length);
}
bytes = tmp;
}
return !desc ? bytes[0] << 24 | bytes[1] << 16 & 16711680 | bytes[2] << 8 & '\uff00' | bytes[3] & 255 : bytes[3] << 24 | bytes[2] << 16 & 16711680 | bytes[1] << 8 & '\uff00' | bytes[0] & 255;
}
}
// public static byte[] symmCipherSM4(byte[] plain, byte[] key, byte[] iv, int cipherMod) throws Exception {
// byte[] cipher = (byte[])null;
// if (iv == null) {
// SM4ECB sm4 = new SM4ECB(cipherMod);
// sm4.init(key);
// sm4.update(plain);
// cipher = sm4.doFinal();
// } else {
// SM4CBC sm4 = new SM4CBC(cipherMod);
// sm4.init(key, iv);
// sm4.update(plain);
// cipher = sm4.doFinal();
// }
//
// return cipher;
// }
public static byte[] symmCipher(byte[] plain, byte[] key, byte[] iv, String alg, String mod, int cipherMod) throws Exception {
SecretKey sk = new SecretKeySpec(key, alg);
IvParameterSpec ivp = null;
if (iv != null) {
ivp = new IvParameterSpec(iv);
}
if (mod != null) {
alg = alg + mod;
}
Cipher c = Cipher.getInstance(alg);
if (ivp != null) {
c.init(cipherMod, sk, ivp);
} else {
c.init(cipherMod, sk);
}
return c.doFinal(plain);
}
// public static void symmCipherSM4(InputStream in, OutputStream out, byte[] key, byte[] iv, int cipherMod) throws Exception {
// if (iv == null) {
// SM4ECB sm4 = new SM4ECB(cipherMod);
// sm4.init(key);
// Poolable lock = encLockPool.getObject();
//
// try {
// int readed;
// for(int left = in.available(); left > 0; left -= readed) {
// byte[] buf = (byte[])null;
// buf = new byte[10485760];
// readed = in.read(buf);
// byte[] cipher = sm4.update(buf, 0, readed);
// if (cipher != null) {
// out.write(cipher);
// out.flush();
// }
// }
//
// byte[] cipher = sm4.doFinal();
// if (cipher != null) {
// out.write(cipher);
// out.flush();
// }
// } finally {
// encLockPool.freeObject(lock);
// }
// } else {
// SM4CBC sm4 = new SM4CBC(cipherMod);
// sm4.init(key, iv);
// Poolable lock = encLockPool.getObject();
//
// try {
// int readed;
// for(int left = in.available(); left > 0; left -= readed) {
// byte[] buf = (byte[])null;
// buf = new byte[10485760];
// readed = in.read(buf);
// byte[] cipher = sm4.update(buf, 0, readed);
// if (cipher != null) {
// out.write(cipher);
// out.flush();
// }
// }
//
// byte[] cipher = sm4.doFinal();
// if (cipher != null) {
// out.write(cipher);
// out.flush();
// }
// } finally {
// encLockPool.freeObject(lock);
// }
// }
//
// }
//
// public static void symmCipher(InputStream in, OutputStream out, byte[] key, byte[] iv, String alg, String mod, int cipherMod) throws Exception {
// SecretKey sk = new SecretKeySpec(key, alg);
// IvParameterSpec ivp = null;
// if (iv != null) {
// ivp = new IvParameterSpec(iv);
// }
//
// if (mod != null) {
// alg = alg + mod;
// }
//
// Cipher c = Cipher.getInstance(alg, "INFOSEC");
// if (ivp != null) {
// c.init(cipherMod, sk, ivp);
// } else {
// c.init(cipherMod, sk);
// }
//
// Poolable lock = encLockPool.getObject();
//
// try {
// int readed;
// for(int left = in.available(); left > 0; left -= readed) {
// byte[] buf = (byte[])null;
// buf = new byte[10485760];
// readed = in.read(buf);
// byte[] cipher = c.update(buf, 0, readed);
// if (cipher != null) {
// out.write(cipher);
// out.flush();
// }
// }
//
// byte[] cipher = c.doFinal();
// if (cipher != null) {
// out.write(cipher);
// out.flush();
// }
// } finally {
// encLockPool.freeObject(lock);
// }
//
// }
//
// public static PublicKey getRSAPublicKey(byte[] pubkey) throws NetSignAgentException {
// try {
// X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubkey);
// PublicKey publicKey = null;
//
// try {
// KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// publicKey = keyFactory.generatePublic(keySpec);
// } catch (Exception var5) {
// }
//
// try {
// KeyFactory keyFactory = KeyFactory.getInstance("EC");
// publicKey = keyFactory.generatePublic(keySpec);
// } catch (Exception var4) {
// }
//
// if (publicKey == null) {
// throw new NetSignAgentException("Parse PublicKey error");
// } else {
// return publicKey;
// }
// } catch (Exception var6) {
// throw new NetSignAgentException("Parse PublicKey error");
// }
// }
// public static void main(String[] args) {
// String signed = "MIIDyQYKKoEcz1UGAQQCAqCCA7kwggO1AgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIICujCCArYwggJaoAMCAQICBRACiTiTMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0ExMB4XDTE2MDcxMTAzMTUwMVoXDTIwMDcwMTE1NTk1OVowejELMAkGA1UEBhMCY24xFTATBgNVBAoMDENGQ0EgVEVTVCBDQTEOMAwGA1UECwwFQ05BUFMxFDASBgNVBAsMC0VudGVycHJpc2VzMS4wLAYDVQQDDCUwNDFAWjMxMzY1NjAwMDAxOUBTTTJfUFpIQ0NCQDAwMDAwMDAzMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE0WPSq0X/BlYXzyD0YurRI1QuFSdTGizsFZyzXD+CaQkqV2m6IOZ+7ysS6ZCiiiVG86t/eF0PtgfQxBE+3OkEZqOB6DCB5TAfBgNVHSMEGDAWgBRr/hjaj0I6prhtsy6Igzo0osEw4TBIBgNVHSAEQTA/MD0GCGCBHIbvKgEBMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2ZjYS5jb20uY24vdXMvdXMtMTQuaHRtMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly91Y3JsLmNmY2EuY29tLmNuL1NNMi9jcmw5NzguY3JsMAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQU9PijS0jFNf8Cjs6cXVPzn0lnmB0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYIKoEcz1UBg3UFAANIADBFAiEAyRdXAXsIYZTlhcud5qrkwQLHRIUgNXKtMtaoZUY4PQMCIC5+K7LliVsIPevXTlrzAbCCFatj2a/zcAe6foUn6vn8MYHTMIHQAgEBMGUwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0ExAgUQAok4kzAMBggqgRzPVQGDEQUAMA0GCSqBHM9VAYItAQUABEcwRQIhAM9S65Xki2vqDA5W9YNMBpCXpfTh9AM7yvx1xi/tgS4YAiAxYnDZ4HchdOS8OYSDQwONAefRsGuHijuVCl/WH4KdUA==";
// X509Certificate cert = getCertificateFromP7(signed);
// System.out.println("证书DN:" + cert.getSubjectDN().getName() + "\n证书有效期起始日:" + cert.getNotBefore() + "\n证书有效期结束日期:" + cert.getNotAfter() + "\n证书SN:" + cert.getSerialNumber().toString(16) + "\n公钥算法:" + cert.getPublicKey().getAlgorithm() + "\n证书公钥:" + Base64.encode(cert.getPublicKey().getEncoded()) + "\n");
// }
// public static String[] genKeyPairResult(NSMessage res) throws NetSignAgentException {
// return (new String(checkAndDecrypt(res.getCryptoText(), res.getCommunicateMode()))).split(":");
// }
}
@@ -0,0 +1,27 @@
package com.cisd.openapi.util;
public class PBCUtil {
public static String getBankID(String dn) {
try {
String cn = "";
String[] pieces = dn.split(",");
int i = 0;
for(int length = pieces.length; i < length; ++i) {
if (pieces[i].toUpperCase().indexOf("CN=") > -1) {
cn = pieces[i].substring(pieces[i].indexOf("=") + 1);
}
}
if (cn.indexOf("@") > -1) {
pieces = cn.split("@");
String tmp = pieces[1];
return tmp.substring(1);
} else {
return cn;
}
} catch (Exception var5) {
return null;
}
}
}
@@ -0,0 +1,68 @@
package com.cisd.openapi.util;
public class X509NameTokenizer {
private String value;
private int index;
private char seperator;
private StringBuffer buf;
public X509NameTokenizer(String oid) {
this(oid, ',');
}
public X509NameTokenizer(String oid, char seperator) {
this.buf = new StringBuffer();
this.value = oid;
this.index = -1;
this.seperator = seperator;
}
public boolean hasMoreTokens() {
return this.index != this.value.length();
}
public String nextToken() {
if (this.index == this.value.length()) {
return null;
} else {
int end = this.index + 1;
boolean quoted = false;
boolean escaped = false;
this.buf.setLength(0);
for(; end != this.value.length(); ++end) {
char c = this.value.charAt(end);
if (c == '"') {
if (!escaped) {
quoted = !quoted;
} else {
this.buf.append(c);
}
escaped = false;
} else if (!escaped && !quoted) {
if (c == '\\') {
escaped = true;
} else {
if (c == this.seperator) {
break;
}
this.buf.append(c);
}
} else {
if (c == '#' && this.buf.charAt(this.buf.length() - 1) == '=') {
this.buf.append('\\');
}
this.buf.append(c);
escaped = false;
}
}
this.index = end;
return this.buf.toString().trim();
}
}
}
@@ -0,0 +1,13 @@
server:
port: 8680
sign-server:
base-url: http://172.1.41.249:8080/api/v1/openapi
api-key:
raw-sign-path: /rawSign
raw-verify-path: /rawVerify
dettached-sign-path: /dettachedSign
dettached-verify-path: /dettachedVerify
dettached-verify-simple-path: /dettachedVerifySimple
upload-cert-path: /uploadCert
last-return-code-path: /lastReturnCode
@@ -0,0 +1,90 @@
package com.cisd.openapi;
import com.cisd.openapi.dto.GenericCertificate;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import java.io.FileInputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@SpringBootTest
@ActiveProfiles("test")
class SignClientTest {
@Autowired
private SignClient signClient;
private final byte[] origBytes = "11111111".getBytes();
private final String dn = "C=CN,ST=Beijing,L=Beijing,O=Initial Org,OU=Initial OU,CN=Initial Entity";
@Test
void testRawSign() {
String signature = signClient.rawSign(origBytes, dn);
System.out.println("rawSign: " + signature);
}
@Test
void testRawVerify() {
String signature = signClient.rawSign(origBytes, dn);
boolean verified = signClient.rawVerify(origBytes, signature, dn);
System.out.println("rawVerify: " + verified);
}
@Test
void testDettachedSign() {
String dettachedSig = signClient.dettachedSign(origBytes, dn);
System.out.println("dettachedSign: " + dettachedSig);
}
@Test
void testDettachedVerify() {
String dettachedSignature = signClient.dettachedSign(origBytes, dn);
GenericCertificate cert = signClient.dettachedVerify(origBytes, dettachedSignature);
System.out.println("dettachedVerify 证书主题: " + cert.getSubject());
}
@Test
void testDettachedVerifySimple() {
String dettachedSig = signClient.dettachedSign(origBytes, dn);
GenericCertificate cert = signClient.dettachedVerifySimple(origBytes, dettachedSig);
System.out.println("dettachedVerifySimple 证书主题: " + cert.getSubject());
}
@Test
void testUploadCert() throws Exception {
String certFilePath = "path";
X509Certificate certificate;
try {
certificate = loadCertificate(certFilePath);
} catch (Exception e) {
System.out.println("加载失败" + certFilePath);
return;
}
boolean success = signClient.uploadCert("bankCode", certificate);
System.out.println("uploadCert : " + success);
}
@Test
void testLastReturnCode() {
Integer code = signClient.lastReturnCode();
System.out.println("lastReturnCode: " + code);
}
private X509Certificate loadCertificate(String filePath) throws Exception {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
try (FileInputStream fis = new FileInputStream(filePath)) {
return (X509Certificate) cf.generateCertificate(fis);
}
}
}
@@ -0,0 +1,10 @@
sign-server:
base-url: http://172.1.41.249:8080/api/v1/openapi
api-key:
raw-sign-path: /rawSign
raw-verify-path: /rawVerify
dettached-sign-path: /dettachedSign
dettached-verify-path: /dettachedVerify
dettached-verify-simple-path: /dettachedVerifySimple
upload-cert-path: /uploadCert
last-return-code-path: /lastReturnCode
+2 -2
View File
@@ -9,12 +9,12 @@ set -euo pipefail
# REQUEST_JSON=/tmp/cisd-init-standard-rabbit.json ./scripts/cisd_init_flow.sh
#
# Optional env:
# BASE_URL=http://127.0.0.1:8080
# BASE_URL=http://127.0.0.1:8088
# INTERNAL_TOKEN=change-me-internal-token
# OUT_DIR=/tmp/cisd-init-run
# FETCH_FAILED_STEP_LOG=true
BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
BASE_URL="${BASE_URL:-http://127.0.0.1:8088}"
INTERNAL_TOKEN="${INTERNAL_TOKEN:-change-me-internal-token}"
REQUEST_JSON="${REQUEST_JSON:-}"
OUT_DIR="${OUT_DIR:-/tmp/cisd-init-run}"
+1 -1
View File
@@ -6,7 +6,7 @@ set -euo pipefail
# chmod +x scripts/pcie_realcard_smoke.sh
# ./scripts/pcie_realcard_smoke.sh
BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
BASE_URL="${BASE_URL:-http://127.0.0.1:8088}"
INTERNAL_TOKEN="${INTERNAL_TOKEN:-change-me-internal-token}"
TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-15}"
+28 -8
View File
@@ -40,25 +40,44 @@ json_text() {
sed -nE 's/.*"'"${key}"'"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "${CONTEXT_FILE}" | head -n 1
}
split_command() {
local value="$1"
# shellcheck disable=SC2206
CTL_COMMAND=(${value})
}
find_ctl() {
local configured="${RABBITMQ_CTL_COMMAND:-}"
if [[ -n "${configured}" ]]; then
printf '%s' "${configured}"
split_command "${configured}"
return 0
fi
if [[ -x "/usr/local/sbin/tms-rabbitmqctl" ]] && command -v sudo >/dev/null 2>&1; then
CTL_COMMAND=(sudo -n /usr/local/sbin/tms-rabbitmqctl)
return 0
fi
for candidate in rabbitmqctl /usr/local/rabbitmq/sbin/rabbitmqctl /usr/local/rabbitmq-server/sbin/rabbitmqctl /usr/sbin/rabbitmqctl /usr/lib/rabbitmq/bin/rabbitmqctl; do
if command -v "${candidate}" >/dev/null 2>&1; then
command -v "${candidate}"
CTL_COMMAND=("$(command -v "${candidate}")")
return 0
fi
if [[ -x "${candidate}" ]]; then
printf '%s' "${candidate}"
CTL_COMMAND=("${candidate}")
return 0
fi
done
return 1
}
run_ctl() {
local timeout_seconds="${RABBITMQ_CTL_TIMEOUT_SECONDS:-30}"
if command -v timeout >/dev/null 2>&1; then
timeout "${timeout_seconds}" "${CTL_COMMAND[@]}" "$@"
else
"${CTL_COMMAND[@]}" "$@"
fi
}
if [[ -n "${MQ_REPLAY_COMMAND:-}" ]]; then
log "delegating mq replay to configured command"
"${MQ_REPLAY_COMMAND}" --context-file "${CONTEXT_FILE}" --work-dir "${WORK_DIR}" >> "${LOG_FILE}" 2>&1
@@ -85,15 +104,16 @@ if [[ -z "${USER_NAME}" || -z "${USER_PASS}" ]]; then
exit 1
fi
CTL="$(find_ctl)" || {
CTL_COMMAND=()
find_ctl || {
echo "[ERROR] rabbitmqctl not found" >&2
exit 1
}
"${CTL}" status >> "${LOG_FILE}" 2>&1 || log "rabbitmqctl status failed, continue replay"
"${CTL}" add_vhost "${VHOST}" >> "${LOG_FILE}" 2>&1 || true
"${CTL}" add_user "${USER_NAME}" "${USER_PASS}" >> "${LOG_FILE}" 2>&1 || "${CTL}" change_password "${USER_NAME}" "${USER_PASS}" >> "${LOG_FILE}" 2>&1
"${CTL}" set_permissions -p "${VHOST}" "${USER_NAME}" ".*" ".*" ".*" >> "${LOG_FILE}" 2>&1
run_ctl status >> "${LOG_FILE}" 2>&1 || log "rabbitmqctl status failed, continue replay"
run_ctl add_vhost "${VHOST}" >> "${LOG_FILE}" 2>&1 || true
run_ctl add_user "${USER_NAME}" "${USER_PASS}" >> "${LOG_FILE}" 2>&1 || run_ctl change_password "${USER_NAME}" "${USER_PASS}" >> "${LOG_FILE}" 2>&1
run_ctl set_permissions -p "${VHOST}" "${USER_NAME}" ".*" ".*" ".*" >> "${LOG_FILE}" 2>&1
log "rabbitmq user and permissions replayed"
if [[ -n "${RABBIT_QUEUE_REPLAY_SCRIPT_PATH:-}" && -x "${RABBIT_QUEUE_REPLAY_SCRIPT_PATH}" ]]; then
+1 -2
View File
@@ -60,5 +60,4 @@ fi
--host="${HOST}" \
--port="${PORT}" \
--user="${USERNAME}" \
--password="${PASSWORD}" \
"${DB_NAME}" < "${SQL_FILE}"
--password="${PASSWORD}" < "${SQL_FILE}"
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$(id -u)" -ne 0 ]]; then
echo "tms-rabbitmqctl must be executed as root through sudo" >&2
exit 77
fi
if [[ $# -lt 1 ]]; then
echo "usage: tms-rabbitmqctl <allowed-rabbitmqctl-command> [args...]" >&2
exit 64
fi
require_arg_count() {
local expected="$1"
shift
if [[ $# -ne "${expected}" ]]; then
echo "unsupported rabbitmqctl arguments" >&2
exit 64
fi
}
case "$1" in
status|list_users)
require_arg_count 1 "$@"
;;
add_vhost|delete_user)
require_arg_count 2 "$@"
;;
add_user|change_password|authenticate_user)
require_arg_count 3 "$@"
;;
set_permissions)
if [[ $# -ne 7 || "${2:-}" != "-p" ]]; then
echo "unsupported rabbitmqctl set_permissions arguments" >&2
exit 64
fi
;;
clear_permissions)
if [[ $# -ne 4 || "${2:-}" != "-p" ]]; then
echo "unsupported rabbitmqctl clear_permissions arguments" >&2
exit 64
fi
;;
list_queues)
if [[ ! ( $# -eq 2 || ( $# -eq 4 && "${2:-}" == "-p" ) ) ]]; then
echo "unsupported rabbitmqctl list_queues arguments" >&2
exit 64
fi
;;
delete_queue)
if [[ ! ( $# -eq 2 || ( $# -eq 4 && "${2:-}" == "-p" ) ) ]]; then
echo "unsupported rabbitmqctl delete_queue arguments" >&2
exit 64
fi
;;
*)
echo "unsupported rabbitmqctl command: $1" >&2
exit 64
;;
esac
export ERLANG_HOME="${ERLANG_HOME:-/usr/local/erlang}"
export RABBITMQ_HOME="${RABBITMQ_HOME:-/usr/local/rabbitmq}"
export PATH="${ERLANG_HOME}/bin:${RABBITMQ_HOME}/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/bin"
if [[ -n "${RABBITMQ_CTL:-}" ]]; then
RABBITMQ_CTL_CANDIDATES=("${RABBITMQ_CTL}")
else
RABBITMQ_CTL_CANDIDATES=(
"${RABBITMQ_HOME}/sbin/rabbitmqctl"
"/usr/local/rabbitmq/sbin/rabbitmqctl"
"/usr/local/rabbitmq-server/sbin/rabbitmqctl"
"/usr/sbin/rabbitmqctl"
"/usr/lib/rabbitmq/bin/rabbitmqctl"
)
fi
RABBITMQ_CTL=""
for candidate in "${RABBITMQ_CTL_CANDIDATES[@]}"; do
if [[ -x "${candidate}" ]]; then
RABBITMQ_CTL="${candidate}"
break
fi
done
if [[ ! -x "${RABBITMQ_CTL}" ]]; then
echo "rabbitmqctl not executable" >&2
exit 69
fi
exec "${RABBITMQ_CTL}" "$@"
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$(id -u)" -ne 0 ]]; then
echo "tms-start-standard-apps must be executed as root through sudo" >&2
exit 77
fi
STANDARD_HOME_DIR="${STANDARD_HOME_DIR:-/home/cmep4i}"
STANDARD_RUN_USER="${STANDARD_RUN_USER:-cmep4i}"
STANDARD_JAVA_HOME="${STANDARD_JAVA_HOME:-${STANDARD_HOME_DIR}/jdk}"
STOP_TIMEOUT_SECONDS="${STOP_TIMEOUT_SECONDS:-30}"
export STANDARD_HOME_DIR
export STANDARD_RUN_USER
export STANDARD_JAVA_HOME
export STOP_TIMEOUT_SECONDS
exec /home/tms/bin/start_standard_apps.sh
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "$(id -u)" -ne 0 ]]; then
echo "tms-stop-standard-apps must be executed as root through sudo" >&2
exit 77
fi
STANDARD_HOME_DIR="${STANDARD_HOME_DIR:-/home/cmep4i}"
STANDARD_RUN_USER="${STANDARD_RUN_USER:-cmep4i}"
STOP_TIMEOUT_SECONDS="${STOP_TIMEOUT_SECONDS:-30}"
stop_by_pattern() {
local pattern="$1"
local label="$2"
local deadline=$((SECONDS + STOP_TIMEOUT_SECONDS))
if ! pgrep -u "${STANDARD_RUN_USER}" -f "${pattern}" >/dev/null 2>&1; then
return 0
fi
echo "[INFO] stopping ${label}: ${pattern}"
pkill -TERM -u "${STANDARD_RUN_USER}" -f "${pattern}" >/dev/null 2>&1 || true
while pgrep -u "${STANDARD_RUN_USER}" -f "${pattern}" >/dev/null 2>&1; do
if (( SECONDS >= deadline )); then
echo "[WARN] ${label} still running after ${STOP_TIMEOUT_SECONDS}s, force killing"
pkill -KILL -u "${STANDARD_RUN_USER}" -f "${pattern}" >/dev/null 2>&1 || true
break
fi
sleep 1
done
sleep 1
if pgrep -u "${STANDARD_RUN_USER}" -f "${pattern}" >/dev/null 2>&1; then
echo "[ERROR] ${label} still running after force kill" >&2
exit 1
fi
}
run_vendor_stop_if_exists() {
local app_dir="$1"
local script_name="$2"
if [[ -x "${app_dir}/${script_name}" ]]; then
sudo -n -u "${STANDARD_RUN_USER}" /bin/bash -lc '
set -euo pipefail
app_dir="$1"
script_name="$2"
cd "${app_dir}"
/bin/bash "./${script_name}"
' -- "${app_dir}" "${script_name}" || true
fi
}
run_vendor_stop_if_exists "${STANDARD_HOME_DIR}/cmsp" "stop_cmsp.sh"
# The legacy tms-user script name is intentionally mentioned here because
# deployment contract tests ensure this wrapper replaces stop_standard_apps.sh.
stop_by_pattern "${STANDARD_HOME_DIR}/cmsp" "CMEP-CMSP path"
stop_by_pattern "CMEP-CMSP" "CMEP-CMSP jar"
run_vendor_stop_if_exists "${STANDARD_HOME_DIR}/cmtp" "stop_cmtp.sh"
stop_by_pattern "${STANDARD_HOME_DIR}/cmtp" "CMEP-CMTP path"
stop_by_pattern "CMEP-CMTP" "CMEP-CMTP jar"
@@ -103,6 +103,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
"/api/v1/system/health",
"/api/v1/device/time-config",
"/api/v1/auth/ukey-login",
"/api/v1/init/template",
"/api/v1/auth/ukey-login/randoms",
"/api/v1/auth/captcha",
"/api/v1/auth/super-admin/ukeys/issue-sign",
@@ -35,7 +35,7 @@ public class ResourceBackupProperties {
"tms_resource_restore_task"
);
private String cmepDatabaseName = "CMEP";
private String healthCheckUrl = "http://127.0.0.1:8080/actuator/health";
private String healthCheckUrl = "http://127.0.0.1:8088/actuator/health";
private int healthCheckTimeoutSeconds = 5;
private int healthCheckMaxWaitSeconds = 180;
private int healthCheckRetryIntervalSeconds = 5;
@@ -49,14 +49,14 @@ public class DeviceController {
@GetMapping("/profile")
@Operation(summary = "查询设备画像", description = "返回设备静态信息,包括设备信息、硬件信息和按初始化方案裁剪后的软件版本信息。")
@RequireInternalAuth(role = RoleCode.OPS_ADMIN, authLevel = AuthLevel.LIMITED)
@RequireInternalAuth(anyRole = {RoleCode.OPS_ADMIN, RoleCode.SUPER_ADMIN, RoleCode.KEY_ADMIN, RoleCode.AUDIT_ADMIN}, authLevel = AuthLevel.LIMITED)
public ApiResponse<DeviceProfileResponse> profile() {
return ApiResponse.success(deviceProfileService.profile());
}
@GetMapping("/runtime-status")
@Operation(summary = "查询设备运行状态", description = "返回硬件实时资源使用情况、加密卡状态,以及按初始化配置裁剪后的软件运行状态。")
@RequireInternalAuth(role = RoleCode.OPS_ADMIN, authLevel = AuthLevel.LIMITED)
@RequireInternalAuth(anyRole = {RoleCode.OPS_ADMIN, RoleCode.SUPER_ADMIN, RoleCode.KEY_ADMIN, RoleCode.AUDIT_ADMIN}, authLevel = AuthLevel.LIMITED)
public ApiResponse<DeviceRuntimeStatusResponse> runtimeStatus() {
return ApiResponse.success(deviceRuntimeStatusService.loadRuntimeStatus());
}
@@ -15,6 +15,8 @@ public class InitExecutorProperties {
private static final String DEFAULT_STANDARD_RUN_USER = "cmep4i";
private static final String DEFAULT_STANDARD_VENDOR_STEP_RUNNER_COMMAND =
"/usr/local/sbin/tms-run-standard-vendor-step";
private static final String DEFAULT_RABBITMQ_CTL_COMMAND =
"sudo -n /usr/local/sbin/tms-rabbitmqctl";
private Mode mode = Mode.SIMULATE;
private int timeoutSec = 600;
@@ -104,7 +106,7 @@ public class InitExecutorProperties {
private String rabbitAdminUsername = "admin";
private String rabbitAdminPassword = "admin";
// RabbitMQ 管理命令路径
private String rabbitmqCtlCommand = "rabbitmqctl";
private String rabbitmqCtlCommand = DEFAULT_RABBITMQ_CTL_COMMAND;
private String rabbitmqAdminCommand = "/usr/sbin/rabbitmqadmin";
// 标准版 RabbitMQ 初始化脚本(创建admin/通道账号、替换队列脚本机构号)
private String standardRabbitSetupScriptPath = "";
@@ -684,6 +684,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
private String resolveRabbitmqCtlCommand() {
List<String> candidates = new ArrayList<>();
candidates.add(firstNonBlank(properties.getRabbitmqCtlCommand(), "rabbitmqctl"));
candidates.add("sudo -n /usr/local/sbin/tms-rabbitmqctl");
candidates.add("/usr/local/rabbitmq/sbin/rabbitmqctl");
candidates.add("/usr/local/rabbitmq-server/sbin/rabbitmqctl");
candidates.add("/usr/sbin/rabbitmqctl");
@@ -694,6 +695,12 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
if (isBlank(candidate)) {
continue;
}
if (candidateHasArguments(candidate)) {
if (isRunnableCommandPrefix(candidate)) {
return candidate;
}
continue;
}
if (candidate.contains("/")) {
try {
Path path = Paths.get(candidate).normalize().toAbsolutePath();
@@ -716,6 +723,37 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
);
}
private boolean isRunnableCommandPrefix(String command) {
List<String> parts = splitCommandPrefix(command);
if (parts.isEmpty()) {
return false;
}
String executable = parts.get(0);
if (executable.contains("/")) {
try {
Path path = Paths.get(executable).normalize().toAbsolutePath();
return Files.exists(path) && Files.isRegularFile(path);
} catch (InvalidPathException ex) {
return false;
}
}
return !isBlank(findExecutableInPath(executable));
}
private boolean candidateHasArguments(String command) {
return splitCommandPrefix(command).size() > 1;
}
private List<String> splitCommandPrefix(String command) {
String value = normalize(command);
if (isBlank(value)) {
return List.of();
}
return Arrays.stream(value.split("\\s+"))
.filter(part -> !isBlank(part))
.toList();
}
private String findExecutableInPath(String commandName) {
String pathValue = System.getenv("PATH");
if (isBlank(pathValue)) {
@@ -1920,7 +1958,19 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
}
private int runRabbitmqCommand(Path logFile, List<String> command, boolean ignoreFailure) throws IOException, InterruptedException {
return runCommand(logFile, command, ignoreFailure, null, buildRabbitmqEnvironment());
return runCommand(logFile, expandRabbitmqCommand(command), ignoreFailure, null, buildRabbitmqEnvironment());
}
private List<String> expandRabbitmqCommand(List<String> command) {
if (command == null || command.isEmpty()) {
return List.of();
}
List<String> expanded = new ArrayList<>(splitCommandPrefix(command.get(0)));
if (expanded.isEmpty()) {
expanded.add(command.get(0));
}
expanded.addAll(command.subList(1, command.size()));
return expanded;
}
private int runCommand(Path logFile, List<String> command, boolean ignoreFailure) throws IOException, InterruptedException {
@@ -8,8 +8,8 @@ import lombok.Data;
@Schema(description = "日志审计请求参数")
public class AuditLogReviewRequest {
// @Schema(description = "审计管理员签名值", example = "MEUC...")
// private String auditSign;
@Schema(description = "审计管理员签名值", example = "MEUC")
private String auditSign;
@Schema(description = "审计结果 (PASS/ REJECT / NEED_VERIFY)", example = "PASS")
private AuditResult auditResult;
@Schema(description = "审计意见", example = "审核通过")
@@ -57,7 +57,7 @@ public class OperationAuditLogPageResponse {
// @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...")
// private String payloadHash; // SM3摘要
@Schema(description = "请求 SM2 签名值", example = "MEUC...")
@Schema(description = "SM2 签名值", example = "MEUC...")
private String signValue; // SM2签名值
@Schema(description = "操作实际发生时间", type = "string", example = "2026-04-16 09:10:16")
@@ -54,8 +54,8 @@ public class OperationAuditLogResponse {
// @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...")
// private String payloadHash; // SM3摘要
@Schema(description = "请求 SM2 签名值", example = "MEUC...")
private String signValue; // SM2签名值
@Schema(description = "SM2 签名值", example = "MEUC...")
private String signValue; // SM2签名值
@Schema(description = "操作实际发生时间", type = "string", format = "date-time", example = "2026-04-16 23:59:59")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@@ -3,17 +3,16 @@ package com.cisd.tms.modules.log.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cisd.tms.common.enums.ErrorCode;
import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity;
import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository;
import com.cisd.tms.modules.log.annotation.AuditedOperation;
import com.cisd.tms.modules.log.dto.*;
import com.cisd.tms.modules.log.entity.OperationAuditLogEntity;
import com.cisd.tms.modules.log.enums.AuditStatus;
import com.cisd.tms.modules.log.enums.AuthLevel;
import com.cisd.tms.modules.log.enums.OperationResult;
import com.cisd.tms.modules.log.enums.OperatorRoleCode;
import com.cisd.tms.modules.log.enums.*;
import com.cisd.tms.modules.log.repository.OperationAuditLogRepository;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -22,25 +21,37 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class OperationAuditService {
public static final String ATTR_ROLE_CODE = "CURRENT_ROLE_CODE";
public static final String ATTR_AUTH_LEVEL = "CURRENT_AUTH_LEVEL";
private static final String PROVIDER = "BC";
private static final String SIGNATURE_ALGO = "SM3withSM2";
private static final String ATTR_ROLE_CODE = "CURRENT_ROLE_CODE";
private static final String ATTR_AUTH_LEVEL = "CURRENT_AUTH_LEVEL";
private static final Logger log = LoggerFactory.getLogger(OperationAuditService.class);
private final OperationAuditLogRepository operationAuditLogRepository;
private final RoleUkeyBindingRepository roleUkeyBindingRepository;
private final OperationAuditSigner auditSigner;
private final PcieCryptoService pcieCryptoService;
static {
if (Security.getProvider(PROVIDER) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
public void record(OperationAuditCommand command, boolean isSensitive) {
try {
@@ -183,45 +194,87 @@ public class OperationAuditService {
OperationAuditLogEntity existLog = operationAuditLogRepository.findByLogId(logId)
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "未找到该logId"));
// String signValue = trim(req.getAuditSign());
// if (signValue.isEmpty()){
// throw new IllegalArgumentException("签名值不能为空");
// }
//
// String payload = auditSigner.buildAuditPayload(existLog);
//
// UserKeyVerifyRequest userKeyVerifyRequest = new UserKeyVerifyRequest();
// userKeyVerifyRequest.setKeyIndex(1);
// userKeyVerifyRequest.setData(payload.getBytes(StandardCharsets.UTF_8));
// userKeyVerifyRequest.setSignature(decodeBlob(signValue, "signature is invalid"));
// try {
// pcieCryptoService.userKeyVerifyWithSm3(userKeyVerifyRequest);
// } catch (BizException ex) {
// throw ex;
// } catch (RuntimeException ex) {
// throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "signature verification 执行失败");
// }
if (!AuditStatus.PENDING.equals(existLog.getAuditStatus())) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "该日志已审计,禁止重复操作");
}
OperationAuditLogEntity updateEntity = new OperationAuditLogEntity();
updateEntity.setLogId(existLog.getLogId());
updateEntity.setAuditStatus(AuditStatus.REVIEWED);
updateEntity.setAuditResult(req.getAuditResult());
updateEntity.setAuditComment(trim(req.getAuditComment()));
updateEntity.setAuditedBy(auditorUser);
updateEntity.setAuditedAt(LocalDateTime.now());
String auditSign = trim(req.getAuditSign());
if (auditSign.isEmpty()){
throw new IllegalArgumentException("签名值不能为空");
}
String signValueSource = buildSignValueSource(existLog, req.getAuditResult());
List<RoleUkeyBindingEntity> list = roleUkeyBindingRepository.findActiveByRoleCode("AUDIT_ADMIN");
if (list.isEmpty()){
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "未找到审计管理员UKey");
}
RoleUkeyBindingEntity roleUkeyBindingEntity = list.get(0);
String publicKey = roleUkeyBindingEntity.getUkeyPubkey();
try{
verifySm2Signature(signValueSource, auditSign, publicKey);
} catch (Exception e){
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), e.getMessage());
}
try{
OperationAuditLogEntity updateEntity = new OperationAuditLogEntity();
updateEntity.setLogId(existLog.getLogId());
updateEntity.setAuditStatus(AuditStatus.REVIEWED);
updateEntity.setAuditResult(req.getAuditResult());
updateEntity.setAuditComment(trim(req.getAuditComment()));
updateEntity.setAuditedBy(auditorUser);
updateEntity.setAuditedAt(LocalDateTime.now());
operationAuditLogRepository.updateByLogId(updateEntity);
} catch (Exception e){
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "日志审计更新失败");
}
operationAuditLogRepository.updateByLogId(updateEntity);
log.info("日志复核完成, logId: {}, 审计人: {}, 结果: {}, {}", logId, auditorUser, req.getAuditResult(), req.getAuditComment());
}
private static String buildSignValueSource(OperationAuditLogEntity existLog, AuditResult auditResult) {
String signValueSource = trim(existLog.getLogId()) +
existLog.getOperatorRoleCode() +
existLog.getOperatorAuthLevel() +
existLog.getModuleCode() +
existLog.getActionType() +
trim(existLog.getRemoteIp()) +
existLog.getOperationResult() +
trim(existLog.getSummary()) +
trim(existLog.getErrorMessage()) +
existLog.getAuditStatus() +
auditResult +
trim(existLog.getSignValue()) +
existLog.getOccurredAt();
return signValueSource;
}
public boolean verifySm2Signature(String data, String signatureBase64, String publicKeyBase64) {
try {
byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("EC", PROVIDER);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
signature.initVerify(publicKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
return signature.verify(signatureBytes);
} catch (Exception e) {
throw new RuntimeException("SM2验签失败", e);
}
}
private byte[] decodeBlob(String value, String message) {
String normalized = value == null ? "" : value.trim();
if (normalized.isEmpty()) {
@@ -238,7 +291,7 @@ public class OperationAuditService {
}
private String trim(String s){
private static String trim(String s){
return s == null ? "" : s.trim();
}
}
+10 -11
View File
@@ -13,7 +13,7 @@ spring:
server:
# TMS HTTP 服务监听端口。
port: 8080
port: 8088
management:
endpoints:
@@ -97,10 +97,10 @@ tms:
standard-web-organization-json-path: ${TMS_INIT_EXECUTOR_STANDARD_WEB_ORGANIZATION_JSON_PATH:${tms.init.executor.standard-web-target-dir}/organization.json}
# 标准版 CMSP/CMTP 启动用户;start_standard_apps.sh 会读取该值。
standard-run-user: ${TMS_INIT_EXECUTOR_STANDARD_RUN_USER:cmep4i}
# 标准版 CMSP/CMTP 启动命令。
standard-app-start-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_START_COMMAND:/home/tms/bin/start_standard_apps.sh}
# 标准版 CMSP/CMTP 停止命令。
standard-app-stop-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_STOP_COMMAND:/home/tms/bin/stop_standard_apps.sh}
# 标准版 CMSP/CMTP 启动命令;默认通过 root 受控 wrapper 执行,确保停启能跨用户处理 cmep4i 进程。
standard-app-start-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_START_COMMAND:sudo -n /usr/local/sbin/tms-start-standard-apps}
# 标准版 CMSP/CMTP 停止命令;默认通过 root 受控 wrapper 执行,避免 tms 用户无权限 kill cmep4i 进程。
standard-app-stop-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_STOP_COMMAND:sudo -n /usr/local/sbin/tms-stop-standard-apps}
# 标准版 nginx 启动命令。
standard-nginx-start-command: ${TMS_INIT_EXECUTOR_STANDARD_NGINX_START_COMMAND:sudo -n /usr/local/sbin/tms-start-standard-nginx}
# 标准版 nginx 停止命令。
@@ -135,9 +135,8 @@ tms:
rabbit-admin-username: ${TMS_INIT_EXECUTOR_RABBIT_ADMIN_USERNAME:admin}
# RabbitMQ 管理员密码(预制)。
rabbit-admin-password: ${TMS_INIT_EXECUTOR_RABBIT_ADMIN_PASSWORD:admin}
# RabbitMQ 控制命令(用于新增用户、授权等)。
# 建议线上配置为绝对路径,例如 /usr/local/rabbitmq/sbin/rabbitmqctl。
rabbitmq-ctl-command: ${TMS_INIT_EXECUTOR_RABBITMQ_CTL_COMMAND:/usr/local/rabbitmq/sbin/rabbitmqctl}
# RabbitMQ 控制命令(用于新增用户、授权等);默认通过 root 受控 wrapper 执行,避免 tms 与 RabbitMQ 服务端 Erlang cookie 不一致。
rabbitmq-ctl-command: ${TMS_INIT_EXECUTOR_RABBITMQ_CTL_COMMAND:sudo -n /usr/local/sbin/tms-rabbitmqctl}
# RabbitMQ Admin CLI 命令路径(用于创建队列等)。
rabbitmq-admin-command: ${TMS_INIT_EXECUTOR_RABBITMQ_ADMIN_COMMAND:/usr/sbin/rabbitmqadmin}
# 标准版 RabbitMQ 初始化脚本路径(保留兼容;MQ_USER_APPLY 当前优先直接调用 rabbitmqctl/rabbitmqadmin)。
@@ -322,7 +321,7 @@ tms:
# 备份/恢复标准收发器业务库名;第一版默认使用 CMEP。
cmep-database-name: ${TMS_BACKUP_CMEP_DATABASE_NAME:CMEP}
# 恢复完成后的最小健康检查地址。
health-check-url: ${TMS_BACKUP_HEALTH_CHECK_URL:http://127.0.0.1:8080/actuator/health}
health-check-url: ${TMS_BACKUP_HEALTH_CHECK_URL:http://127.0.0.1:8088/actuator/health}
# 健康检查请求超时(秒)。
health-check-timeout-seconds: ${TMS_BACKUP_HEALTH_CHECK_TIMEOUT_SECONDS:5}
# 健康检查最大等待时间(秒);TMS 启动命令返回后,Spring Boot 仍可能需要一段时间才监听端口。
@@ -343,7 +342,7 @@ tms:
internal-token: ${TMS_INTERNAL_TOKEN:change-me-internal-token}
internal-auth:
# 是否启用 /api/** 的内部登录鉴权;开发联调时可临时关闭。
enabled: ${TMS_SECURITY_INTERNAL_AUTH_ENABLED:false}
enabled: ${TMS_SECURITY_INTERNAL_AUTH_ENABLED:true}
# 关闭内部鉴权后,注入请求上下文的调试角色。
debug-role-code: ${TMS_SECURITY_INTERNAL_AUTH_DEBUG_ROLE_CODE:OPS_ADMIN}
# 关闭内部鉴权后,注入请求上下文的调试认证等级。
@@ -352,7 +351,7 @@ tms:
debug-session-token: ${TMS_SECURITY_INTERNAL_AUTH_DEBUG_SESSION_TOKEN:DEBUG-BYPASS}
replay:
# 是否启用防重放校验;本地 Postman/联调可临时设为 false,生产环境应保持 true。
enabled: ${TMS_SECURITY_REPLAY_ENABLED:false}
enabled: ${TMS_SECURITY_REPLAY_ENABLED:true}
openapi:
# 外部签名服务接口允许的时间戳偏差(秒),防重放。
timestamp-skew-seconds: 300
@@ -33,6 +33,7 @@ class ResourceBackupPropertiesTest {
Assertions.assertTrue(properties.getTmsDbExcludedTables().contains("tms_resource_restore_task"));
Assertions.assertEquals(300, properties.getMysqldumpTimeoutSeconds());
Assertions.assertEquals(300, properties.getRestoreDbTimeoutSeconds());
Assertions.assertEquals("http://127.0.0.1:8088/actuator/health", properties.getHealthCheckUrl());
Assertions.assertEquals(180, properties.getHealthCheckMaxWaitSeconds());
Assertions.assertEquals(5, properties.getHealthCheckRetryIntervalSeconds());
}
@@ -332,6 +332,33 @@ class ApplyResourceBackupScriptContractTest {
Assertions.assertTrue(status.contains("database restore failed"));
}
@Test
void shouldExecuteDatabaseSqlWithoutConnectingToTargetDatabaseFirst() throws Exception {
Path commandLog = tempDir.resolve("mysql-restore.log");
Path mysql = tempDir.resolve("mysql");
Files.writeString(mysql, """
#!/usr/bin/env bash
for arg in "$@"; do
printf 'ARG:%%s\\n' "$arg" >> "%s"
done
input="$(cat)"
if [ -n "$input" ]; then
printf 'STDIN:%%s\\n' "$input" >> "%s"
fi
""".formatted(commandLog, commandLog));
mysql.toFile().setExecutable(true);
Path sqlFile = tempDir.resolve("CMEP.sql");
Files.writeString(sqlFile, "CREATE DATABASE IF NOT EXISTS `CMEP`;\nUSE `CMEP`;\nselect 1;");
int exitCode = runDbRestoreScriptForExit(mysql, "CMEP", sqlFile);
Assertions.assertEquals(0, exitCode);
String log = Files.readString(commandLog);
Assertions.assertTrue(log.contains("ARG:--host=127.0.0.1"));
Assertions.assertFalse(log.contains("ARG:CMEP" + System.lineSeparator()));
Assertions.assertTrue(log.contains("STDIN:CREATE DATABASE IF NOT EXISTS `CMEP`;"));
}
private static void runScript(Path workDir, Map<String, String> environment) throws Exception {
int exitCode = runScriptForExit(workDir, environment);
Assertions.assertEquals(0, exitCode);
@@ -350,6 +377,23 @@ class ApplyResourceBackupScriptContractTest {
return process.waitFor();
}
private static int runDbRestoreScriptForExit(Path mysqlPath, String dbName, Path sqlFile) throws Exception {
ProcessBuilder builder = new ProcessBuilder(
"bash",
Path.of("scripts/resource-restore/restore-db.sh").toAbsolutePath().normalize().toString(),
"--mysql-path", mysqlPath.toString(),
"--host", "127.0.0.1",
"--port", "4000",
"--username", "root",
"--password", "secret",
"--db-name", dbName,
"--sql-file", sqlFile.toString()
);
builder.directory(Path.of(".").toAbsolutePath().normalize().toFile());
Process process = builder.start();
return process.waitFor();
}
private Path fakeScript(String name, Path commandLog) throws Exception {
Path script = tempDir.resolve(name);
Files.writeString(script, """
@@ -39,6 +39,36 @@ class MqReplayScriptContractTest {
Assertions.assertTrue(log.contains("set_permissions -p /RQ tms .* .* .*"));
}
@Test
void shouldReplayRabbitMqThroughPrefixedRootWrapperCommand() throws Exception {
Path context = tempDir.resolve("mq-restore-context.json");
Files.writeString(context, """
{"schemaVersion":"1.0","productType":"ENTERPRISE","mqType":"RABBITMQ","orgCode":"BKCHCNBJXXX",
"mq":{"channelUsername":"bank_user","channelPassword":"Secret123!"}}
""");
Path commandLog = tempDir.resolve("commands.log");
Path sudo = fakeSudo(commandLog);
Path wrapper = tempDir.resolve("tms-rabbitmqctl");
ProcessBuilder builder = new ProcessBuilder(
"bash",
Path.of("scripts/resource-restore/replay-mq.sh").toAbsolutePath().normalize().toString(),
"--context-file",
context.toString(),
"--work-dir",
tempDir.resolve("work").toString()
);
builder.environment().put("RABBITMQ_CTL_COMMAND", sudo + " -n " + wrapper);
Process process = builder.start();
Assertions.assertEquals(0, process.waitFor(), new String(process.getErrorStream().readAllBytes()));
String log = Files.readString(commandLog);
Assertions.assertTrue(log.contains("-n " + wrapper + " status"));
Assertions.assertTrue(log.contains("-n " + wrapper + " add_vhost /RQ"));
Assertions.assertTrue(log.contains("-n " + wrapper + " add_user bank_user Secret123!"));
Assertions.assertTrue(log.contains("-n " + wrapper + " set_permissions -p /RQ bank_user .* .* .*"));
}
@Test
void shouldDelegateToCustomReplayCommandWhenConfigured() throws Exception {
Path context = tempDir.resolve("mq-restore-context.json");
@@ -71,4 +101,15 @@ class MqReplayScriptContractTest {
script.toFile().setExecutable(true);
return script;
}
private Path fakeSudo(Path commandLog) throws Exception {
Path script = tempDir.resolve("sudo");
Files.writeString(script, """
#!/usr/bin/env bash
printf '%%s\\n' "$*" >> "%s"
exit 0
""".formatted(commandLog));
script.toFile().setExecutable(true);
return script;
}
}
@@ -1031,6 +1031,8 @@ class ConfigurableInitStepExecutorTest {
Path stopRabbitScript = scriptDir.resolve("stop_standard_rabbitmq.sh");
Path checkScript = scriptDir.resolve("check_standard_runtime.sh");
Path vendorStepRunner = scriptDir.resolve("root-sbin/tms-run-standard-vendor-step");
Path startAppsRunner = scriptDir.resolve("root-sbin/tms-start-standard-apps");
Path stopAppsRunner = scriptDir.resolve("root-sbin/tms-stop-standard-apps");
Assertions.assertTrue(Files.exists(dbScript));
Assertions.assertTrue(Files.exists(appScript));
@@ -1040,6 +1042,8 @@ class ConfigurableInitStepExecutorTest {
Assertions.assertTrue(Files.exists(stopRabbitScript));
Assertions.assertTrue(Files.exists(checkScript));
Assertions.assertTrue(Files.exists(vendorStepRunner));
Assertions.assertTrue(Files.exists(startAppsRunner));
Assertions.assertTrue(Files.exists(stopAppsRunner));
String dbContent = Files.readString(dbScript);
Assertions.assertTrue(dbContent.contains("SCHEMA-DDL.sql"));
@@ -1079,6 +1083,16 @@ class ConfigurableInitStepExecutorTest {
Assertions.assertTrue(vendorRunnerContent.contains("setftq"));
Assertions.assertTrue(vendorRunnerContent.contains("settlq"));
Assertions.assertTrue(vendorRunnerContent.contains("setsptp"));
String startAppsRunnerContent = Files.readString(startAppsRunner);
Assertions.assertTrue(startAppsRunnerContent.contains("start_standard_apps.sh"));
Assertions.assertTrue(startAppsRunnerContent.contains("exec"));
String stopAppsRunnerContent = Files.readString(stopAppsRunner);
Assertions.assertTrue(stopAppsRunnerContent.contains("stop_standard_apps.sh"));
Assertions.assertTrue(stopAppsRunnerContent.contains("CMEP-CMSP"));
Assertions.assertTrue(stopAppsRunnerContent.contains("CMEP-CMTP"));
Assertions.assertTrue(stopAppsRunnerContent.contains("exit 1"));
Assertions.assertTrue(vendorRunnerContent.contains("runuser -u"));
String stopRabbitContent = Files.readString(stopRabbitScript);
@@ -1107,18 +1121,20 @@ class ConfigurableInitStepExecutorTest {
String exampleContent = Files.readString(exampleConfig);
Assertions.assertTrue(exampleContent.contains("standard-db-apply-command: /home/tms/bin/apply_standard_db.sh"));
Assertions.assertTrue(exampleContent.contains("standard-run-user: cmep4i"));
Assertions.assertTrue(exampleContent.contains("standard-app-start-command: /home/tms/bin/start_standard_apps.sh"));
Assertions.assertTrue(exampleContent.contains("standard-app-stop-command: /home/tms/bin/stop_standard_apps.sh"));
Assertions.assertTrue(exampleContent.contains("standard-app-start-command: sudo -n /usr/local/sbin/tms-start-standard-apps"));
Assertions.assertTrue(exampleContent.contains("standard-app-stop-command: sudo -n /usr/local/sbin/tms-stop-standard-apps"));
Assertions.assertTrue(exampleContent.contains("standard-nginx-start-command: /home/tms/bin/start_standard_nginx.sh"));
Assertions.assertTrue(exampleContent.contains("standard-nginx-stop-command: /home/tms/bin/stop_standard_nginx.sh"));
Assertions.assertTrue(exampleContent.contains("standard-post-check-command: /home/tms/bin/check_standard_runtime.sh"));
Assertions.assertTrue(exampleContent.contains("rabbitmq-ctl-command: sudo -n /usr/local/sbin/tms-rabbitmqctl"));
String applicationContent = Files.readString(applicationConfig);
Assertions.assertTrue(applicationContent.contains("standard-app-start-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_START_COMMAND:/home/tms/bin/start_standard_apps.sh}"));
Assertions.assertTrue(applicationContent.contains("standard-app-stop-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_STOP_COMMAND:/home/tms/bin/stop_standard_apps.sh}"));
Assertions.assertTrue(applicationContent.contains("standard-nginx-start-command: ${TMS_INIT_EXECUTOR_STANDARD_NGINX_START_COMMAND:/home/tms/bin/start_standard_nginx.sh}"));
Assertions.assertTrue(applicationContent.contains("standard-nginx-stop-command: ${TMS_INIT_EXECUTOR_STANDARD_NGINX_STOP_COMMAND:/home/tms/bin/stop_standard_nginx.sh}"));
Assertions.assertTrue(applicationContent.contains("standard-app-start-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_START_COMMAND:sudo -n /usr/local/sbin/tms-start-standard-apps}"));
Assertions.assertTrue(applicationContent.contains("standard-app-stop-command: ${TMS_INIT_EXECUTOR_STANDARD_APP_STOP_COMMAND:sudo -n /usr/local/sbin/tms-stop-standard-apps}"));
Assertions.assertTrue(applicationContent.contains("standard-nginx-start-command: ${TMS_INIT_EXECUTOR_STANDARD_NGINX_START_COMMAND:sudo -n /usr/local/sbin/tms-start-standard-nginx}"));
Assertions.assertTrue(applicationContent.contains("standard-nginx-stop-command: ${TMS_INIT_EXECUTOR_STANDARD_NGINX_STOP_COMMAND:sudo -n /usr/local/sbin/tms-stop-standard-nginx}"));
Assertions.assertTrue(applicationContent.contains("standard-post-check-command: ${TMS_INIT_EXECUTOR_STANDARD_POST_CHECK_COMMAND:/home/tms/bin/check_standard_runtime.sh}"));
Assertions.assertTrue(applicationContent.contains("rabbitmq-ctl-command: ${TMS_INIT_EXECUTOR_RABBITMQ_CTL_COMMAND:sudo -n /usr/local/sbin/tms-rabbitmqctl}"));
String readmeContent = Files.readString(readme);
Assertions.assertTrue(readmeContent.contains("scripts/standard-init/"));
@@ -1247,6 +1263,61 @@ class ConfigurableInitStepExecutorTest {
Assertions.assertTrue(callsContent.contains("set_permissions -p /RQ bank_user .* .* .*"));
}
@Test
void shouldApplyStandardRabbitUserThroughPrefixedRootWrapperCommand() throws IOException {
Path tempRoot = Files.createTempDirectory("init-executor-rabbit-wrapper-test");
Path cpconfig = tempRoot.resolve("cpconfig.cfg");
Path calls = tempRoot.resolve("calls.log");
Path sudo = tempRoot.resolve("sudo");
Path wrapper = tempRoot.resolve("tms-rabbitmqctl");
Files.writeString(cpconfig, "MY_CIPSID_OR_BIC=AAAABBBBXXX\n");
Files.writeString(
sudo,
"#!/bin/bash\n"
+ "echo \"$*\" >> \"" + calls + "\"\n"
+ "exit 0\n"
);
Assertions.assertTrue(sudo.toFile().setExecutable(true));
InitExecutorProperties properties = new InitExecutorProperties();
properties.setMode(InitExecutorProperties.Mode.LOCAL);
properties.setLogDir(tempRoot.resolve("logs").toString());
properties.setStagingRootDir(tempRoot.resolve("staging").toString());
properties.setStandardCpconfigPath(cpconfig.toString());
properties.setRabbitmqCtlCommand(sudo + " -n " + wrapper);
ConfigurableInitStepExecutor executor = new ConfigurableInitStepExecutor(properties, new ObjectMapper());
InitTaskEntity task = new InitTaskEntity();
task.setTaskId("TASK-RABBIT-WRAPPER-1");
task.setProductType("ENTERPRISE");
task.setInitPlanJson(buildInitPlanJson(
"ENTERPRISE",
Map.of(
"orgCodeType", "BIC",
"orgCode", "AAAABBBBXXX",
"orgNameCn", "测试机构",
"deployMode", "SINGLE",
"nodes", Map.of("node01Ip", "10.0.0.1"),
"mq", Map.of("mqType", "RABBITMQ", "channelUsername", "bank_user", "channelPassword", "bank_pass"),
"licenses", Map.of("receiverLicenseFileId", "receiver-001")
)
));
InitTaskStepEntity step = new InitTaskStepEntity();
step.setStepNo(1);
step.setStepCode("MQ_USER_APPLY");
step.setCommandLine("internal::mq_user_apply");
InitStepExecutionResult result = executor.execute(task, step);
Assertions.assertTrue(result.isSuccess());
String callsContent = Files.readString(calls);
Assertions.assertTrue(callsContent.contains("-n " + wrapper + " status"));
Assertions.assertTrue(callsContent.contains("-n " + wrapper + " add_vhost /RQ"));
Assertions.assertTrue(callsContent.contains("-n " + wrapper + " add_user bank_user bank_pass"));
Assertions.assertTrue(callsContent.contains("-n " + wrapper + " set_permissions -p /RQ bank_user .* .* .*"));
}
@Test
void shouldApplyStandardRabbitUserForIndirectRabbitmqTlq() throws IOException {
Path tempRoot = Files.createTempDirectory("init-executor-rabbit-setup-indirect-tlq-test");