# 消息加解密和签名验证

更新时间:2023-02-08 11:00:04

在服务商开发运维授权小程序过程中,会收到第三方平台服务器推送的系统消息。出于安全考虑,第三方平台服务器会向第三方应用发送加密后的消息,因此,服务商需要解密后才能查看具体信息。

目前第三方平台服务器会向第三方应用【授权事件接收 URL】以 HTTP POST 方式推送:

1、component_ticket;

2、授权成功通知、授权解除通知、第三方应用找回授权码通知;

会向第三方应用【消息与事件接收 URL】以 HTTP POST 方式推送:

3、代授权小程序提审代码的审核结果;

这种系统事件推送通知,均以POST方式请求,content-type 均为 json。接收到后需要直接返回 content-type 为json的结果如下:

{
  "result":1,
  "message_id": "RESDFLSDFP8456LKJFOI45JIUO3J534" // 取自POST请求中的msgId
}

注意:

1、上述提到的【授权事件接收 URL】、【消息与事件接收 URL】、【消息验证 TOKEN】、【消息加密解密 KEY】,均为服务商在创建第三方应用时填写的。

2、服务商接收到第三方平台服务器推送给第三方应用的消息后必须返回约定成功内容,否则第三方平台服务器会认为推送失败,从而进行多次重试(重试 3 次)。

# 消息验证

目的是保证消息是来自于快手的第三方平台服务器。

消息格式为:

{
  "encryptedMsg": "ERWLSF239035_=FW3LFLSFL23L4K023JLKLSFSMF2L3-S3HNFA",  //加密后信息
  "msgId": "a63cae97-3ded-4f76-be21-8d45112ee06f",  // 消息ID
  "componentAppId": "ks656399649443988986",  // 三方应用ID
  "timestamp": 1625740912167  // 毫秒
}
  • msgId: 为消息ID,用于唯一定义消息,排查问题。
  • timestamp:事件发生的时间戳,毫秒。
  • encryptedMsg:加密的消息体。
  • componentAppId:三方应用ID。
  • http header中的"kwaisign"的内容:消息签名。

验签方式如下:

  1. 取出 http 的 request body 作为字符串,记为 bodyStr。
  2. 取出 http header 中的 "kwaisign" 字段的内容,记为 signStr。
  3. 取出在后台设置的【消息验证Token】字符串,记为 tokenStr。
  4. 计算 sha1Hex(bodyStr + tokenStr) = calcSign。
  5. 判断 calcSign == signStr 是否为true。

验签示例代码如下(Java):

        @RequestMapping(value = "/ks_msg_hook", method = RequestMethod.POST)
    @ResponseBody
    public JSONObject messageHook(HttpServletRequest request, @RequestBody String bodyStr) throws Exception {
        //验签过程
        String signStr = request.getHeader("kwaisign");
        String tokenStr = "xxxxxxxxxxxxxxxxxxxxxxx"; //后台的【消息校检Token】字段
        String unSignStr = bodyStr + tokenStr;

        String calcSign = DigestUtils.sha1Hex(unSignStr);
        if (calcSign.equals(signStr)) {
            log.info("验签成功"); ;
        }else {
            log.info("验签失败");
        }

        // 解密过程省略,步骤见下一小节

          // 返回确认成果结果
        JSONObject result = new JSONObject();
        result.put("result", 1);
        result.put("message_id", messageId);
        return result;
    }

# 消息解密

上面的消息中,encryptedMsg 字段为加密消息体,可以使用【消息加密解密 KEY】通过AES解密算法进行解密。

解密示例代码如下(Java):

/**
 * @param encryptedText 待解密的字符串
 * @param base64Key 消息加密解密KEY
 * @return 返回解密后的字符串
 */
public static String decrypt(String encryptedText, String base64Key) throws Exception {
        // 计算AES算法的密钥和IV参数:
        // - 密钥:【消息加密解密KEY】直接通过 base64 decode获取。长度为32个byte数组。
        // - iv: 取密钥的前16个byte数组。
        byte[] secretKey = Base64.decodeBase64(base64Key);
        byte[] iv = new byte[16];
        System.arraycopy(secretKey, 0, iv, 0, 16);

        // 设置解密算法和模式
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));

        // 解密计算
        byte[] encryptBytes = Base64.decodeBase64(encryptedText);
        byte[] originalBytes = cipher.doFinal(encryptBytes);

        // 二进制解密结果转换成string
        return new String(originalBytes, UTF_8);
}
Copyright ©2025, All Rights Reserved