回调加解密说明
教育号加解密应用场景主要有两种, 一是对回调进行加解密处理,一个是对入参与返回的参数进行加解密
鉴于加解密算法相对复杂,教育号提供了算法库。目前已有/java/golang/python等语言版本。均提供了 回调解密 和 接口入参加密 相关算法, 开发者可根据自身需要下载开发者可参考 加解密库下载 里调相关代码
前置说明
教育号在推送消息给企业时,会对消息内容做AES加密,以XML格式POST到企业应用的URL上。
阅读本章节前,需要了解以下术语:
- Signature: 消息签名,用于验证请求是否来自教育号(防止攻击者伪造)
- EncodingAESKey:用于消息体的加密,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,是AESKey的Base64编码。解码后即为32字节长的AESKey
AESKey=Base64_Decode(EncodingAESKey + “=”)
- AESKey:AES算法的密钥,长度为32字节。
AES采用CBC模式,数据采用PKCS#7填充至32字节的倍数;IV初始向量大小为16字节,取AESKey前16字节,详见:http://tools.ietf.org/html/rfc2315
- Token:随机字符串,用于生成Signature与消息体对比请求签名正确性
- Msg:为消息体明文,格式为XML(后续会支持Json格式)
解密流程
示例: 开发者为应用801159配置的加解密参数如下:
Token:"SdBcJhEt1X0izTA25VuGZFtAw7"
EncodingAESKey:"HE2TfUnOpq8jWN5ZbFwMcvcmkcbXjPIn8afCSk4GT6q"
收到的教育号回调请求如下
POST /push/ticket?msg_signature=83c29839d75980d98018c96094ef202ec129241a×tamp=1701932041667&nonce=6284853754 HTTP/1.1
Host: test-oapi.epaas.qq.com
Content-Length: 613
<xml>
<ToUserName><![CDATA[801159]]></ToUserName>
<Encrypt><![CDATA[ZI0Yy1szqujD5QZDIipGQErFlcI9xgE6bQqPw2iBajlYhnSdeOgOfqoniicEqtfucLFDkLPc6D9pG6QvuPZejVa0xq9H9kvMm9DdqrFPvb35EguA1Xljj4psixoJcAMAUCWodD4R74OpCdoXjwRdZzJOKVORbhtj+pNGY0tj3rbXa3obtGkebuJWBn+g2rBpgX0OvN6RhVSOkVbFl472f7sVFSxUd6Gg2OUi6no7xLMmrOup4VPdOq7FEmWsaJ7xC04aZWu2Od3/OjOfavTvwgwMmYkdhrwG+TqhW3venmZYZqPNO42dlYHXrVSB3M1IADthWyDReKvDdPZ5LjAspA==]]></Encrypt>
<AgentID><![CDATA[218]]></AgentID>
</xml>
服务商收到post请求之后应该
1. 解析出url上的参数,包括消息体签名(msg_signature),时间戳(timestamp)以及随机数字串(nonce)
以上示例示例中
消息体签名(msg_signature):83c29839d75980d98018c96094ef202ec129241a
时间戳(timestamp):1701932041667
随机数字串(nonce):6284853754
2. 验证消息体签名的正确性。
dev_msg_signature=sha1(sort(token、timestamp、nonce、msg_encrypt))。
以上为生成签名伪代码流程,以上述http请求来示例,相关参数示例如下,msg_encrypt为请求body体xml的
token: "SdBcJhEt1X0izTA25VuGZFtAw7"
timestamp: "1701932041667"
nonce: "6284853754"
msg_encrypt: "ZI0Yy1szqujD5QZDIipGQErFlcI9xgE6bQqPw2iBajlYhnSdeOgOfqoniicEqtfucLFDkLPc6D9pG6QvuPZejVa0xq9H9kvMm9DdqrFPvb35EguA1Xljj4psixoJcAMAUCWodD4R74OpCdoXjwRdZzJOKVORbhtj+pNGY0tj3rbXa3obtGkebuJWBn+g2rBpgX0OvN6RhVSOkVbFl472f7sVFSxUd6Gg2OUi6no7xLMmrOup4VPdOq7FEmWsaJ7xC04aZWu2Od3/OjOfavTvwgwMmYkdhrwG+TqhW3venmZYZqPNO42dlYHXrVSB3M1IADthWyDReKvDdPZ5LjAspA=="
生成签名的具体流程为:
将参数值按照字母字典排序,然后从小到大拼接成一个字符串 sha1处理结果要编码为可见字符,编码的方式是把每字节散列值打印为16进制,全部小写
最后将函数生成的签名dev_msg_signature和入参的消息体签msg_signature名进行对比,两边完全一致则认为消息来源正确,且无篡改。
3. 将post请求的数据进行xml解析,并将标签的内容进行解密,解密出来的明文即是消息的明文,明文具体的含义需要参考对应回调的消息格式
解密流程如下
1.对密文BASE64解码
aes_msg=Base64_Decode(msg_encrypt)
2.使用AESKey做AES-256-CBC解密
rand_msg=AES_Decrypt(aes_msg)
以上http请求示例,解密出来的消息如下
GIcrm2mQZ9EGtLC5�<xml><SuiteId><![CDATA[801159]]></SuiteId><InfoType><![CDATA[suite_ticket]]></InfoType><TimeStamp>1701932041667</TimeStamp><SuiteTicket><![CDATA[757bf5faf4bcc77dc12c558e297efc92]]></SuiteTicket></xml>801159
3.去掉rand_msg头部的16个随机字节和4个字节的msg_len,截取msg_len长度的部分即为msg,剩下的为尾部的receiveid(即教育号appid)
得到的最终消息体和消息长度与接收appid如下
msg:<xml><SuiteId><![CDATA[801159]]></SuiteId><InfoType><![CDATA[suite_ticket]]></InfoType><TimeStamp>1701932041667</TimeStamp><SuiteTicket><![CDATA[757bf5faf4bcc77dc12c558e297efc92]]></SuiteTicket></xml>
msg_len:200
receiveid:801159
验证解密后的receiveid、msg_len
对比xmlbody体里的
以上步骤参考 加解密库下载 里message文件夹下相关代码