基于 SM2/SM4 的签名与加密调用流程梳理

本文记录了某接口调用过程中,如何基于 SM2 签名SM4 加密 完成数据请求的流程。为安全起见,涉及的密钥参数已做脱敏处理。


一、公共参数配置

private static String url = "http://xx.xx.xx.xx:8083/epc/api";
private static String appSecret = "****";
private static String appId = "****";
private static String publicKey = "****";
private static String privateKey = "****";
private static String path = "/fixmedins/hospRxDetlQuery";

二、SM2 签名流程

1. 构建 signstr

  • 剔除字段:signDataencDataextra
  • 将剩余参数排序
  • 拼接 appSecret 到最后的 key 字段

示例入参:

{
  "certno": "612*****316",
  "drCode": "D610****06954",
  "fixmedinsCode": "H610****0077",
  "fixmedinsName": "某某医院",
  "hiRxno": "610700022**********45233982978",
  "mdtrtId": "1274753890",
  "patnName": "张三",
  "pharCertType": "01",
  "pharCertno": "21233121",
  "pharChkTime": "2025-08-27 17:57:59",
  "pharCode": "111",
  "pharDeptCode": "A50.04",
  "pharDeptName": "儿一科",
  "pharName": "李四",
  "pharProfttlCodg": "1",
  "pharProfttlName": "执业药师",
  "prscDrName": "王五",
  "psnCertType": "01",
  "rxTraceCode": "612*****261"
}

拼接后的 signstr

appId=****&data={...}&encType=SM4&signType=SM2&timestamp=1757326792488&version=1.0.0&key=****

2. 私钥转换

原始私钥(Base64 编码)需先转化为十六进制格式:

CommonUtil.byteArrayToHex(this.decoder.decode(privateKey));

示例转换结果(脱敏):

69ECB3F1F419A015******************************B4DDE79CB3D8899542

3. 执行签名

  • 算法:GMObjectIdentifiers.sm2sign_with_sm3
  • 返回值为 Base64 编码签名字符串

示例签名结果:

j7hAVMocO14BPOli/bKxe6Md4d********************************yuPjhKnTLMZOMKI3kJngKfIUolg==

三、SM4 加密流程

1. 待加密的 data

仅取业务字段部分(如处方信息等 JSON)。

{
  "pharDeptName": "儿一科",
  "pharCertType": "01",
  "psnCertType": "01",
  "pharName": "李四",
  "hiRxno": "610*****978",
  "pharCertno": "21233121",
  "pharCode": "111",
  "pharDeptCode": "A50.04",
  "drCode": "D610724006954",
  "pharProfttlName": "执业药师",
  "certno": "612****316",
  "fixmedinsCode": "H61****077",
  "patnName": "张三",
  "prscDrName": "王五",
  "rxTraceCode": "612897******48261",
  "pharChkTime": "2025-08-27 17:57:59",
  "pharProfttlCodg": "1",
  "fixmedinsName": "某某医院",
  "mdtrtId": "127*****90"
}

2. 生成 SM4 Key

SM4Util.encryptEcb(CommonUtil.stringToHexString(appID.substring(0, 16)), secret)
  .substring(0, 16)
  .toUpperCase()
  .getBytes();

3. 执行加密

  • 模式:SM4/ECB/PKCS7Padding
  • 调用 Cipher cipher = generateEcbCipher(...);
  • 最终输出为十六进制字符串

示例加密结果(脱敏)

f4064a4d7e1291ea1aef4fc8436a4a22...
...39ef651c08fb50eec96f23b184d2506

四、请求参数组装

最终请求中:

  • data 字段需删除
  • 新增 encData (即 SM4 加密结果)
  • 搭配 signData(SM2 签名结果)

所有参数放入 TreeMap 后再发起请求。


五、总结

  1. SM2 用于签名,保证请求数据完整性与来源可靠。
  2. SM4 用于加密,保护敏感数据传输过程中的安全性。
  3. 请求参数需 排序 + 拼接 appSecret,生成签名字符串。
  4. data 字段仅参与签名,不直接传输;传输的是 encData

六、调用时序流程图

sequenceDiagram
    participant Client as 客户端
    participant SM2 as SM2签名模块
    participant SM4 as SM4加密模块
    participant Server as 服务端接口

    Client->>Client: 构建业务参数(JSON)
    Client->>Client: 剔除 signData/encData/extra
    Client->>Client: 参数排序 + 拼接 appSecret = signstr

    Client->>SM2: 使用私钥对 signstr 执行 SM2 签名
    SM2-->>Client: 返回 signData(Base64)

    Client->>SM4: 使用 appId/secret 生成 SM4 key
    Client->>SM4: 对 data(JSON) 执行 SM4/ECB/PKCS7Padding 加密
    SM4-->>Client: 返回 encData(Hex)

    Client->>Client: TreeMap 组装请求参数
    Note right of Client: 删除原 data,保留<br/>appId、timestamp、signData、encData 等

    Client->>Server: 发送请求(url+path)
    Server-->>Client: 返回处理结果(JSON)


七、签名与加密整体流程图

graph TD
     A[开始] --> B[构建业务参数 JSON]
    B --> C[剔除 signData / encData / extra 字段]
    C --> D[参数排序]
    D --> E[拼接 appSecret 得到 signstr]

    E --> F1[SM2 签名]
    F1 --> F2[生成 signData Base64]

    D --> H[提取 data JSON]
    H --> I[生成 SM4 Key 使用 appId 和 secret]
    I --> J[SM4 加密 ECB PKCS7Padding]
    J --> K[生成 encData Hex]

    F2 --> L[TreeMap 组装参数]
    K --> L
    L --> M[删除 data 保留 signData 和 encData]
    M --> N[发送请求 url+path]
    N --> O[服务端接收并校验]
    O --> P[返回结果 JSON]
    P --> Q[结束]
    

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]