钉钉开发文档

消息体加密解密

更新时间: 2018-6-12

业务场景

为了验证调用者的合法性,钉钉在回调url中增加了消息签名,以参数signature标识,应用需要验证此参数的正确性后再解密。

术语说明

1.signature是签名,用于验证调用者的合法性。
2.EncodingAESKey:注册应用提供的数据加密密钥。用于消息体的加密,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,是AESKey的Base64编码。解码后即为32字节长的AESKey。

3.AESKey=Base64_Decode(EncodingAESKey + “=”),是AES算法的密钥,长度为32字节。AES采用CBC模式,数据采用PKCS#7填充;IV初始向量大小为16字节,取AESKey前16字节。具体详见:http://tools.ietf.org/html/rfc2315。

4.msg为消息体明文,格式为JSON。

钉钉服务器会把msg消息体明文编码成encrypt,encrypt = Base64_Encode(AES_Encrypt[random(16B) + msg_len(4B) + msg + key]),是对明文消息msg加密处理后的Base64编码。其中random为16字节的随机字符串;msg\_len为4字节的msg长度,网络字节序;msg为消息体明文;key对于ISV开发来说,填写对应的suitekey,$key对于普通企业开发,填写企业的Corpid。最终传给回调者的是encrypt,字段名为encrypt。

验证步骤

1.企业计算签名:dev_msg_signature=sha1(sort(token、timestamp、nonce、msg_encrypt))。sort的含义是将参数按照字母字典排序,然后从小到大拼接成一个字符串。
2.比较dev_msg_signature和回调接口中推送的字段signature是否相等,相等则表示验证通过。
以上验证步骤已经包含在钉钉提供的加解密库中,开发者不需要自己实现,代码体现如下:

dingTalkEncryptor = new DingTalkEncryptor(Env.TOKEN, Env.ENCODING_AES_KEY, Env.SUITE_KEY);
/*
获取从encrypt解密出来的明文
*/
plainText = dingTalkEncryptor.getDecryptMsg(msgSignature, timeStamp, nonce, encrypt);

加解密方案说明

明文msg的加密过程:

msg_encrypt = Base64_Encode( AES_Encrypt[random(16B) + msg_len(4B) + msg + key] ); AES加密的buf由16个字节的随机字符串、4个字节的msg长度、明文msg和key组成。其中msg_len为msg的字节数,网络字节序;
key对于ISV来说,填写对应的suitekey; key对于普通企业开发,填写企业的Corpid。

加密方案对应的解密方案:

取出返回的JSON中的encrypt字段;
对密文BASE64解码:aes_msg=Base64_Decode(encrypt);
使用AESKey做AES解密:rand_msg=AES_Decrypt(aes_msg);
验证解密后key、msg\_len,当为第三方应用回调事件时,CorpID的内容为suitekey; 去掉rand\_msg头部的16个随机字节,4个字节的msg\_len,和尾部的CorpID即为最终的消息体原文msg。

加解密库下载

Java库和demo:
参考QuickStart服务端代码CallbackController,请仔细阅读文档中的内容,如果有问题请提交工单

php库和demo:
库地址:https://github.com/injekt/openapi-demo-php/tree/master/isv/crypto
demo地址:https://github.com/injekt/openapi-demo-php/blob/master/isv/receive.php

C#库和demo:
库地址:https://github.com/ian-cuc/suite-demo-c-/tree/master/API
demo地址:https://github.com/ian-cuc/suite-demo-c-/blob/master/receive.ashx.cs

调试工具

回调接口本地调试方案:由于回调接口需要在外网环境接收钉钉服务器的推送,假如开发者暂时没有外网地址,需要在本地调试回调接口的加解密方案,可以在本地环境构造推送。具体构造参数示例:

URL后面带的参数:signature=5a65ceeef9aab2d149439f82dc191dd6c5cbe2c0&timestamp=1445827045067&nonce=nEXhMP4r

Post参数:

{
  "encrypt":"1a3NBxmCFwkCJvfoQ7WhJHB+iX3qHPsc9JbaDznE1i03peOk1LaOQoRz3+nlyGNhwmwJ3vDMG+OzrHMeiZI7gTRWVdUBmfxjZ8Ej23JVYa9VrYeJ5as7XM/ZpulX8NEQis44w53h1qAgnC3PRzM7Zc/D6Ibr0rgUathB6zRHP8PYrfgnNOS9PhSBdHlegK+AGGanfwjXuQ9+0pZcy0w9lQ=="
}

Token:123456
数据加密密钥(ENCODING_AES_KEY):4g5j64qlyl3zvetqxz5jiocdr586fn2zvjpa8zls3ij
suitekey:suite4xxxxxxxxxxxxxxx