Ver código fonte

Merge branch 'text-ca'

xiaochan 10 meses atrás
pai
commit
8ebc99f1fb

+ 41 - 0
src/main/java/thyyxxk/webserver/controller/ca/CaController.java

@@ -0,0 +1,41 @@
+package thyyxxk.webserver.controller.ca;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import thyyxxk.webserver.config.auth.PassToken;
+import thyyxxk.webserver.entity.ResultVo;
+import thyyxxk.webserver.entity.ca.CaReturn;
+import thyyxxk.webserver.entity.ca.CaSingBizSnReturnData;
+import thyyxxk.webserver.service.ca.CaServer;
+import thyyxxk.webserver.utils.ResultVoUtil;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/thyyca")
+public class CaController {
+    private final CaServer server;
+
+    @Autowired
+    public CaController(CaServer server) {
+        this.server = server;
+    }
+
+    @PostMapping("/sendBatchByCode")
+    public ResultVo<List<CaReturn.CaData>> sendBatchByCode(@RequestBody CaServer.Send send) {
+        return ResultVoUtil.success(server.sendBatchByCode(send));
+    }
+
+
+    @PostMapping("/sendByCode")
+    @PassToken
+    public ResultVo<CaReturn.CaData> sendByCode(@RequestBody CaServer.Send send) {
+        return ResultVoUtil.success(server.sendByCode(send));
+    }
+
+    @GetMapping("/getSignInfoByBizSn")
+    @PassToken
+    public ResultVo<CaSingBizSnReturnData> getSignInfoByBizSn(@RequestParam("bizSn") String bizSn) {
+        return server.getSignInfoByBizSn(bizSn);
+    }
+}

+ 75 - 0
src/main/java/thyyxxk/webserver/entity/ca/CaData.java

@@ -1,4 +1,79 @@
 package thyyxxk.webserver.entity.ca;
 
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@Data
+@ConfigurationProperties(prefix = "thyy.ca")
+@NoArgsConstructor
 public class CaData {
+
+    @Data
+    public static class MobileApp {
+        private String signAlgorithms;
+        private String privatekey;
+        private String appId;
+        private String id;
+        private String url;
+    }
+
+    @Data
+    public static class SignAuthentication {
+        /**
+         * 应用系统标识
+         */
+        private String appCode;
+
+        /**
+         * 应用系统密码
+         */
+        private String appPwd;
+
+        /**
+         * 证书
+         */
+        private String cert;
+
+        /**
+         * 签名算法
+         */
+        private String signAig;
+
+        /**
+         * 地址
+         */
+        private String url;
+    }
+
+    @Data
+    public static class TimestampAuthentication {
+        private String url;
+        /**
+         * 应用系统标识
+         */
+        private String appCode;
+
+        /**
+         * 应用系统密码
+         */
+        private String appPwd;
+    }
+
+    @Data
+    public static class HBoardSign {
+        private String apiKey;
+        private String apiSecret;
+    }
+
+    private MobileApp mobileApp;
+    private SignAuthentication signAuthentication;
+    private TimestampAuthentication timestampAuthentication;
+
+    /**
+     * 手写数字签名系统接口规范
+     */
+    private HBoardSign hBoardSign;
 }

+ 32 - 0
src/main/java/thyyxxk/webserver/entity/ca/CaReturn.java

@@ -0,0 +1,32 @@
+package thyyxxk.webserver.entity.ca;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+public class CaReturn {
+
+    private String ret;
+    private String msg;
+    private CaData data;
+
+    @EqualsAndHashCode(callSuper = true)
+    @Data
+    public static class CaData extends EmrCaData {
+        private Integer isTrust;
+        private String cert;
+        private String signAlg;
+        private String signValue;
+        private String certSn;
+    }
+
+    @Data
+    public static class EmrCaData {
+        private String timeStamp;
+        private String bizSn;
+        private String signature;
+        private String name;
+    }
+    // 2191 THyy3456
+
+}

+ 41 - 0
src/main/java/thyyxxk/webserver/entity/ca/CaSendBatchParams.java

@@ -0,0 +1,41 @@
+package thyyxxk.webserver.entity.ca;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class CaSendBatchParams {
+
+    @Data
+    @Builder
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class CaSendBatchData {
+        private String bizSn;
+        private String msg;
+        private String msgWrapper;
+        private String desc;
+    }
+
+    /**
+     * 移动安全认证系统分配的应用Id
+     */
+    private String appId;
+    /**
+     * 用户标识,用于关联应用系统的用户身份,对应用户注册信息中的uuid
+     */
+    private String id;
+
+    /**
+     * 用应用私钥对msg值(URLEncode编码之前,摘要处理之后的数据)进行参数签名,
+     * 得到Base64 编码格式签名值后进行URLEncode(UTF-8)传
+     * 输。当后台开启鉴权时,此参数必填。
+     * 应用私钥由Mkey后台分配,示例代码参考8.2参
+     */
+    private String sign;
+}

+ 64 - 0
src/main/java/thyyxxk/webserver/entity/ca/CaSendParams.java

@@ -0,0 +1,64 @@
+package thyyxxk.webserver.entity.ca;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class CaSendParams {
+    /**
+     * 移动安全认证系统分配的应用Id
+     */
+    private String appId;
+    /**
+     * 用户标识,用于关联应用系统的用户身份,对应用户注册信息中的uuid
+     */
+    private String id;
+    /**
+     * 业务流水号,需要保持唯一性,用于标识每笔业务的关联,签名操作后会将对应的数据回调给应用
+     */
+    private String bizSn;
+    /**
+     * 待签名数据,传输前对摘要计算之后的msg做URLEncode编码(UTF-8)。
+     * 对有举证意义的数据(原文)做摘要处理,计算公式:
+     * msg=new String(Base64.encode(SHA256(原文)))
+     * 参考示例代码8.1SHA-256摘要转换
+     */
+    private String msg;
+    /**
+     * 是否对待签名数据进行Base64解码,默认为0 0:不解码
+     * 1:解码
+     */
+    private String msgWrapper;
+
+    /**
+     * 签名动作描述(URLEncode,utf-8),最大长度512。内容会在APP端展现
+     */
+    private String desc;
+
+    /**
+     * 应用接收签名后数据的回调地址
+     * (URLEncode,utf-8),最大长度512。
+     * 由应用系统提供接口地址,接口定义参见6.2.1
+     */
+    private String url;
+
+    /**
+     * 数据传递方式,推荐使用redirect。redirect:由服务器传递给应用
+     * forward:由APP传递给应用
+     */
+    private String mode;
+
+    /**
+     * 用应用私钥对msg值(URLEncode编码之前,摘要处理之后的数据)进行参数签名,
+     * 得到Base64 编码格式签名值后进行URLEncode(UTF-8)传
+     * 输。当后台开启鉴权时,此参数必填。
+     * 应用私钥由Mkey后台分配,示例代码参考8.2参
+     */
+    private String sign;
+
+}

+ 52 - 0
src/main/java/thyyxxk/webserver/entity/ca/CaSingBizSnReturnData.java

@@ -0,0 +1,52 @@
+package thyyxxk.webserver.entity.ca;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class CaSingBizSnReturnData implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Data
+    public static class SingData{
+
+        /**
+         * 签名值(需URLDecode解码,utf-8)
+         */
+        private String signData;
+
+        /**
+         * 用户签名证书(需URLDecode解码,utf-8)
+         */
+        private String cert;
+
+        /**
+         * 证书序列号
+         */
+        private String certSn;
+
+        /**
+         * 用户签章图片(Base64格式,需URLDecode解码,utf-8)
+         */
+        private String seal;
+
+        /**
+         * 用户标识
+         */
+        private String uuid;
+    }
+
+    /**
+     * 结果码:success为成功,其他为失败
+     */
+    private String ret;
+
+    /**
+     * 结果描述,提供更详细的错误信息或成功信息
+     */
+    private String msg;
+
+    private SingData data;
+}

+ 216 - 0
src/main/java/thyyxxk/webserver/service/ca/CaServer.java

@@ -0,0 +1,216 @@
+package thyyxxk.webserver.service.ca;
+
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.dtflys.forest.Forest;
+import com.dtflys.forest.http.ForestRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Service;
+import thyyxxk.webserver.config.exception.BizException;
+import thyyxxk.webserver.config.exception.ExceptionEnum;
+import thyyxxk.webserver.constants.YesOrNo;
+import thyyxxk.webserver.entity.ResultVo;
+import thyyxxk.webserver.entity.ca.CaData;
+import thyyxxk.webserver.entity.ca.CaReturn;
+import thyyxxk.webserver.entity.ca.CaSendParams;
+import thyyxxk.webserver.entity.ca.CaSingBizSnReturnData;
+import thyyxxk.webserver.entity.login.UserInfo;
+import thyyxxk.webserver.service.redislike.RedisLikeService;
+import thyyxxk.webserver.utils.ResultVoUtil;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@Service
+public class CaServer {
+
+    private final CaData caData;
+    private final String SUCCRSS = "success";
+    private final RedisLikeService redisLikeService;
+
+    public CaServer(CaData caData, RedisLikeService redisLikeService) {
+        this.caData = caData;
+        this.redisLikeService = redisLikeService;
+    }
+
+    @EqualsAndHashCode(callSuper = true)
+    @Data
+    public static class Send extends CaSendParams {
+        private String id;
+        private String msg;
+        private String desc;
+        private Integer count = 1;
+    }
+
+    public List<CaReturn.CaData> sendBatchByCode(Send send) {
+        List<CaReturn.CaData> returnValue = new ArrayList<>();
+        for (int i = 0; i < send.getCount(); i++) {
+            returnValue.add(sendByCode(send));
+        }
+        return returnValue;
+    }
+
+    public CaReturn.CaData sendByCode(Send send) {
+        UserInfo userInfoByCode = redisLikeService.getUserInfoByCode(send.getId());
+        send.setBizSn(IdUtil.simpleUUID());
+        CaReturn push = push(send);
+        signatureAuthentication(send, push);
+        String timeStamp = generateTimestampByInData(send);
+        push.getData().setTimeStamp(timeStamp);
+        push.getData().setBizSn(send.getBizSn());
+        push.getData().setSignature("http://172.16.32.167:8077/doctorSignatureImage/" + send.getId() + ".png");
+        push.getData().setName(userInfoByCode.getName());
+        return push.getData();
+    }
+
+    /**
+     * 推送用户签名
+     *
+     * @param send
+     * @return 返回值
+     */
+    private CaReturn push(Send send) {
+        CaReturn caReturn;
+        try {
+            CaData.MobileApp app = caData.getMobileApp();
+            ForestRequest<?> post = Forest.post(app.getUrl() + "/v1/push/sign");
+            send.setAppId(app.getAppId());
+            send.setMsg(CaUtils.GetSHA256FormString(send.getMsg()));
+            send.setMsgWrapper("0");
+            send.setUrl(URLEncoder.encode("http://127.0.0.1/", "UTF-8"));
+            send.setMode("redirect");
+            send.setSign(CaUtils.getSign(app.getPrivatekey(), send.getMsg()));
+            String execute = post.addBody(send).execute(String.class);
+            String decode = URLDecoder.decode(execute, "UTF-8");
+            log.info("返回数据:{}", decode);
+            caReturn = JSONUtil.toBean(decode, CaReturn.class);
+            if (!SUCCRSS.equals(caReturn.getRet())) {
+                throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名失败。" + caReturn.getMsg());
+            }
+            if (Integer.valueOf(1).equals(caReturn.getData().getIsTrust())) {
+                throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名失败,请用户打开免密签名。");
+            }
+            if (Integer.valueOf(2).equals(caReturn.getData().getIsTrust())) {
+                return caReturn;
+            }
+            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名失败。" + caReturn.getMsg());
+        } catch (UnsupportedEncodingException e) {
+            log.error(e.getMessage());
+            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名失败。" + e.getMessage());
+        }
+    }
+
+    /**
+     * 认证签名
+     *
+     * @param send
+     * @param caReturn
+     */
+    private void signatureAuthentication(Send send, CaReturn caReturn) {
+        CaData.SignAuthentication signData = caData.getSignAuthentication();
+        CaWSDL caWSDL = new CaWSDL(signData.getUrl());
+        String replace = getVerifySignDataParams(send, caReturn, signData);
+        JSONObject rst = caWSDL.invoke("verifySignData", replace);
+        log.info("认证签名xml:{}\n认证签名返回值:{}\n", replace, rst.toString());
+        Integer code = rst.getByPath("Root.RetCode", Integer.class);
+        if (!YesOrNo.YES.getCode().equals(code)) {
+            String msg = rst.getByPath("Root.RetMsg", String.class);
+            throw new BizException(ExceptionEnum.LOGICAL_ERROR, msg);
+        }
+
+    }
+
+    private static @NotNull String getVerifySignDataParams(Send send, CaReturn caReturn, CaData.SignAuthentication signData) {
+        String request = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
+                "<Root>\n" +
+                "\t<AppCode>${AppCode}</AppCode>\n" +
+                "\t<AppPWD>${AppPWD}</AppPWD>\n" +
+                "\t<Request>\n" +
+                "\t\t<Cert>${Cert}</Cert>\n" +
+                "\t\t<SignAlg>${SignAlg}</SignAlg>\n" +
+                "\t\t<InData>${InData}</InData>\n" +
+                "\t\t<SignData>${SignData}</SignData>\n" +
+                "\t</Request>\n" +
+                "</Root>";
+
+        return request.replace("${AppCode}", signData.getAppCode())
+                .replace("${AppPWD}", signData.getAppPwd())
+                .replace("${Cert}", caReturn.getData().getCert())
+                .replace("${SignAlg}", signData.getSignAig())
+                .replace("${InData}", send.getMsg())
+                .replace("${SignData}", caReturn.getData().getSignValue());
+    }
+
+    /**
+     * 认证时间戳并返回
+     *
+     * @param send 数据
+     * @return
+     */
+    private String generateTimestampByInData(Send send) {
+        String data = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
+                "<Root>\n" +
+                "\t<AppCode>${AppCode}</AppCode>\n" +
+                "\t<AppPWD>${AppPWD}</AppPWD>\n" +
+                "\t<Request>\n" +
+                "\t\t<InData>${InData}</InData>\n" +
+                "<HashAlg>${HashAlg}</HashAlg>\n" +
+                "<CertReq>${CertReq}</CertReq>\n" +
+                "\t</Request>\n" +
+                "</Root>";
+
+        CaData.TimestampAuthentication timeData = caData.getTimestampAuthentication();
+        String request = data.replace("${AppCode}", timeData.getAppCode())
+                .replace("${AppPWD}", timeData.getAppPwd())
+                .replace("${InData}", send.getMsg())
+                .replace("${HashAlg}", "SHA256")
+                .replace("${CertReq}", "true");
+
+        CaWSDL caWSDL = new CaWSDL(timeData.getUrl());
+        JSONObject ret = caWSDL.invoke("generateTimestampByInData", request);
+        Integer code = ret.getByPath("Root.RetCode", Integer.class);
+        log.info("认证时间戳xml:{}\n认证时间戳返回值:{}\n", request, ret);
+        if (code != 1) {
+            String msg = ret.getByPath("Root.RetMsg", String.class);
+            throw new BizException(ExceptionEnum.LOGICAL_ERROR, msg);
+        }
+        return ret.getByPath("Root.Response.TimeStamp", String.class);
+    }
+
+    public ResultVo<CaSingBizSnReturnData> getSignInfoByBizSn(String bizSn) {
+        CaData.MobileApp app = caData.getMobileApp();
+        ForestRequest<?> post = Forest.post(app.getUrl() + "/v1/sign/info/" + bizSn);
+        CaSendParams send = new CaSendParams();
+        send.setAppId(app.getAppId());
+
+        String indata = "appId=" + app.getAppId() + "&bizSn=" + bizSn;
+        String sign = CaUtils.getSign(app.getPrivatekey(), indata);
+        send.setSign(sign);
+
+        String ret = post.addBody(send).execute(String.class);
+
+        CaSingBizSnReturnData execute = JSONUtil.toBean(ret, CaSingBizSnReturnData.class);
+        if (SUCCRSS.equals(execute.getRet())) {
+            CaSingBizSnReturnData.SingData data = execute.getData();
+            try {
+                data.setSignData(URLDecoder.decode(data.getSignData(), "UTF-8"));
+                data.setCert(URLDecoder.decode(data.getCert(), "UTF-8"));
+                data.setSeal(URLDecoder.decode(data.getSeal(), "UTF-8"));
+            } catch (UnsupportedEncodingException e) {
+                log.error(e.getMessage());
+                return ResultVoUtil.success(ExceptionEnum.LOGICAL_ERROR, "解码失败请联系管理员" + bizSn);
+            }
+            return ResultVoUtil.success(execute);
+        }
+        return ResultVoUtil.success(ExceptionEnum.LOGICAL_ERROR, execute.getMsg());
+    }
+
+}

+ 60 - 0
src/main/java/thyyxxk/webserver/service/ca/CaUtils.java

@@ -0,0 +1,60 @@
+package thyyxxk.webserver.service.ca;
+
+import com.alibaba.fastjson.JSONObject;
+import com.sun.xml.internal.messaging.saaj.packaging.mime.util.BASE64EncoderStream;
+import lombok.extern.slf4j.Slf4j;
+import org.bouncycastle.util.encoders.Base64;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+@Slf4j
+@Service
+public class CaUtils {
+    private static String SIGN_ALGORITHMS = "SHA256WithRSA";
+
+    public static String GetSHA256FormString(String inData) {
+        String result = "";
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            byte[] messageDigest = md.digest(inData.getBytes());
+            result = new String(BASE64EncoderStream.encode(messageDigest));
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return result;
+    }
+
+    public static String getSign(String privateKey, String data) {
+        byte[] signData = signature(privateKey, data);
+        String sign = null;
+        if (signData != null) {
+            sign = new String(Base64.encode(signData));
+        }
+        return sign;
+    }
+
+    public static byte[] signature(String privateKey, String data) {
+        try {
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
+                    Base64.decode(privateKey));
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            PrivateKey priKey = keyFactory.generatePrivate(keySpec);
+
+            Signature oSig = Signature.getInstance(SIGN_ALGORITHMS);
+            oSig.initSign(priKey);
+            oSig.update(data.getBytes(StandardCharsets.UTF_8));
+            return oSig.sign();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+}

+ 50 - 0
src/main/java/thyyxxk/webserver/service/ca/CaWSDL.java

@@ -0,0 +1,50 @@
+package thyyxxk.webserver.service.ca;
+
+import cn.hutool.http.HtmlUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.dtflys.forest.Forest;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CaWSDL {
+
+    private final String regex = "<ns1:out>(.*?)</ns1:out>";
+    // 创建 Pattern 对象
+    private final Pattern pattern = Pattern.compile(regex);
+
+    private final String WSDL = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://webservice.security.openapi.sp.custle.com\">\n" +
+            "   <soapenv:Header/>\n" +
+            "   <soapenv:Body>\n" +
+            "      <web:${name}>\n" +
+            "         <web:in0>\n" +
+            "    <![CDATA[${data}]]>\n" +
+            "         </web:in0>\n" +
+            "      </web:${name}>\n" +
+            "   </soapenv:Body>\n" +
+            "</soapenv:Envelope>";
+
+    private final String url;
+
+    public CaWSDL(String url) {
+        this.url = url;
+    }
+
+    public JSONObject invoke(String method, String request) {
+        String body = WSDL.replace("${name}", method).replace("${data}", request);
+        String execute = Forest.post(url).addBody(body).execute(String.class);
+        String data = findData(execute);
+        return JSONUtil.xmlToJson(data);
+    }
+
+    private String findData(String input) {
+        Matcher matcher = pattern.matcher(input);
+        if (matcher.find()) {
+            // 获取第一个捕获组
+            return HtmlUtil.unescape(matcher.group(1));
+        } else {
+            return "";
+        }
+    }
+}

+ 0 - 1
src/main/java/thyyxxk/webserver/service/zhuyuanyisheng/ShouShuShenQingService.java

@@ -282,7 +282,6 @@ public class ShouShuShenQingService {
         return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
     }
 
-
     public ResultVo<String> addASurgicalSite(String name) {
         if (dao.repeatPartName(name)) {
             return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "手术部位名称重复");

+ 21 - 1
src/main/resources/application-cytest.yml

@@ -15,7 +15,7 @@ spring:
     cache: false
   datasource:
     dynamic:
-      primary: his
+      primary: dev
       strict: false
       datasource:
         his:
@@ -173,3 +173,23 @@ appletThmzUrl: http://172.16.30.119:8089/thmz
 logging:
   level:
     thyyxxk.webserver.dao: debug
+thyy:
+  ca:
+    sign-authentication:
+      app-code: ywxt
+      app-pwd: 12345678
+      cert: MIIB9TCCAZigAwIBAgIOAV53M9Vp5AY8j5DL1EUwDAYIKoEcz1UBg3UFADA+MQswCQYDVQQGEwJDTjEUMBIGA1UEAwwLU00yIFJPT1QgQ0ExCzAJBgNVBAgMAlNIMQwwCgYDVQQKDANoYWgwHhcNMjMwMzA5MDY1MzQyWhcNMjUwMzA5MDY1MzQyWjBZMQswCQYDVQQGEwJDTjELMAkGA1UECAwCU0gxCzAJBgNVBAcMAlNIMQ0wCwYDVQQKDARzaWduMQ0wCwYDVQQLDARzaWduMRIwEAYDVQQDDAlzaWduX3Rlc3QwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAT9dIroxCUB5oZvxl2Irq3OoAtbWvqa5tNvw854VGmgJxfuuTfgBtcVdEHkbXsSvRNdj9Uq2ld2XFjoaIyHaC1Co10wWzAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFCE77Si3YBk/u4qjZMQAJNHBvWKUMB0GA1UdDgQWBBSjsmDHccu255pXS76XpNE3LL7f7DALBgNVHQ8EBAMCB4AwDAYIKoEcz1UBg3UFAANJADBGAiEAkECq7DAZiONAwDblXKigoyGkmjePKsS2L4noqg/wQuwCIQCGnRJITQ+ErYg/l+YNrAWgY1UtoQnsM8A4XIsUynzu7Q==
+      sign-aig: SM3withSM2
+      url: http://103.36.136.173:7845/services/CipherServices
+    mobile-app:
+      app-id: 3
+      url: https://device.mkeysec.net/openapi
+      privatekey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHwpN7M6Xhja7sfaB0K3y2MBd4xLfhOF64mv0mtEZfqaWvphadXAimnfru8Y4sf0wDb9LYjacXRTkRMhZaZq5ZRCNFB+G8HnNb1y9Vr+7VqvT3wlRV0n+Jar+rEFguW293kH6wOLPQVNkmL4xEy/ecI81gfqR0wSHbyVWaBJ4o7w8qYVFc3is0vswl/bpKE2/4ngjEGDNuYdS0oXZR1TOjEEtNPEws9944yHyfYnbJgbTXkO78hlRUUu4tA8UnSjLrO0m0I6aQkuibPHKjMj247q2629L5IQpn+mGpgxmqUQmzSlKf5wg3uE0Ss1Bea8/AneSDBx2tzjsEs2qKn8cfAgMBAAECggEAM18GbaVCjNIPMf/rmmnmPA6AoztVFU0+Un6bcmzfAE3ymz+u6QatY1b+YDJZiS72NYq37yfS5XRVPtOEL9sQ+EhXTETKP2QKZONNTxBOwN166tHHFd6cUgRp2LJLm+cPi9/KgKZELH4e2Vs+qb3AyX2mtm/VjTSTulY6JRjAPF6EIyQqiLIinyw9TUXNqBs1kT5ma/50YPO2pSXWiOcXX9WWL7ECCNLCfKVRCKlEuaHuIozDuuwGHBrALAdqjWxrj0IMywCZIoVbBke+U7+KCKhKsc5eqsUJu8YfoVR3hxVhUHjlqIFbuAlLvrMqYZd3HgT1O3RSp/eOyXRMGsW0kQKBgQDHvA1p9Qv7K62WYOwwHEIg5Qym5uK4ClhvWUUQPE63bhzrkLkfNlDEOFpsnIk1EonvJHbfzW4pKR36AjbGBv9xxdgntkee+35pN8d8MT2eNMyHdsF6TDToKqPmqKHcxXmnpOCtC9Cam1wjBU+e7gdSoAeNWJeKUzg79HN7M7uD6QKBgQCuAPkd1G7GHkhMiXur1Td6nTmzwz9zPeHHycAE2r1epH22VUfKTu/oDgmky/VKO3JJuc1txt2bHs90iTLRDmcMy9l+6Ix9UJzmanAIRzhAVgmwz1liUNLbnJGl0YxCq0qlQA3FUUmOP36C2cvHcJm/hRIPG2O2nT1DLKsHvgU1xwKBgGL6mPciHU7aBUrZOxJYXpjoiQ2Iq1+imNKNPYFfMW8LHT/LV0HVa87hUkYyiHgJeNtOia48olO5cYZ8ZGJcA2iBL632UaXCYZGIt82epTdqWNTkj9qoOyu2PcTHWncKUVA3j7ORgE2tocolDqDmujBC55svBOHifaKQcE3khA9JAoGAf/fLPjrrNN00gsVhpJ/sa0qSEzh2w4QxNkOT6n4MYzxKD/xsDcc7/MfGI+K0BOHvTXVONXvZoqloHOaB7unOs8R/sivIlqjgmzyQJCZsojQkcFot/HZAfK6LFw4jPyzGev2+ou0DUZA0tHsEuSqAiC+PdnjIPpcpZOvG5KzFHCUCgYEAuLEUmIzQ7ex85Ulag2qfGj0CDMAq1R/rmwrk+1d2aP9GY890HwcyN5dlZGy8/Z/lMlzOGo1kDhhhwOfbiYSit00FdLChVux0wh8nyqqB4IuTy0S0ZeTNjWnjftpNaPXdHh5mpUXXKygCp3UT65HC6coaqoD3Vv8jE32svsz/uPE=
+    timestamp-authentication:
+      url: http://103.36.136.173:7845/services/TimeStampServices
+      app-code: ywxt
+      app-pwd: 12345678
+    HBoard-sign:
+      api-key: 00000
+      api-secret: 00000000
+