企业微信_会话内容存档解密-java

会话内容存档解密

0.说明

感谢:

https://github.com/hufeiyaya/QyChat 提供了参考代码。

https://blog.csdn.net/u011056339/article/details/105704995 提供了参考代码和公钥秘钥生成工具。

写该文章的原因是:
    0.本人对公钥秘钥对加密不了解...记录一下!
    1.工作原因,需要将企业微信的聊天记录进行留存。
    2.在网上找到了代码,但是踩了坑,因此想写详细一些分享出来,避免其他人踩坑。(其实半天就可以搞定的,但是我做了一两天...)

1.前期准备

1.1.下载会话内容存档SDK

-在会话内容存档功能中的API文档

image-20200717160656444.png

https://work.weixin.qq.com/wework_admin/frame#financial/corpEncryptData

windows:
    https://wwcdn.weixin.qq.com/node/wework/images/sdk_win.7z
Linux:
    https://wwcdn.weixin.qq.com/node/wework/images/sdk_20200401.zip

1.2.创建公钥与私钥(PKCS1 PKCS8都可以,解密方式不同)

http://tool.chacuo.net/cryptrsaprikey

1.2.1.生成公钥私钥对

image-20200717161429829.png

1.2.2.配置公钥

每次设置版本会+1

(踩坑的原因是我没有认真看API文档说明,因为我阅读能力差)
坑:当前公钥版本下接收存档的消息,必须要由当前版本公钥对应的私钥进行解密。当时我在测试的时候一直用第一条数据进行测试,但是我的公钥版本已经变了好几次了。因此测试的时候,拿最后面最新的数据,被公钥加密的消息,进行解密。
image-20200717162442146.png

--Q:如何查看消息公钥版本?(Demo跑起来再回来看)

//参考API文档参数说明,根据需要进行修改
Finance.GetChatData(sdk, 100, 1000, "", "", 10, slice);
System.out.println("getChatData :" + Finance.GetContentFromSlice(slice));
image-20200717162951705.png
image-20200717163150985.png

2.创建Java工程

image-20200717161542830.png

我是直接将SDK的工程导入了IDEA,结构如图

3.创建测试Demo

创建Bean

//简写,setter/getter需要自行生成哦
public class ChatDatas implements Serializable {
   private static final long serialVersionUID = 1700586620843625231L;
   private String errcode;
   private String errmsg;
   private List<Qychat> chatdata;
....
//简写,setter/getter需要自行生成哦
public class Qychat implements Serializable{
   private static final long serialVersionUID = 1L;
   private String seq;
   private String msgid;
   private String publickey_ver;
   /**加密RSA秘钥*/
   private String encrypt_random_key;
   /**加密消息*/
   private String encrypt_chat_msg;
//测试类
public class FinanceTestDemo {
   /**
    * 打开企业微信管理端-我的企业-最下面
    * */
   private static String corpid = "企业微信id";
   /**
    * 打开企业微信管理端-管理工具-会话内容存档-secret
    * */
   private static String secret = "会话内容存档-secret";

   public static void main(String[] args){
      long sdk = Finance.NewSdk();
      System.out.println(Finance.Init(sdk,  corpid,secret));
      long slice = Finance.NewSlice();
      //参考API文档参数说明,根据需要进行修改
      Finance.GetChatData(sdk, 100, 1000, "", "", 10, slice);
      System.out.println("getChatData :" + Finance.GetContentFromSlice(slice));
      JSONObject chatResultJson = JSONObject.parseObject(Finance.GetContentFromSlice(slice));
      ChatDatas cdata = JSON.toJavaObject(chatResultJson, ChatDatas.class);
      List<Qychat> list = cdata.getChatdata();
      for (Qychat qychat : list) {
         String encrypt_chat_msg = qychat.getEncrypt_chat_msg();
         String privateKey = null;
         try {
            //注意生成公钥时使用的是 PKCS8还是PKCS1
            privateKey = RSAUtil_PKCS8.getPrivateKey(qychat.getEncrypt_random_key());
         } catch (Exception e) {
//          e.printStackTrace();
         }
         System.out.println(privateKey);
         // 将获取到的数据进行解密操作
         long msg = Finance.NewSlice();
         Finance.DecryptData(sdk, privateKey, encrypt_chat_msg, msg);
         String content = Finance.GetContentFromSlice(msg);
         System.out.println("------------获得解密后的内容-----------"+content);
      }
      Finance.FreeSlice(slice);
      Finance.DestroySdk(sdk);
   }
}

4.创建工具类(两种:PKCS1/PKCS8)

/**
 * 1.生成公钥私钥对: http://tool.chacuo.net/cryptrsaprikey
 *        -1.1选择RSA秘钥对,2048位+PKCS#1,生成的【公钥】放在企业微信的配置中用来加密(消息加密公钥-版本号:x [设置]),【私钥】放在下面用来解密
 *        -1.2公钥版本不同,解密的私钥也不同,在企业微信后台中,每次修改公钥版本都会改变
 *        -1.3消息存档时,已经被当前版本公钥加密。因此,会出现 不同消息使用不同公钥的情况。
 *
 * 2.参考博客:https://blog.csdn.net/u011056339/article/details/105704995
 *
 * 3.Java RSA 加密工具类 参考: https://blog.csdn.net/qy20115549/article/details/83105736
 */

4.1.RSAUtil_PKCS1

public class RSAUtil_PKCS1 {

    private static final String privKeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n" +
            "MIICXAIBAAKBgQDV+keBFjpFITkYRRWQ8xcBXvWuRWsdI5pO17olqL2i7zTIeiD+\n" +
            "Gu4m77TCrEmoh7j9/imwKDA4CD/fCtI/JWMSR4YFWFBzfsNZZYOpSye03a9r5Oiy\n" +
            "wnUGha06mQiJfHdZ87bKixrWdRbs9VQISbaPCLHDjY+tO0qJ1dy/rK9LqwIDAQAB\n" +
            "AoGAS4cCizVpzvHZoc/su1OamLDIfkFhfBEPi+nIejdz7FmLo4G8OIUP761ne9lt\n" +
            "TG/Po9N9KoEc+AItbVB4ArLzIyBBnNLMBZN3pEbOAdosgIGWKQ5PtJYqMgHwf2rB\n" +
            "Mh+q2+zqO0Q/KkQcF1+FqgxMP0gULVnsy8UqgTdKD3E60/UCQQD/ahv3hBk05GuW\n" +
            "ev1DwTjbwgTw2cG1a48FobSFNkkDqQNYSHmLjAMaUbcPQclAOJAvziaoHKbNWm0o\n" +
            "E9owe0xnAkEA1nfaTP8DByRQeAIE6LEgjpmBCxoR/gnblfOSTaYkMrLi6bytdIF9\n" +
            "d/MBMUCakeyqvgweIcFsw3J3v7RFZwS8HQJAfZLQry9+KRgIoVJUhGRSLRFF1pho\n" +
            "+WYpSg6Hr1rSKP+GingPcgFjYSQ9yT2B0ZY9pZNIRCzaAWps8mBYTK/CDwJBAKuE\n" +
            "VnVVTFqd1CzlkH93iI4CfY0fYFxGDfKyRMMMp85T+dzsI9wU4v7WvJFjFTq2hyZO\n" +
            "Epr0UcNM/C+4P/jPdJECQFec9fqAltWb6Vjqta2spesB/4W0ZqYt3GyWPMCYOLlr\n" +
            "knhoOD7Sve0bTQNHRa5HKLufuV6zbMk2t8mbQKRcKwk=\n" +
            "-----END RSA PRIVATE KEY-----";

    // 用此方法先获取秘钥
    public static String getPrivateKey(String encrypt_random_key) throws Exception {

        String privKeyPEMnew = privKeyPEM.replaceAll("\\n", "").replace("-----BEGIN RSA PRIVATE KEY-----", "").replace("-----END RSA PRIVATE KEY-----", "");
        byte[] bytes = java.util.Base64.getDecoder().decode(privKeyPEMnew);

        DerInputStream derReader = new DerInputStream(bytes);
        DerValue[] seq = derReader.getSequence(0);
        BigInteger modulus = seq[1].getBigInteger();
        BigInteger publicExp = seq[2].getBigInteger();
        BigInteger privateExp = seq[3].getBigInteger();
        BigInteger prime1 = seq[4].getBigInteger();
        BigInteger prime2 = seq[5].getBigInteger();
        BigInteger exp1 = seq[6].getBigInteger();
        BigInteger exp2 = seq[7].getBigInteger();
        BigInteger crtCoef = seq[8].getBigInteger();

        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2, exp1, exp2, crtCoef);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        // 64位解码加密后的字符串
        byte[] inputByte = Base64.getMimeDecoder().decode(encrypt_random_key);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        String s = new String(cipher.doFinal(inputByte));

        return s;
    }
}

4.2.RSAUtil_PKCS8

public class RSAUtil_PKCS8 {

   private static final String privKeyPEM = "-----BEGIN PRIVATE KEY-----\n" +
         "MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDgAqLhj+56BOjr\n" +
         "1Q9M/Te87Wr1u0zkUz8Xk+6buQgTpKsKoNNOb7R05K6GKnipNyppazOO6jRKDB1N\n" +
         "vtOj7yX1oDZzSQTwlYiC3+4HqFKfVU9D9C+BLq15IW3VFE9hJubT8H93wpKYAgeC\n" +
         "YvUZ6nl9yqdsk0worW7qHvHqr+BUQkoU6x2XNwqemvxhcgeptxJUceqQz0qivgc6\n" +
         "qgdswRgeTWlBsItXDqdQxDhNl7P1mFGknzfspWMvTEzVh0f6K/ADT+EDnrD3CP81\n" +
         "ExIeLkquVw3uBPBsqfkew8IqMTeRgD/riNjT7ZkGGI0h7KJtSvIeEePbExrIEOr0\n" +
         "nFBlrFfRAgMBAAECggEBAKdj0OrUbtNnD9YKI6DYJupaTu7IwzBqtF6eAFME9PAY\n" +
         "wGb0vnGCL0qaB3/iBMANpPeZT7GfeOtMGimaSvPZJHhi+80x5ysP0i5ZvriiIvtE\n" +
         "+DJDKaxSgPZe8H+k6ZwjQaFluRp4nqpP+eSIpbZz68z/vhP4DZTn5FW2Qfeo7OyI\n" +
         "Jnd4uVOBPd3cfjqkJqftWYnoM5znE/eINISp6LbZWO3YOWNJkPaUULHiynPYkhxx\n" +
         "/ePZXoaSyMwJxU1/5fzWxyifoNGKfGm/rkBjNfkiujIwvCKRZXTmF9n34I3eS2VM\n" +
         "fwLf6PXaoy7adP25BVDHyZi0WsFHzJaZKABKVcCGFwECgYEA8/BVJ6Yhayzbkzsq\n" +
         "qQ1jeaOmbzvwXn4/bq6v1Jl8wMjjZxay7lPFihODsJBw5bIstxEl6+InLZKVxT1E\n" +
         "fToULVbLG4l0+XJznpGmd94EG4l0A6PhFRQAkuwR3J0/AK47cJUMUYIdjjC/zyFr\n" +
         "1wasWB2lOZWfH1zBiDPK4m99aXkCgYEA6xYOwMmdMgxikf2410KqMpdmsCTpCczR\n" +
         "gN9KZthbstxMQfd0BlsvJd0Rtg+628lpFRk3q1lA97Q+f/IQC9wmYGEqaYJiWgHw\n" +
         "GwdoYya2uR+vlvRGppl8GrvdhcDPCY6lnwSxSP+zmXO7Cl0vq0tzqT4OgrjcxdXg\n" +
         "98VCeo4toxkCgYEAmFU2+EGYkPM8U58ZLuS7gBSgNMp7eqbgOeBA0UTgUQuiZpgY\n" +
         "ORh7PZSeIj6xId+4aMH+qmVaDe2CNd/iy0jfnMicoZ+fOr8sUJOoHya37fJSToui\n" +
         "XaVWDmn5ZYmU9HnZiJ6rSKM2jbsHrPO0Al2adpRcv68d5VnpSYL+aZUx/hECgYEA\n" +
         "mL6uO0lEX/54FU97yDHCkDibOhvhZsKz5T4wA37UpfRJgBseQfsBOWLYXSj/Sksl\n" +
         "gdXuu+C0O73bVhqbnnkeXkUD36Yd7UmRDp7Tjoja9JHH7xcsyJa1clFab8uFOjp7\n" +
         "FkVgQ4QQ18XAY82EaZIOxopRt1IR8GE1WQfMWAEFWhECgYEA3XNF5z+Yc+NS/cbj\n" +
         "JOue18iCHAvR2LPbyiabtgj9o3omhrJ//xayL0zUjpqd7URffBKw25uxjXYppxgB\n" +
         "JhBJ5/bRmHUWadk62d+4Jei+VKz5ZOv2fBAFnE5rq0t6wuPIEvQ7+pYw6iAiASmM\n" +
         "R6xBKhxiWW3le10SzqFtOCV/ZkY=\n" +
         "-----END PRIVATE KEY-----\n";

   // 用此方法先获取秘钥
   public static String getPrivateKey(String encrypt_random_key) throws Exception {

      String privKeyPEMnew = privKeyPEM.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "");

      byte[] decoded = Base64.getDecoder().decode(privKeyPEMnew);
      RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA")
            .generatePrivate(new PKCS8EncodedKeySpec(decoded));
      // 64位解码加密后的字符串
      byte[] inputByte = Base64.getDecoder().decode(encrypt_random_key);

      // RSA解密
      Cipher cipher = Cipher.getInstance("RSA");
      cipher.init(Cipher.DECRYPT_MODE, priKey);
      String outStr = new String(cipher.doFinal(inputByte));

      return outStr;
   }
}

5.测试结果

image-20200717163457038.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。