企业微信会话存档功能接入

企业微信会话存档功能接入

  • 简介
    • 官方文档
    • 上代码
    • 参考链接

简介

最近有个需求需要接入企业微信,将员工和客户的聊天记录存档,于是翻阅企业微信开放平台接口文档,写的那叫一个“好”(lan)。
网上文档也比较少,仔细阅读,翻阅一些文章后还是搞定了,于是分享一下。

官方文档

encrypt_random_key内容解密说明:
encrypt_random_key是使用企业在管理端填写的公钥(使用模值为2048bit的秘钥),采用RSA加密算法进行加密处理后base64 encode的内容,加密内容为企业微信产生。RSA使用PKCS1。
企业通过GetChatData获取到会话数据后:
a) 需首先对每条消息的encrypt_random_key内容进行base64 decode,得到字符串str1.
b) 使用publickey_ver指定版本的私钥,使用RSA PKCS1算法对str1进行解密,得到解密内容str2.
c) 得到str2与对应消息的encrypt_chat_msg,调用下方描述的DecryptData接口,即可获得消息明文。

看到这一串有点懵逼。翻译一下:
需要先接受到消息,接受到消息之后有一个encrypt_random_key字段,需要先对这个字段进行解密,解密需要用到秘钥进行,这个秘钥对应着公司的企业微信管理后台设置的公钥使用模值为2048bit的秘钥,使用rsa PKCS1算法进行解密。

具体怎么做呢

首先使用http://tool.chacuo.net/cryptrsapubkey 生成生成2048位 PKCS1秘钥对
生成秘钥对
将公钥复制到企业管理后台。
注意:java私钥需要把pkcs1转成pkcs8!!(我也不知道为什么,参考的github项目https://github.com/yang2wuhen/work_wx)
http://www.metools.info/code/c84.html 使用这个可以直接转换。转换后的私钥为解密的私钥。

上代码

public void synchronizationMessage() {int init = FinanceUtils.init(corpId, messageSecret);// 不为0 则为失败if (CommonConstant.INTEGER_ZERO != init){log.error("初始化微信sdk失败");}else {long slice = Finance.NewSlice();int i = Finance.GetChatData(FinanceUtils.sdk, 311, 1000, "", "", 3, slice);String getContentFromSlice = Finance.GetContentFromSlice(slice);log.info(getContentFromSlice);Finance.FreeSlice(slice);GetWeChatMessageResponseVo getWeChatMessageResponseVo = JSONObject.parseObject(getContentFromSlice, GetWeChatMessageResponseVo.class);if (0 != getWeChatMessageResponseVo.getErrCode()){log.error("获取消息失败");}else {slice = Finance.NewSlice();List<WeChatMessageChatDataResponseVo> chatDataList = getWeChatMessageResponseVo.getChatData();for (WeChatMessageChatDataResponseVo weChatMessageChatDataResponseVo : chatDataList) {// encrypt_random_key内容解密说明://encrypt_random_key是使用企业在管理端填写的公钥(使用模值为2048bit的秘钥),采用RSA加密算法进行加密处理后base64 encode的内容,加密内容为企业微信产生。RSA使用PKCS1。//企业通过GetChatData获取到会话数据后://a) 需首先对每条消息的encrypt_random_key内容进行base64 decode,得到字符串str1.//b) 使用publickey_ver指定版本的私钥,使用RSA PKCS1算法对str1进行解密,得到解密内容str2.//c) 得到str2与对应消息的encrypt_chat_msg,调用下方描述的DecryptData接口,即可获得消息明文。String encryptRandomKey = weChatMessageChatDataResponseVo.getEncryptRandomKey();String decryptByPrivateKey = RSAUtils.decryptByPriKey(encryptRandomKey, PRIKEY_PKCS8);int decryptData = Finance.DecryptData(FinanceUtils.sdk, decryptByPrivateKey, weChatMessageChatDataResponseVo.getEncryptChatMsg(),slice);if (CommonConstant.INTEGER_ZERO != decryptData){log.error("解密消息失败, encrypt_random_key = {}, PRIKEY_PKCS8 = {}, decryptByPrivateKey = {}, encrypt_chat_msg = {}",encryptRandomKey, PRIKEY_PKCS8, decryptByPrivateKey, weChatMessageChatDataResponseVo.getEncryptChatMsg());}else {String message = Finance.GetContentFromSlice(slice);log.info("获取到解密消息{}", message);JSONObject chatVo = JSONObject.parseObject(message);}}}Finance.FreeSlice(slice);}}

这个其实没啥作用

public class FinanceUtils {public static long sdk = 0;public static int init(String corpId, String messageSecret) {if (sdk == 0) {sdk = Finance.NewSdk();return Finance.Init(sdk, corpId, messageSecret);}else {return 0;}}
}

解密工具类

package com.iresearch.community.utils;import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;/*** RSAUtils* @author 小浩* @date 2020/11/26* @since 1.0.0*/
@Slf4j
public class RSAUtils {/*** 数字签名,密钥算法*/private static final String RSA_KEY_ALGORITHM = "RSA";/*** 数字签名签名/验证算法*/private static final String SIGNATURE_ALGORITHM = "MD5withRSA";/*** RSA密钥长度,RSA算法的默认密钥长度是1024密钥长度必须是64的倍数,在512到65536位之间*/private static final int KEY_SIZE = 1024;/*** 生成密钥对*/private static Map<String, String> initKey() throws Exception {KeyPairGenerator keygen = KeyPairGenerator.getInstance(RSA_KEY_ALGORITHM);SecureRandom secrand = new SecureRandom();/*** 初始化随机产生器*/secrand.setSeed("initSeed".getBytes());/*** 初始化密钥生成器*/keygen.initialize(KEY_SIZE, secrand);KeyPair keys = keygen.genKeyPair();byte[] pub_key = keys.getPublic().getEncoded();String publicKeyString = org.apache.commons.codec.binary.Base64.encodeBase64String(pub_key);byte[] pri_key = keys.getPrivate().getEncoded();String privateKeyString = org.apache.commons.codec.binary.Base64.encodeBase64String(pri_key);Map<String, String> keyPairMap = new HashMap<>();keyPairMap.put("publicKeyString", publicKeyString);keyPairMap.put("privateKeyString", privateKeyString);return keyPairMap;}/*** 密钥转成字符串** @param key* @return*/public static String encodeBase64String(byte[] key) {return org.apache.commons.codec.binary.Base64.encodeBase64String(key);}/*** 密钥转成byte[]** @param key* @return*/public static byte[] decodeBase64(String key) {return Base64.decodeBase64(key);}/*** 公钥加密** @param data      加密前的字符串* @param publicKey 公钥* @return 加密后的字符串* @throws Exception*/public static String encryptByPubKey(String data, String publicKey) throws Exception {byte[] pubKey = RSAUtils.decodeBase64(publicKey);byte[] enSign = encryptByPubKey(data.getBytes(), pubKey);return org.apache.commons.codec.binary.Base64.encodeBase64String(enSign);}/*** 公钥加密** @param data   待加密数据* @param pubKey 公钥* @return* @throws Exception*/public static byte[] encryptByPubKey(byte[] data, byte[] pubKey) throws Exception {X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, publicKey);return cipher.doFinal(data);}/*** 私钥加密** @param data       加密前的字符串* @param privateKey 私钥* @return 加密后的字符串* @throws Exception*/public static String encryptByPriKey(String data, String privateKey) throws Exception {byte[] priKey = RSAUtils.decodeBase64(privateKey);byte[] enSign = encryptByPriKey(data.getBytes(), priKey);return org.apache.commons.codec.binary.Base64.encodeBase64String(enSign);}/*** 私钥加密** @param data   待加密的数据* @param priKey 私钥* @return 加密后的数据* @throws Exception*/public static byte[] encryptByPriKey(byte[] data, byte[] priKey) throws Exception {PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey);KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, privateKey);return cipher.doFinal(data);}/*** 公钥解密** @param data   待解密的数据* @param pubKey 公钥* @return 解密后的数据* @throws Exception*/public static byte[] decryptByPubKey(byte[] data, byte[] pubKey) throws Exception {X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, publicKey);return cipher.doFinal(data);}/*** 公钥解密** @param data      解密前的字符串* @param publicKey 公钥* @return 解密后的字符串* @throws Exception*/public static String decryptByPubKey(String data, String publicKey) throws Exception {byte[] pubKey = RSAUtils.decodeBase64(publicKey);byte[] design = decryptByPubKey(org.apache.commons.codec.binary.Base64.decodeBase64(data), pubKey);return new String(design);}/*** 私钥解密** @param data   待解密的数据* @param priKey 私钥* @return* @throws Exception*/@SneakyThrowspublic static byte[] decryptByPriKey(byte[] data, byte[] priKey) {PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey);KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, privateKey);return getMaxResultDecrypt(data, cipher);}/*** 私钥解密** @param data       解密前的字符串* @param privateKey 私钥* @return 解密后的字符串* @throws Exception*/@SneakyThrowspublic static String decryptByPriKey(String data, String privateKey) {byte[] priKey = RSAUtils.decodeBase64(privateKey);byte[] design = decryptByPriKey(Base64.decodeBase64(data.getBytes("UTF-8")), priKey);return new String(design);}/*** RSA签名** @param data   待签名数据* @param priKey 私钥* @return 签名* @throws Exception*/public static String sign(byte[] data, byte[] priKey) throws Exception {// 取得私钥PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey);KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);// 生成私钥PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);// 实例化SignatureSignature signature = Signature.getInstance(SIGNATURE_ALGORITHM);// 初始化Signaturesignature.initSign(privateKey);// 更新signature.update(data);return Base64.encodeBase64String(signature.sign());}/*** RSA校验数字签名** @param data   待校验数据* @param sign   数字签名* @param pubKey 公钥* @return boolean 校验成功返回true,失败返回false*/public boolean verify(byte[] data, byte[] sign, byte[] pubKey) throws Exception {// 实例化密钥工厂KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);// 初始化公钥X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);// 产生公钥PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);// 实例化SignatureSignature signature = Signature.getInstance(SIGNATURE_ALGORITHM);// 初始化Signaturesignature.initVerify(publicKey);// 更新signature.update(data);// 验证return signature.verify(sign);}private static byte[] getMaxResultDecrypt(byte[] inputArray, Cipher cipher) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {int inputLength = inputArray.length;// 最大解密字节数,超出最大字节数需要分组加密int MAX_ENCRYPT_BLOCK = 128;// 标识int offSet = 0;byte[] resultBytes = {};byte[] cache = {};while (inputLength - offSet > 0) {if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);offSet += MAX_ENCRYPT_BLOCK;} else {cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);offSet = inputLength;}resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);}return resultBytes;}
}

在这里插入图片描述
还要注意的地方是这个sdk的文件要和demo的保持一致。

参考链接

https://blog.csdn.net/u011056339/article/details/105704995

https://github.com/yang2wuhen/work_wx

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/66542.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

midjourney/stable diffusion/太乙/文心一格/智源对比

挑战周杰伦《稻香》MV画面生成&#xff01;中英文绘画模型PK&#xff0c;Midjourney、DALLE2、Stable Diffusion、百度ERNIE-ViLG 2.0.._哔哩哔哩_bilibili本期视频小土同学使用周杰伦《稻香》评测了最近大热的 6 个 AI 绘画模型的效果&#xff1a;MidJourney、Stable Diffusio…

文心一格小程序,AI绘画产品

文章目录 AIGC什么是AI作画&#xff1f;Prompt文心一格文心一格小程序使用方法使用小程序进行AI绘图 AIGC的未来发展结语 AIGC AIGC&#xff08;AI Generated Content&#xff09;是指利用人工智能生成内容。是利用人工智能来生成你所需要的内容&#xff0c;GC的意思是创作内容…

文心一格:文心一格与ChatGPT的对比分析

文章目录 [toc]简介1.中文作诗2.中国历史文化3.代码能力4.内容查询5.多模态小结其他&#xff1a; 简介 根据文心一言网页端信息显示&#xff0c;目前最新发版是4月1号的版本&#xff0c;版本号是v1.0.3&#xff0c;应该是从上个月16号发布会以后又做了两版迭代。根据文心一言自…

虚拟号的运营商一般是哪家的?你知道吗?

虚拟号的运营商一般分三种来源&#xff0c;下面和小编 一起了解一下 吧&#xff1a; 一&#xff1a;运营商&#xff0c;中国联通&#xff0c;中国移动&#xff0c;中国电信都有。 二&#xff1a;虚拟运营商&#xff0c;阿里&#xff0c;华为&#xff0c;腾讯之类 三&#xff1a…

保存138亿年?!5维磁盘在南安普顿大学研制成功

南安普顿大学研究中心&#xff08;ORC&#xff09;的人员近日发表报告称&#xff0c;他们成功研制出 5D 磁盘&#xff0c;且可以存储上亿年。利用纳米技术将信息蚀刻到玻璃上&#xff0c;拥有良好的稳定性&#xff0c;可以承受一千度的高温&#xff0c;而由于玻璃的物理特性&am…

DeepMind成为AI界创业加速营:3年17名资深员工与高管离职

来源丨新智元 编辑丨极市平台 在过去几年里&#xff0c;谷歌旗下的人工智能研究实验室DeepMind已经成为顶级人工智能人才的宝库。 自从谷歌在2014年以约5亿美元的价格收购了这家总部位于伦敦的初创公司以来&#xff0c;DeepMind在AI界屡有划时代突破&#xff1a; 创建了可以在棋…

10种流行的机器学习算法进行泰坦尼克幸存者分析

还记得上世纪轰动全球的泰坦尼克吗? 1912年4月,正在处女航的泰坦尼克号在撞上冰山后沉没,2224名乘客和机组人员中有1502人遇难,这场悲剧轰动全球,遇难的一大原因正式没有足够的就剩设备给到船上的船员和乘客。 虽然幸存者活下来有着一定的运气成分,但在这艘船上,总有一…

英国南安普顿大学医院博士后职位—儿科重症监护

英国南安普顿大学医院博士后职位—儿科重症监护 [知识人网-博士后招聘]南安普顿大学&#xff08;University of Southampton&#xff0c;勋衔Soton&#xff09;&#xff0c;世界百强名校&#xff0c;英国顶尖学府&#xff0c;是罗素大学集团、世界大学联盟、科学与工程南联盟、…

南安普顿大学计算机专业如何,南安普顿大学计算机专业,南安最值得读的专业之一!...

主要的硕士课程包括&#xff1a; 计算机科学与软件工程 MSc Computer Science 计算机科学 MSc Artificial Intelligence 人工智能 MSc Cyber Security 网络安全 MSc Data Science 数据科学 MSc Software Engineering 软件工程 电子与电气工程 MSc Electronic Engineering 电子工…

知识图谱学习一:啥是知识图谱,用来干啥,怎么做?

一、啥是知识图谱 通俗的讲就是由知识形成的图。知识图 1.1 知识 知识让机器具有认知能力 百度AIG&#xff08;人工智能技术体系&#xff09;负责人王海峰曾提出&#xff0c;AI可以分为感知层和认知层&#xff0c;感知是人类和动物都有的能力&#xff0c;机器一定程度也可以比人…

2023年第六届广西大学生程序设计竞赛(正式赛)题解

比赛题目链接&#xff0c;可以继续提交代码: 2023年第六届广西大学生程序设计竞赛&#xff08;正式赛&#xff09; | 知乎&#xff1a;如何评价第六届广西大学生程序设计竞赛? 难度题号备注签到题A J K已给出题解和代码普通题B D E H已给出题解和代码中等题C G I给出 I 题代码…

Python 集合应用之“简易英语词汇生词本”

# 英语生词本""" 介绍&#xff1a;背单词是学英语最基础的一环&#xff0c;不少学生在背单词的过程中会整理自己的生词本&#xff0c;以不断拓展自己的词汇量。知识点&#xff1a;1、集合的创建、增添、删除、查询、遍历2、循环语句&#xff1a;while、for3、条…

英文诗歌数据-绘制英文词云图+英文本文分类(pytorch)

英文诗歌数据-绘制词云图本文分类 本项目包含&#xff1a; 1.文本处理 2.词云图绘制 3.文本分类 往期文章可以关注我的专栏 下巴同学的数据加油小站 或者关注CSDN 会不定期分享数据挖掘、机器学习、风控模型、深度学习、NLP等方向的学习项目 数据和完整代码文末链接可以下载 …

EasyNLP玩转文本摘要(新闻标题)生成

作者&#xff1a;王明、黄俊 导读 文本生成是自然语言处理领域的一个重要研究方向&#xff0c;具有丰富的实际应用场景以及研究价值。其中&#xff0c;生成式文本摘要作为文本生成的一个重要子任务&#xff0c;在实际应用场景中&#xff0c;包括新闻标题生成、摘要生成、关键词…

微信聊天记录生成词云图

微信聊天记录生成词云图 基本材料准备 电脑微信客户端、手机微信客户端、电脑mumu安卓模拟器&#xff08;安装微信和RE文件管理器&#xff09;、sqlcipher.exe、idea 获取微信聊天记录 电脑微信客户端备份聊天记录 微信左下角点击备份与恢复按钮出现如下弹窗 然后点击左侧…

txt文件英语单词词频统计

目录 一、需求分析 二、相关库列表 三、代码在此 四、一些问题 一、需求分析 把txt文件里的英语单词按照出现次数排序并生成csv文件&#xff0c;如果次数相同按照单词的md5值来排序 二、相关库列表 pandasrecollectionshashlib 三、代码在此 打开文件 txt_file open(f…

从文本中提取单词生成单词本

词频统计及单词提取 对一段英文文本做词频统计&#xff0c;提取单词&#xff0c;查词&#xff0c;最终生成一个单词本&#xff0c;生成的单词本可以导入Anki中学习。 问题分析 考虑到单词的变形&#xff0c;分词后先做词形还原&#xff0c;之后再进行词频统计。去除掉较为简…

给英文文章加音标,建生词表

先上个效果图 10. Thats WhyJimmy/ˈʤɪmi/ 吉米more/mɔː/ adj.更多的adv.更started/ˈstɑːtɪd/[start]v.开始&#xff0c;着手&#xff0c;发动were/wɜː/ (be/biː/ was/were been) v.是&#xff0c;存在painting/ˈpeɪntɪŋ/ n.画&#xff0c;绘画(艺术)different/…

生词提取方法,学以致用(用于生成学习计划)

为了能够更加便捷的吸收英文文章的养分,从下周开始,计划边读文章,边学习。在拿到一片英文材料后,首先识别其中已经知道的单词,然后识别自己不会的单词。根据不会的单词制定单词学习计划。单词掌握以后,学习内容,既确保学以致用,又确保能够吸收到优秀文章的养分。 我的初…

英语词缀与英语派生词词典读书笔记,并总结输出思维导图

大部分构词法知识在词根章节已说到&#xff0c;这里以词缀相关知识点作为重点讲述&#xff1b; 本文摘抄总结于 “英语词缀与英语派生词词典 - 李武平“ 往期文章&#xff1a; 英语词根与说文解字词典读书笔记&#xff0c;并总结输出思维导图 目录 思维导图 一、词缀概述…