feat: initial commit

This commit is contained in:
杨建炊 2025-09-24 17:16:04 +08:00
commit fa262e97f2
26 changed files with 863 additions and 0 deletions

39
.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
.kotlin
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

5
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,5 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

6
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,5 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" />
</settings>
</component>

14
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="15" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

62
pom.xml Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.shop</groupId>
<artifactId>crop</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<!-- Spring Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- MyBatis-Plus Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.6</version> <!-- 推荐使用最新稳定版 -->
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,128 @@
package com.tencent.wework;
/* sdk返回数据
typedef struct Slice_t {
char* buf;
int len;
} Slice_t;
typedef struct MediaData {
char* outindexbuf;
int out_len;
char* data;
int data_len;
int is_finish;
} MediaData_t;
*/
public class Finance {
public native static long NewSdk();
/**
* 初始化函数
* Return值=0表示该API调用成功
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] corpid 调用企业的企业id例如wwd08c8exxxx5ab44d可以在企业微信管理端--我的企业--企业信息查看
* @param [in] secret 聊天内容存档的Secret可以在企业微信管理端--管理工具--聊天内容存档查看
* @return 返回是否初始化成功
* 0 - 成功
* !=0 - 失败
*/
public native static int Init(long sdk, String corpid, String secret);
/**
* 拉取聊天记录函数
* Return值=0表示该API调用成功
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] seq 从指定的seq开始拉取消息注意的是返回的消息从seq+1开始返回seq为之前接口返回的最大seq值首次使用请使用seq:0
* @param [in] limit 一次拉取的消息条数最大值1000条超过1000条会返回错误
* @param [in] proxy 使用代理的请求需要传入代理的链接socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
* @param [in] passwd 代理账号密码需要传入代理的账号密码 user_name:passwd_123
* @param [out] chatDatas 返回本次拉取消息的数据slice结构体.内容包括errcode/errmsg以及每条消息内容
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
*/
public native static int GetChatData(long sdk, long seq, long limit, String proxy, String passwd, long timeout, long chatData);
/**
* 拉取媒体消息函数
* Return值=0表示该API调用成功
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] sdkFileid 从GetChatData返回的聊天消息中媒体消息包括的sdkfileid
* @param [in] proxy 使用代理的请求需要传入代理的链接socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
* @param [in] passwd 代理账号密码需要传入代理的账号密码 user_name:passwd_123
* @param [in] indexbuf 媒体消息分片拉取需要填入每次拉取的索引信息首次不需要填写默认拉取512k后续每次调用只需要将上次调用返回的outindexbuf填入即可
* @param [out] media_data 返回本次拉取的媒体数据.MediaData结构体.内容包括data(数据内容)/outindexbuf(下次索引)/is_finish(拉取完成标记)
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
*/
public native static int GetMediaData(long sdk, String indexbuf, String sdkField, String proxy, String passwd, long timeout, long mediaData);
/**
* @param [in] encrypt_key, getchatdata返回的encrypt_key
* @param [in] encrypt_msg, getchatdata返回的content
* @param [out] msg, 解密的消息明文
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
* @brief 解析密文
*/
public native static int DecryptData(long sdk, String encrypt_key, String encrypt_msg, long msg);
public native static void DestroySdk(long sdk);
public native static long NewSlice();
/**
* @return
* @brief 释放slice和NewSlice成对使用
*/
public native static void FreeSlice(long slice);
/**
* @return 内容
* @brief 获取slice内容
*/
public native static String GetContentFromSlice(long slice);
/**
* @return 内容
* @brief 获取slice内容长度
*/
public native static int GetSliceLen(long slice);
public native static long NewMediaData();
public native static void FreeMediaData(long mediaData);
/**
* @return outindex
* @brief 获取mediadata outindex
*/
public native static String GetOutIndexBuf(long mediaData);
/**
* @return data
* @brief 获取mediadata data数据
*/
public native static byte[] GetData(long mediaData);
public native static int GetIndexLen(long mediaData);
public native static int GetDataLen(long mediaData);
/**
* @return 1完成0未完成
* @brief 判断mediadata是否结束
*/
public native static int IsMediaDataFinish(long mediaData);
static {
System.loadLibrary("WeWorkFinanceSdk");
}
}

View File

@ -0,0 +1,15 @@
package org.shop.crop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author god23bin
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,114 @@
package org.shop.crop;
import com.alibaba.fastjson.JSON;
import com.tencent.wework.Finance;
import lombok.extern.slf4j.Slf4j;
import org.shop.crop.dto.BaseMessageDto;
import org.shop.crop.dto.ChatData;
import org.shop.crop.dto.Message;
import org.shop.crop.dto.MessagePullResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Slf4j
@RestController
public class CropController {
@Autowired
Long sdk;
@Autowired
private Environment environment; // nul
@GetMapping("/")
public String index() {
return "Hello, World!";
}
@GetMapping("/crop/chatData")
private List<Message> chatData(Long startSeq) throws IOException {
long limit = 1000;
long slice = Finance.NewSlice();
int ret = Finance.GetChatData(sdk, startSeq, limit, "", "", 1000, slice);
if (ret != 0) {
System.out.println("调用sdk拉取消息接口失败,失败消息为 ret = " + ret);
Finance.FreeSlice(slice);
return null;
}
String contentResult = Finance.GetContentFromSlice(slice);
log.info("内容字符串" + contentResult);
return decodeChatData(contentResult);
}
/**
* 解密chatData数据
*
* @param contentResult 拉取到的JSON原文
*/
public List<Message> decodeChatData(String contentResult) throws IOException {
// 1.基础信息解析
List<Message> messageList = new ArrayList<>();
MessagePullResponse messagePullResponse = JSON.parseObject(contentResult, MessagePullResponse.class);
if (messagePullResponse == null || messagePullResponse.getChatdata() == null) {
return Collections.emptyList();
}
List<ChatData> chatdataList = messagePullResponse.getChatdata();
// 2.解密数据
ClassPathResource resource = new ClassPathResource("private_key.pem");
if (!resource.exists()) {
return Collections.emptyList();
}
String privateKey = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
for (ChatData chatData : chatdataList) {
try {
// 解密ChatData数据
String plainTextJson = decrypt(chatData, privateKey);
BaseMessageDto messageDto = JSON.parseObject(plainTextJson, BaseMessageDto.class);
// 转换dto数据到entity
Message message = messageDto.convertToMessage();
message.setSeq(chatData.getSeq());
message.setOriginContent(plainTextJson);
messageList.add(message);
} catch (Exception e) {
log.error("解密失败{}", e.getMessage());
// 收集失败情况数据
e.printStackTrace();
}
}
// 4.保存消息和失败任务
return messageList;
}
/**
* 解密数据
*
* @param chatData 待解密数据
* @param privateKeyStr 私钥字符串
* @return 解密后的原文
*/
private String decrypt(ChatData chatData, String privateKeyStr) throws Exception {
// 1.解密EncryptRandomKey
String randomKey = EncodeUtils.decryptRandomKey(chatData.getEncryptRandomKey(), privateKeyStr);
// 2.调用SDK方法解密密文数据
long msg = Finance.NewSlice();
Finance.DecryptData(sdk, randomKey, chatData.getEncryptChatMsg(), msg);
String plaintext = Finance.GetContentFromSlice(msg);
Finance.FreeSlice(msg);
return plaintext;
}
}

View File

@ -0,0 +1,55 @@
package org.shop.crop;
import javax.crypto.Cipher;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.Base64;
/**
* @description 加解密工具
* @date 2024/11/21 9:22
*/
public class EncodeUtils {
public static PrivateKey getPrivateKey(String privKeyPEM) throws Exception {
String pem = privKeyPEM.trim();
// 清理头尾和空白
if (pem.contains("BEGIN PRIVATE KEY")) {
// PKCS#8 格式使用 URL-safe Base64
pem = pem
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
} else {
throw new IllegalArgumentException("Invalid private key format");
}
System.out.println("Raw private key: " + pem);
// 关键使用 url decoder
byte[] derBytes = Base64.getDecoder().decode(pem);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// PKCS#8
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(derBytes);
return keyFactory.generatePrivate(spec);
}
// 修复命名和字符集
public static String decryptRandomKey(String encryptedRandomKeyBase64, String privateKeyPem) throws Exception {
PrivateKey privateKey = getPrivateKey(privateKeyPem);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedRandomKeyBase64);;
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8); // 显式指定 UTF-8
}
}

View File

@ -0,0 +1,17 @@
package org.shop.crop;
//TIP <b>运行</b>代码请按 <shortcut actionId="Run"/>
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标
public class Main {
public static void main(String[] args) {
//TIP 当文本光标位于高亮显示的文本处时按 <shortcut actionId="ShowIntentionActions"/>
// 查看 IntelliJ IDEA 建议如何修正
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP <shortcut actionId="Debug"/> 开始调试代码我们已经设置了一个 <icon src="AllIcons.Debugger.Db_set_breakpoint"/> 断点
// 但您始终可以通过按 <shortcut actionId="ToggleLineBreakpoint"/> 添加更多断点
System.out.println("i = " + i);
}
}
}

View File

@ -0,0 +1,26 @@
package org.shop.crop;
import com.tencent.wework.Finance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public long sdk() {
System.out.println("Initializing SDK...");
long sdk = Finance.NewSdk();
int init = Finance.Init(
sdk,
"ww64f47cd0afc456a5",
"mEizahrSF6axdfWtSK_f73a3j6-sV02hhyGG7ogmTpM"
);
if (init != 0) {
Finance.DestroySdk(sdk);
throw new RuntimeException("初始化sdk失败,失败消息为ret = " + init);
}
return sdk;
}
}

View File

@ -0,0 +1,112 @@
package org.shop.crop.dao;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import org.apache.ibatis.annotations.Mapper;
import org.shop.crop.dto.Message;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@Data
@TableName("crop_message")
public class CropMessage {
/**
* 主键 ID自增
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 消息唯一标识全局 ID
*/
@TableField("msg_id")
private String msgId;
/**
* 消息动作 send, reply, delete
*/
@TableField("action")
private String action;
/**
* 发送方 ID
*/
@TableField("from")
private String from; // 注意'from' 是关键字不能直接写所以用注解指定
/**
* 接收方列表JSON 或逗号分隔
*/
@TableField("tolist")
private String tolist;
/**
* 群聊消息的房间 ID
*/
@TableField("roomid")
private String roomid;
/**
* 消息序号用于排序
*/
@TableField("seq")
private Long seq;
/**
* 消息类型 text, image, voice
*/
@TableField("msgtype")
private String msgtype;
/**
* 解密后的明文内容TEXT 类型
*/
@TableField("content")
private String content;
/**
* 消息发送时间
*/
@TableField("msgtime")
private LocalDateTime msgtime;
/**
* 消息状态是否已处理
*/
@TableField("status")
private Integer status;
/**
* 创建时间自动填充
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdTime;
/**
* 逻辑删除标志0:正常1:已删除
*/
@TableLogic
@TableField("is_deleted")
private Integer isDeleted;
/**
* 创建时间备用字段可选
*/
@TableField("gmt_created")
private LocalDateTime gmtCreated;
/**
* 修改时间自动填充
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime gmtModified;
}

View File

@ -0,0 +1,90 @@
package org.shop.crop.dto;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* @description 拉取基础信息dto
* @date 2024/11/21 17:50
*/
@Data
public class BaseMessageDto {
/**
* 消息id消息的唯一标识
*/
@JSONField(name = "msgid")
private String msgId;
/**
* 消息动作
*/
@JSONField(name = "action")
private String action;
/**
* 消息发送方id
*/
@JSONField(name = "from")
private String fromId;
/**
* 消息接收方列表
*/
@JSONField(name = "tolist")
private List<String> toList;
/**
* 群聊消息的id
*/
@JSONField(name = "roomid")
private String roomId;
/**
* 消息类型
*/
@JSONField(name = "msgtype")
private String msgType;
/**
* 消息发送的时间
*/
@JSONField(name = "msgtime")
private Long msgTime;
// getter setter
public Message convertToMessage(){
Message message = new Message();
message.setMsgId(this.msgId);
message.setAction(this.action);
message.setFromId(this.fromId);
message.setMsgTime(this.msgTime);
message.setMsgType(this.msgType);
message.setRoomId(this.roomId);
// id数组转为字符串
message.setToList(JSON.toJSONString(this.toList));
// 格式化时间
if(this.getMsgTime() != null){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");;
LocalDateTime dateTime = LocalDateTime.ofInstant(
Instant.ofEpochSecond(this.getMsgTime()),
ZoneId.systemDefault()
);
String timeStr=dateTime.format(formatter);
message.setMsgTimeStr(timeStr);
}
return message;
}
}

View File

@ -0,0 +1,18 @@
package org.shop.crop.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class ChatData {
private Long seq;
private String msgid;
@JsonProperty("publickey_ver")
private int publickeyVer;
@JsonProperty("encrypt_random_key")
private String encryptRandomKey;
@JsonProperty("encrypt_chat_msg")
private String encryptChatMsg;
// Getters and Setters
}

View File

@ -0,0 +1,81 @@
package org.shop.crop.dto;
import lombok.Data;
/**
* @description 消息基础信息
* @date 2024/11/21 10:46
*/
@Data
public class Message {
/**
* 自增主键
*/
private Long id;
/**
* 消息id消息的唯一标识
*/
private String msgId;
/**
* 消息动作
*/
private String action;
/**
* 消息发送方id
*/
private String fromId;
/**
* 消息发送方群名称
*/
private String fromNickName;
/**
* 消息发送方账户名称
*/
private String fromName;
/**
* 消息接收方列表
*/
private String toList;
/**
* 群聊消息的id
*/
private String roomId;
/**
* 消息序号
*/
private Long seq;
/**
* 消息类型
*/
private String msgType;
/**
* 解密后的明文消息内容
*/
private String originContent;
/**
* 消息发送的时间
*/
private Long msgTime;
/**
* 消息发送的时间(格式化)
*/
private String msgTimeStr;
/**
* 消息状态(是否被处理)
*/
private Integer status;
// Getters and Setters
}

View File

@ -0,0 +1,15 @@
package org.shop.crop.dto;
import lombok.Data;
import javax.annotation.Resource;
import java.util.List;
@Data
public class MessagePullResponse {
private int errcode;
private String errmsg;
private List<ChatData> chatdata;
// Getters and Setters
}

View File

@ -0,0 +1,9 @@
package org.shop.crop.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.shop.crop.dao.CropMessage;
@Mapper
public interface CropMessageMapper extends BaseMapper<CropMessage> {
}

View File

@ -0,0 +1 @@
server.port=8085

View File

@ -0,0 +1,17 @@
spring:
datasource:
url: jdbc:mysql://121.199.65.65:3306/shop?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8
username: sktod_test
password: JlZOLKtvma8Z19XF
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo.entity
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL
global-config:
db-config:
id-type: auto
logic-delete-value: 1
logic-not-delete-value: 0

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDbQp/SsUQgkhbX
tzdl/IJVkX9eYD4Qusd8nBhCRYkN6VfQpONX1r9qpvQOkXy0D0FDMpJnRnvDZLQ3
INduo8caj/101DxK6NHc5mYIJ9U2G561JyvDtkiS68qL1bepCK729lZSnvZBs6jr
A4ARN0DTtiFsjqK26oZXP4NqnfI8lYyMjaEvXHpQfA7+MI6LAyQAvC0KtM1681aM
+DAlsma88FRaPSEKvfTkWKvKgZUrfCUuDZscvczROlgNpEYHL4tNVPhQxaLOEB9m
iTfsAIVLdadJBVbhUq3qsTGBKa+Bo3XKMgkHVQl9KJaPv3r4SJ8cyv8mWCp+8VQ0
C2Mmw9rLAgMBAAECggEAODiWq5s5mVJsWgbIRTXgo5lJvATYlUiXYzvROpkC4hbN
phnJk5nkfi/uD5g9jhwFQ3pBsQqRLJJzZVld9Cg5ovV964mKJjkZqtdHHVs9YADo
CpJPNgFVDZs5Ve7Ih6bK2ldi3g+VnFY5f31nxsJQtJiUbHYMgfOT2+ZUx69VUDQd
AAB1D/V0SqbIBO9+MTInkfUeN91p+HPWNyLssMDOn9LWmD6N2irzpS6Uo47vpGT2
lyFUcTZ8iNebebLdRPTmv1wx7mziPc0CVjsKhAwSDwZJHdlUOiWEipDnJ2Kggy6i
T9tl+XpfDU3ymel7mg6MjTHErSA42K7IM2cYHYx4JQKBgQD1qgJndwxtF6NOMW6+
zDYQ454F3iuc3U6N7jNx5T4MxkR5TfIlwQypYEpxMzYI+mJUmrBebTM+eoC7i1QG
oOA+r/5DBsK8MTK/W9uyAyMlH+dEmvpkxWIKxS80U63m9ZuqAoXmSnWTcERD2nTg
eKnbPHJ23cZM+eSM1FzZKR6jzQKBgQDkfDmpXR0wZ0VwBpm2sKqO1cgZ3BVC9adN
ZngWl1OVvWZMY0jTEwVKYvDmBGpLX6eFk8D+8v+gKkkG87m8b4A6UnrJFXjXDGOf
cSitwLq7Xfuv97M2Pl9sfkMRs3w6CxZn0IM2SuMAqXeTlZiEhdk9P+lbQs4lkBXC
0ta9xtMQ9wKBgAFhIwJRuNAkAda+kFCh9OvDW2/M/5Gx7G7/l2pcTyzjMB8+QM3V
d6y7za81Cmiif0raU4M0OcVwCqkC70XaQWgYtOTLdOx5mj9Y1LL7MYdT1oicFiva
r+I/mTRCBAf3+RfLS9UvNdnOF5QJ9f4Mr2d7v2jLdgGYpZ/k0WCmynVBAoGBAL9i
Kq5pl1yG9EvMy46SVpsECPNYPNr0h2m2zhomMVY46mpLs6FRylmvnJce1aZC/kBO
LheflW0kXj0bg9WKE/9GPSEYAjsnXn8cOvWnfQIazApuUce64RUwgZ9fF0xLIz5c
Erw0aAaS7N1LLhC7SOccFKxjvecK1J93GXanBv6rAoGBAMtwLcBLHqbzefawkkow
UoFdz64nM5X47mFVyeVeIW85rUV6lLS2m1WtLYA12qRZ3nx4ls268P9CUYAH7Vh2
m/NWH3UJvNbYR1Z3SPs2qUfTVQ4cJC23bp0ij4J2GMBNGA8m+RTrQK7gTSofvG3d
LzNelpso5YqSOUw3nfTiNLBn
-----END PRIVATE KEY-----

Binary file not shown.

Binary file not shown.

Binary file not shown.