diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpProperties.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpProperties.java
index b2cc778ac..030478e53 100644
--- a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpProperties.java
+++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpProperties.java
@@ -38,6 +38,10 @@ public class WxCpProperties {
* 微信企业号应用 EncodingAESKey
*/
private String aesKey;
+ /**
+ * 微信企业号应用 会话存档私钥
+ */
+ private String msgAuditPriKey;
/**
* 微信企业号应用 会话存档类库路径
*/
diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
index cfcb16fe0..c4bc30036 100644
--- a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
+++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
@@ -18,7 +18,8 @@ public abstract class AbstractWxCpConfigStorageConfiguration {
String token = properties.getToken();
Integer agentId = properties.getAgentId();
String aesKey = properties.getAesKey();
- // 企业微信,会话存档路径
+ // 企业微信,私钥,会话存档路径
+ String msgAuditPriKey = properties.getMsgAuditPriKey();
String msgAuditLibPath = properties.getMsgAuditLibPath();
config.setCorpId(corpId);
@@ -32,6 +33,9 @@ public abstract class AbstractWxCpConfigStorageConfiguration {
if (StringUtils.isNotBlank(aesKey)) {
config.setAesKey(aesKey);
}
+ if (StringUtils.isNotBlank(msgAuditPriKey)) {
+ config.setMsgAuditPriKey(msgAuditPriKey);
+ }
if (StringUtils.isNotBlank(msgAuditLibPath)) {
config.setMsgAuditLibPath(msgAuditLibPath);
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java
index 63389aeb8..95f484675 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java
@@ -34,24 +34,26 @@ public interface WxCpMsgAuditService {
* 获取解密的聊天数据Model
*
* @param chatData getChatDatas()获取到的聊天数据
+ * @param pkcs1 使用什么方式进行解密,1代表使用PKCS1进行解密,2代表PKCS8进行解密 ...
* @return 解密后的聊天数据
* @throws Exception
*/
- WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception;
+ WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;
/**
* 获取解密的聊天数据明文
*
* @param chatData getChatDatas()获取到的聊天数据
+ * @param pkcs1 使用什么方式进行解密,1代表使用PKCS1进行解密,2代表PKCS8进行解密 ...
* @return 解密后的明文
* @throws Exception
*/
- String getChatPlainText(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception;
+ String getChatPlainText(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;
/**
* 获取媒体文件
* 针对图片、文件等媒体数据,提供sdk接口拉取数据内容。
- *
+ *
* 注意:
* 根据上面返回的文件类型,拼接好存放文件的绝对路径即可。此时绝对路径写入文件流,来达到获取媒体文件的目的。
* 详情可以看官方文档,亦可阅读此接口源码。
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolService.java
new file mode 100644
index 000000000..2e97bf075
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolService.java
@@ -0,0 +1,60 @@
+package me.chanjar.weixin.cp.api;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.bean.school.WxCpCustomizeHealthInfo;
+import me.chanjar.weixin.cp.bean.school.WxCpResultList;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 企业微信家校应用 复学码相关接口.
+ * https://developer.work.weixin.qq.com/document/path/93744
+ *
+ * 权限说明:
+ * 仅复学码应用可以调用
+ *
+ * @author Wang_Wong
+ * @date: 2022/5/31 9:10
+ */
+public interface WxCpSchoolService {
+
+ /**
+ * 获取老师健康信息
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/school/user/get_teacher_customize_health_info?access_token=ACCESS_TOKEN
+ *
+ * @param date
+ * @param nextKey
+ * @param limit
+ * @return
+ * @throws WxErrorException
+ */
+ WxCpCustomizeHealthInfo getTeacherCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException;
+
+ /**
+ * 获取学生健康信息
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/school/user/get_student_customize_health_info?access_token=ACCESS_TOKEN
+ *
+ * @param date
+ * @param nextKey
+ * @param limit
+ * @return
+ * @throws WxErrorException
+ */
+ WxCpCustomizeHealthInfo getStudentCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException;
+
+ /**
+ * 获取师生健康码
+ * 请求方式:POST(HTTPS)
+ * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/get_health_qrcode?access_token=ACCESS_TOKEN
+ *
+ * @param userIds
+ * @param type
+ * @return
+ * @throws WxErrorException
+ */
+ WxCpResultList getHealthQrCode(@NotNull List userIds, @NotNull Integer type) throws WxErrorException;
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
index 00be57b10..32606b205 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
@@ -400,6 +400,13 @@ public interface WxCpService extends WxService {
*/
WxCpOaService getOaService();
+ /**
+ * 获取家校应用复学码相关接口的服务类对象
+ *
+ * @return
+ */
+ WxCpSchoolService getSchoolService();
+
/**
* 获取家校应用健康上报的服务类对象
*
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
index 890253b11..fbfdbf383 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
@@ -49,6 +49,7 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH
private WxCpTagService tagService = new WxCpTagServiceImpl(this);
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this);
private WxCpOaService oaService = new WxCpOaServiceImpl(this);
+ private WxCpSchoolService schoolService = new WxCpSchoolServiceImpl(this);
private WxCpSchoolHealthService schoolHealthService = new WxCpSchoolHealthServiceImpl(this);
private WxCpLivingService livingService = new WxCpLivingServiceImpl(this);
private WxCpOaAgentService oaAgentService = new WxCpOaAgentServiceImpl(this);
@@ -494,6 +495,11 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH
return oaService;
}
+ @Override
+ public WxCpSchoolService getSchoolService() {
+ return schoolService;
+ }
+
@Override
public WxCpSchoolHealthService getSchoolHealthService() {
return schoolHealthService;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java
index 33465921c..fa802a1c6 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java
@@ -97,6 +97,7 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
Finance.FreeSlice(slice);
WxCpChatDatas chatDatas = WxCpChatDatas.fromJson(content);
if (chatDatas.getErrCode().intValue() != 0) {
+ Finance.DestroySingletonSDK(sdk);
throw new WxErrorException(chatDatas.toJson());
}
@@ -104,23 +105,33 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
}
@Override
- public WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception {
- String plainText = this.decryptChatData(chatData);
+ public WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
+ String plainText = this.decryptChatData(chatData, pkcs1);
return WxCpChatModel.fromJson(plainText);
}
- public String decryptChatData(WxCpChatDatas.WxCpChatData chatData) throws Exception {
- // 企业获取的会话内容将用公钥加密,企业可用自行保存的私钥解开会话内容数据,aeskey不能为空
- String priKey = cpService.getWxCpConfigStorage().getAesKey();
+ public String decryptChatData(WxCpChatDatas.WxCpChatData chatData, Integer pkcs1) throws Exception {
+ /**
+ * 企业获取的会话内容,使用企业自行配置的消息加密公钥进行加密,企业可用自行保存的私钥解开会话内容数据。
+ * msgAuditPriKey 会话存档私钥不能为空
+ */
+ String priKey = cpService.getWxCpConfigStorage().getMsgAuditPriKey();
if (StringUtils.isEmpty(priKey)) {
- throw new WxErrorException("请配置会话存档私钥【aesKey】");
+ throw new WxErrorException("请配置会话存档私钥【msgAuditPriKey】");
}
- String decryptByPriKey = WxCpCryptUtil.decryptByPriKey(chatData.getEncryptRandomKey(), priKey);
- // 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
+ String decryptByPriKey = WxCpCryptUtil.decryptPriKey(chatData.getEncryptRandomKey(), priKey, pkcs1);
+ /**
+ * 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
+ */
long sdk = Finance.SingletonSDK();
long msg = Finance.NewSlice();
+ /**
+ * 解密会话存档内容
+ * sdk不会要求用户传入rsa私钥,保证用户会话存档数据只有自己能够解密。
+ * 此处需要用户先用rsa私钥解密encrypt_random_key后,作为encrypt_key参数传入sdk来解密encrypt_chat_msg获取会话存档明文。
+ */
int ret = Finance.DecryptData(sdk, decryptByPriKey, chatData.getEncryptChatMsg(), msg);
if (ret != 0) {
Finance.FreeSlice(msg);
@@ -128,15 +139,17 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
throw new WxErrorException("msg err ret " + ret);
}
- // 明文
+ /**
+ * 明文
+ */
String plainText = Finance.GetContentFromSlice(msg);
Finance.FreeSlice(msg);
return plainText;
}
@Override
- public String getChatPlainText(WxCpChatDatas.@NonNull WxCpChatData chatData) throws Exception {
- return this.decryptChatData(chatData);
+ public String getChatPlainText(WxCpChatDatas.@NonNull WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
+ return this.decryptChatData(chatData, pkcs1);
}
@Override
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolServiceImpl.java
new file mode 100644
index 000000000..329c92406
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolServiceImpl.java
@@ -0,0 +1,67 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import com.google.gson.JsonObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.WxCpSchoolService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.school.WxCpCustomizeHealthInfo;
+import me.chanjar.weixin.cp.bean.school.WxCpResultList;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Optional;
+
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.School.*;
+
+/**
+ * 企业微信家校应用 复学码相关接口实现类.
+ * https://developer.work.weixin.qq.com/document/path/93744
+ *
+ * @author Wang_Wong
+ * @date: 2022/6/1 14:05
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class WxCpSchoolServiceImpl implements WxCpSchoolService {
+
+ private final WxCpService cpService;
+
+ @Override
+ public WxCpCustomizeHealthInfo getTeacherCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException {
+ String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_TEACHER_CUSTOMIZE_HEALTH_INFO);
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("date", date);
+ jsonObject.addProperty("limit", Optional.ofNullable(limit).orElse(100));
+ if (nextKey != null) {
+ jsonObject.addProperty("next_key", nextKey);
+ }
+ String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
+ return WxCpCustomizeHealthInfo.fromJson(responseContent);
+ }
+
+ @Override
+ public WxCpCustomizeHealthInfo getStudentCustomizeHealthInfo(@NotNull String date, String nextKey, Integer limit) throws WxErrorException {
+ String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_STUDENT_CUSTOMIZE_HEALTH_INFO);
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("date", date);
+ jsonObject.addProperty("limit", Optional.ofNullable(limit).orElse(100));
+ if (nextKey != null) {
+ jsonObject.addProperty("next_key", nextKey);
+ }
+ String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
+ return WxCpCustomizeHealthInfo.fromJson(responseContent);
+ }
+
+ @Override
+ public WxCpResultList getHealthQrCode(@NotNull List userIds, @NotNull Integer type) throws WxErrorException {
+ String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_HEALTH_QRCODE);
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("type", type);
+ jsonObject.addProperty("userids", userIds.toString());
+ String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
+ return WxCpResultList.fromJson(responseContent);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/school/WxCpCustomizeHealthInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/school/WxCpCustomizeHealthInfo.java
new file mode 100644
index 000000000..a28c3fa35
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/school/WxCpCustomizeHealthInfo.java
@@ -0,0 +1,150 @@
+package me.chanjar.weixin.cp.bean.school;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 获取健康信息.
+ *
+ * @author Wang_Wong
+ */
+@Data
+public class WxCpCustomizeHealthInfo extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -5028321625142879581L;
+
+ @SerializedName("health_infos")
+ private List healthInfos;
+
+ @SerializedName("template_id")
+ private String templateId;
+
+ @SerializedName("next_key")
+ private String nextKey;
+
+ @SerializedName("ending")
+ private Integer ending;
+
+ @Getter
+ @Setter
+ public static class HealthInfo implements Serializable {
+ private static final long serialVersionUID = -5696099236344075582L;
+
+ @SerializedName("userid")
+ private String userId;
+
+ @SerializedName("health_qrcode_status")
+ private Integer healthQrCodeStatus;
+
+ @SerializedName("self_submit")
+ private Integer selfSubmit;
+
+ @SerializedName("report_values")
+ private List reportValues;
+
+ @SerializedName("question_templates")
+ private List questionTemplates;
+
+ public static HealthInfo fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, HealthInfo.class);
+ }
+
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ }
+
+ @Getter
+ @Setter
+ public static class ReportValue implements Serializable {
+ private static final long serialVersionUID = -5696099236344075582L;
+
+ @SerializedName("question_id")
+ private Integer questionId;
+
+ @SerializedName("single_chose")
+ private Integer singleChose;
+
+ @SerializedName("text")
+ private String text;
+
+ public static ReportValue fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, ReportValue.class);
+ }
+
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ }
+
+ @Getter
+ @Setter
+ public static class QuestionTemplate implements Serializable {
+ private static final long serialVersionUID = -5696099236344075582L;
+
+ @SerializedName("question_id")
+ private Integer questionId;
+
+ @SerializedName("question_type")
+ private Integer questionType;
+
+ @SerializedName("title")
+ private String title;
+
+ @SerializedName("is_must_fill")
+ private Integer isMustFill;
+
+ @SerializedName("is_not_display")
+ private Integer isNotDisplay;
+
+ @SerializedName("option_list")
+ private List optionList;
+
+ public static QuestionTemplate fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, QuestionTemplate.class);
+ }
+
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ }
+
+ @Getter
+ @Setter
+ public static class OptionList implements Serializable {
+ private static final long serialVersionUID = -5696099236344075582L;
+
+ @SerializedName("option_id")
+ private Integer optionId;
+
+ @SerializedName("option_text")
+ private String optionText;
+
+ public static OptionList fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, OptionList.class);
+ }
+
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ }
+
+ public static WxCpCustomizeHealthInfo fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpCustomizeHealthInfo.class);
+ }
+
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/school/WxCpResultList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/school/WxCpResultList.java
new file mode 100644
index 000000000..c4305264d
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/school/WxCpResultList.java
@@ -0,0 +1,53 @@
+package me.chanjar.weixin.cp.bean.school;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 获取师生健康码.
+ *
+ * @author Wang_Wong
+ */
+@Data
+public class WxCpResultList extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -5028321625142879581L;
+
+ @SerializedName("result_list")
+ private List qrCodeList;
+
+ @Setter
+ @Getter
+ public static class QrCodeList extends WxCpBaseResp{
+
+ @SerializedName("userid")
+ private String userId;
+
+ @SerializedName("qrcode_data")
+ private String qrCodeData;
+
+ public static QrCodeList fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, QrCodeList.class);
+ }
+
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ }
+
+ public static WxCpResultList fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpResultList.class);
+ }
+
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java
index 1d7e9685d..10ae05ead 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java
@@ -174,6 +174,13 @@ public interface WxCpConfigStorage {
*/
String getAesKey();
+ /**
+ * 企微会话存档私钥
+ *
+ * @return
+ */
+ String getMsgAuditPriKey();
+
/**
* 获取企微会话存档系统库 绝对路径
*
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java
index c716eb735..442e437cf 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java
@@ -43,6 +43,10 @@ public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
private volatile String token;
private volatile String aesKey;
private volatile long expiresTime;
+ /**
+ * 会话存档私钥以及sdk路径
+ */
+ private volatile String msgAuditPriKey;
private volatile String msgAuditLibPath;
private volatile String oauth2redirectUri;
private volatile String httpProxyHost;
@@ -257,6 +261,11 @@ public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
return this.aesKey;
}
+ @Override
+ public String getMsgAuditPriKey() {
+ return this.msgAuditPriKey;
+ }
+
@Override
public String getMsgAuditLibPath() {
return this.msgAuditLibPath;
@@ -294,6 +303,15 @@ public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
this.msgAuditLibPath = msgAuditLibPath;
}
+ /**
+ * 设置会话存档私钥
+ *
+ * @param msgAuditPriKey 会话存档私钥
+ */
+ public void setMsgAuditPriKey(String msgAuditPriKey) {
+ this.msgAuditPriKey = msgAuditPriKey;
+ }
+
@Override
public String getOauth2redirectUri() {
return this.oauth2redirectUri;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java
index 89b939e61..662cf226b 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java
@@ -40,6 +40,7 @@ public class WxCpRedisConfigImpl implements WxCpConfigStorage {
private volatile String token;
private volatile String aesKey;
private volatile Integer agentId;
+ private volatile String msgAuditPriKey;
private volatile String msgAuditLibPath;
private volatile String oauth2redirectUri;
private volatile String httpProxyHost;
@@ -321,6 +322,11 @@ public class WxCpRedisConfigImpl implements WxCpConfigStorage {
return this.aesKey;
}
+ @Override
+ public String getMsgAuditPriKey() {
+ return this.msgAuditPriKey;
+ }
+
@Override
public String getMsgAuditLibPath() {
return this.msgAuditLibPath;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
index 93246c526..a9528929f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
@@ -177,6 +177,10 @@ public interface WxCpApiPathConsts {
String GET_HEALTH_REPORT_STAT = "/cgi-bin/health/get_health_report_stat";
String GET_REPORT_JOBIDS = "/cgi-bin/health/get_report_jobids";
String GET_REPORT_JOB_INFO = "/cgi-bin/health/get_report_job_info";
+
+ String GET_TEACHER_CUSTOMIZE_HEALTH_INFO = "/cgi-bin/school/user/get_teacher_customize_health_info";
+ String GET_STUDENT_CUSTOMIZE_HEALTH_INFO = "/cgi-bin/school/user/get_student_customize_health_info";
+ String GET_HEALTH_QRCODE = "/cgi-bin/school/user/get_health_qrcode";
}
interface Living {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java
index d36a1ce34..7b09bf4ad 100755
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java
@@ -2,14 +2,18 @@ package me.chanjar.weixin.cp.util.crypto;
import com.google.common.base.CharMatcher;
import com.google.common.io.BaseEncoding;
+import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
-import org.apache.commons.codec.binary.Base64;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerValue;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.util.Base64;
public class WxCpCryptUtil extends WxCryptUtil {
public WxCpCryptUtil(WxCpConfigStorage wxCpConfigStorage) {
@@ -28,29 +32,77 @@ public class WxCpCryptUtil extends WxCryptUtil {
}
/**
- * 会话存档接口解密私钥
- * 企业获取的会话内容将用公钥加密,企业用自行保存的私钥解开会话内容数据
+ * 判断使用PKCS8或者PKCS1进行解密
+ *
+ * @param encryptRandomKey 使用PUBLICKEY_VER指定版本的公钥进行非对称加密后base64加密的内容
+ * @param msgAuditPriKey 会话存档私钥
+ * @param pkcs1 使用什么方式进行解密,1代表使用PKCS1进行解密,2代表PKCS8进行解密 ...
+ * @return
+ * @throws Exception
+ */
+ public static String decryptPriKey(String encryptRandomKey, String msgAuditPriKey, Integer pkcs1) throws Exception {
+ if (pkcs1 == null) {
+ throw new WxErrorException("请配置会话存档解密方式");
+ }
+
+ if (pkcs1.intValue() == 1) {
+ return decryptPriKeyByPKCS1(encryptRandomKey, msgAuditPriKey);
+ }
+
+ return decryptPriKeyByPKCS8(encryptRandomKey, msgAuditPriKey);
+ }
+
+ /**
+ * PKCS8 解密私钥
*
* @param encryptRandomKey
* @param msgAuditPriKey
* @return
* @throws Exception
*/
- public static String decryptByPriKey(String encryptRandomKey, String msgAuditPriKey) throws Exception {
+ public static String decryptPriKeyByPKCS8(String encryptRandomKey, String msgAuditPriKey) throws Exception {
String privateKey = msgAuditPriKey.replaceAll("\\n", "")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll(" ", "");
- byte[] keyByte = Base64.decodeBase64(privateKey);
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyByte);
+ byte[] keyBytes = Base64.getDecoder().decode(privateKey);
+ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, priKey);
- byte[] utf8 = cipher.doFinal(Base64.decodeBase64(encryptRandomKey));
+ byte[] utf8 = cipher.doFinal(Base64.getDecoder().decode(encryptRandomKey));
+ return new String(utf8, "UTF-8");
+ }
+ /**
+ * 会话存档,PKCS1 解密私钥
+ * 企业获取的会话内容将用公钥加密,企业用自行保存的私钥解开会话内容数据
+ *
+ * @param encryptRandomKey 使用PUBLICKEY_VER指定版本的公钥进行非对称加密后base64加密的内容,需要业务方先base64 decode处理后,再使用指定版本的私钥进行解密,得出内容。String类型
+ * @param msgAuditPriKey 会话存档私钥
+ * @return
+ * @throws Exception
+ */
+ public static String decryptPriKeyByPKCS1(String encryptRandomKey, String msgAuditPriKey) throws Exception {
+ String privateKey = msgAuditPriKey.replaceAll("\\n", "")
+ .replace("-----BEGIN RSA PRIVATE KEY-----", "")
+ .replace("-----END RSA PRIVATE KEY-----", "")
+ .replaceAll(" ", "");
+
+ byte[] keyBytes = Base64.getDecoder().decode(privateKey);
+ DerValue[] seq = new DerInputStream(keyBytes).getSequence(0);
+ RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(seq[1].getBigInteger(), seq[2].getBigInteger(),
+ seq[3].getBigInteger(), seq[4].getBigInteger(),
+ seq[5].getBigInteger(), seq[6].getBigInteger(),
+ seq[7].getBigInteger(), seq[8].getBigInteger());
+
+ PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(Cipher.DECRYPT_MODE, priKey);
+ byte[] utf8 = cipher.doFinal(Base64.getDecoder().decode(encryptRandomKey));
return new String(utf8, "UTF-8");
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java
index 47e511be7..a07db2edf 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java
@@ -1,6 +1,5 @@
package me.chanjar.weixin.cp.api;
import com.google.common.collect.Lists;
-
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.bean.msgaudit.*;
@@ -18,7 +17,7 @@ import java.util.List;
* 企业微信会话内容存档测试类.
* 官方文档:https://developer.work.weixin.qq.com/document/path/91360
*
- * @author Wang_Wong
+ * @author Wang_Wong
* @date 2022-01-17
*/
@Slf4j
@@ -28,6 +27,7 @@ public class WxCpMsgAuditTest {
private static WxCpService cpService;
// com.binarywang.spring.starter.wxjava.cp.config.WxCpServiceAutoConfiguration
+ // WxCpServiceImpl.getAccessToken()
@Test
public void test() throws Exception {
@@ -39,20 +39,26 @@ public class WxCpMsgAuditTest {
cpService.setWxCpConfigStorage(config);
/**
- * 配置:
+ * 仔细配置:
*
- * wwa3bexxXXXXXX
- * 自定义agentId
- * xIpum7Yt4NMXXXXXXX
- * 2bSNqXXXXXXXX
- * MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZuPVMyVyMvJkdSCZA893B2pggd1r95T8k2QZgz+VejtaDJCbD60mYoW1Uwwlwuqy8W78M6MmXsskn+5XunyR1WJlJGqgi0OMVGYvSfkNb9kD50fM21CGLcN1y4miL9fVNBIsvJmIUeJCNS8TioAVGFvh2EgzjqTR1gHfDwu8If7rfGmTHkPYL8hQxMg/EQ3451JOIBHSa7EQSx64SIoVWEgSDFQjGEpjUiJRfciyyz+nTSkEDgFa9hpyTS6E0c/3Q5lVDFgIwTArC19XBFKb00PbcFuLriOIsTBX4K9XWBtefVXowAdqUVQDH6BNUIK7/iVPQ4L3p+F5DBOrx8I/7AgMBAAECggEBAK53C/nwEX2lU3ynaB/8SuMga274ta1mmmbIkdfaQA65nyOPQJEWZe8szBN0BoiSzgBR9JI/p+srlQ25CLgiRnDSAmMWPU1I3e72fZi7HPcAKakGmEKDUi4OzyVUUDp3aY3B6lZqB4Yn5o2S/b4sRI2ZspfKdxGncSYHP/Far3i6hzq2C1hbyYM6HkHPcrQ+z6ir6GxjLvHXssVJ+/C0HMsVIQAWPyEGbzWozS+EswmQ+itk+7cewiLWbaCSp6lsjHKGTxJwCxRes0nUt2SfkLnIUkDLxB7c6zDQJCn1K2UckCjNBlCWl+oDWLkLQ7UAJ+4IYYSslR4wXzRg8PplW8ECgYEA9VlEprEoG2oSn3HXIMFg0MANViQe89QJQdwd7D5h4FLxXQLItxqmZj77iktlzlICcK9WT9WHRY1AOilsuMaDmY0VH3Z8r/X9BU712KFJqMYH5CNxrqHOya3BG+CclEKToaOTmo9kiOpFAMNSuuWs6gvILJ0CKEmSUo5G9fJu4fkCgYEA4yypHoRZIP0mDdVDeVtdHHcq5JdWF6xbAFs4P57VHG1KDMWouk3IHSeO279gEIwcBAdaLcMMgFfzyQBwcisxjC76oyoZnbSntB7ZMFdPqALKfxIdleLilbASuRKesVAF+OgOx/yp/aQUeLG2pVBivgn2TyGMwjnxznTh9vh+vpMCgYEAmOva7krdRLkIgnjiLXhab8JEjbxVzoQKgRJBVE5NkxQffGmP0RC7Rl9bSQdVnRNgkfu3QGtGtQMlVRscuM6Cl+JnmASyErqvye89LJja4GcN5BRzdvVDflDeXBHThlU4zza1eVCGyQ+7ko4rsnIVJIvTaHs0LQguO2aStBk3I4ECgYAyBsO3VK3L9fNLWItjThtTCWsIq8rpq6reiTf5yqBjgi2sYlqlrDtFMFDlU190RWZl/Lh/G1TFbpjgypf4jEp89Ft9UugRMpc7sw9g9dk0xmiRUwvw1eXP0NZOqysHIPgvt+qJX7qPgHKBoaD3Bpy3/Lmg82Jr4xa8wECCgnZmwQKBgH7hirPs1/HqBrbxS726IZUf9QTmVkyOYIwzuwFYKb/+4caSah+iaXexVux0xS5tchj/6c1dQSKJmlegV8smIb6EEcko7llA1y1P5QFtXtaaRd07tTsv3BKEg496YLRjbxPzgJn6Fsoz3TTdGwESL8Q3I2h0WmVVhmr/rjr+RkWQ
+ * ww45xxx88865xxx
+ * xIpum7Yt4NMXcyxdzcQ2l_46BG4QIQDR57MhA45ebIw // secret
+ * 200000 // 会话存档的应用id
+ * // 回调配置的token
+ * // 回调配置的EncodingAESKey
+ *
+ * // 企业微信会话存档
+ * // 1、会话存档私钥,一定要加上前缀!!
+ * // 2、仔细配置windows以及linux环境sdk路径
+ * MIxxx893B2pggd1r95T8k2QxxxxbD6xxxxmXsskn+5XunyR1WJlJGqgi0OMVGYvSfkNb9kD50fM21CGLcN1y4miL9fVNBIsvJmIUeJCNS8TioAVGFvh2EgzjqTR1gH
+ * /www/osfile/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
+ *
*
* 注意:最好先配置lib开头的系统库,再配置sdk类库,配置绝对路径,最好配置为linux路径
- * windows:
- * D:/WorkSpace/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
- * linux:
- * /www/osfile/work_msg_storage/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
- *
+ * Windows:
+ * D:/WorkSpace/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
+ * Linux:
+ * /www/osfile/work_msg_storage/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
*/
/**
@@ -84,12 +90,13 @@ public class WxCpMsgAuditTest {
// Integer publickeyVer = chatData.getPublickeyVer();
// 获取明文数据
- final String chatPlainText = cpService.getMsgAuditService().getChatPlainText(chatData);
+ final String chatPlainText = cpService.getMsgAuditService().getChatPlainText(chatData, 2);
final WxCpChatModel wxCpChatModel = WxCpChatModel.fromJson(chatPlainText);
log.info("明文数据为:{}", wxCpChatModel.toJson());
// 获取消息数据
- final WxCpChatModel decryptData = cpService.getMsgAuditService().getDecryptData(chatData);
+ // https://developer.work.weixin.qq.com/document/path/91774
+ final WxCpChatModel decryptData = cpService.getMsgAuditService().getDecryptData(chatData, 2);
log.info("获取消息数据为:{}", decryptData.toJson());
/**
@@ -435,6 +442,16 @@ public class WxCpMsgAuditTest {
WxCpGroupChat room = cpService.getMsgAuditService().getGroupChat("wrOQpTDwAAyPl84GBJ40W5eWxWtixSCA");
log.info(room.toJson());
+
+ /**
+ * 获取access_token
+ * https://developer.work.weixin.qq.com/document/path/91039
+ * https://www.jianshu.com/p/dde171887d63
+ */
+ String getUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
+ String data = cpService.get(String.format(getUrl, config.getCorpId(), config.getCorpSecret()), null);
+
+
}
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpSchoolTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpSchoolTest.java
new file mode 100644
index 000000000..ddb1d47c0
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpSchoolTest.java
@@ -0,0 +1,72 @@
+package me.chanjar.weixin.cp.api;
+
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.bean.school.WxCpCustomizeHealthInfo;
+import me.chanjar.weixin.cp.bean.school.WxCpResultList;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.demo.WxCpDemoInMemoryConfigStorage;
+import org.testng.annotations.Test;
+
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * 企业微信家校应用 复学码相关接口.
+ * https://developer.work.weixin.qq.com/document/path/93744
+ *
+ * @author Wang_Wong
+ * @date: 2022/5/31 9:10
+ */
+@Slf4j
+public class WxCpSchoolTest {
+
+ private static WxCpConfigStorage wxCpConfigStorage;
+ private static WxCpService cpService;
+
+ @Test
+ public void test() throws WxErrorException {
+
+ /**
+ * 注意:
+ * 权限说明:仅复学码应用可以调用
+ */
+ InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml");
+ WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(inputStream);
+
+ wxCpConfigStorage = config;
+ cpService = new WxCpServiceImpl();
+ cpService.setWxCpConfigStorage(config);
+ String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+
+
+ /**
+ * 获取老师健康信息
+ * https://developer.work.weixin.qq.com/document/path/93744
+ */
+ WxCpCustomizeHealthInfo teacherCustomizeHealthInfo = cpService.getSchoolService().getTeacherCustomizeHealthInfo(date, null, null);
+ log.info("teacherCustomizeHealthInfo为:{}", teacherCustomizeHealthInfo.toJson());
+
+ /**
+ * 获取学生健康信息
+ * https://developer.work.weixin.qq.com/document/path/93745
+ */
+ WxCpCustomizeHealthInfo studentCustomizeHealthInfo = cpService.getSchoolService().getStudentCustomizeHealthInfo(date, null, null);
+ log.info("studentCustomizeHealthInfo为:{}", studentCustomizeHealthInfo.toJson());
+
+ /**
+ * 获取师生健康码
+ * https://developer.work.weixin.qq.com/document/path/93746
+ */
+ ArrayList userIds = Lists.newArrayList();
+ userIds.add("Wangkai");
+ WxCpResultList healthQrCode = cpService.getSchoolService().getHealthQrCode(userIds, 1);
+ log.info("healthQrCode为:{}", healthQrCode.toJson());
+
+ }
+
+}
diff --git a/weixin-java-cp/src/test/resources/test-config.sample.xml b/weixin-java-cp/src/test/resources/test-config.sample.xml
index 23e83e942..19241aba7 100644
--- a/weixin-java-cp/src/test/resources/test-config.sample.xml
+++ b/weixin-java-cp/src/test/resources/test-config.sample.xml
@@ -11,6 +11,18 @@
企业号通讯录里的某个tagid
网页授权获取用户信息回调地址
webhook链接地址的key值
-
+
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXAIBAAKBgQCTfm5cxqfglfOV7b/Z7OtTZZoZpk2EPTvVhn/ngsfKR899xRdR
+ 25s4h8HkK0XhxqYdOGoAdG3Gms+DvCSY/vu3UtImf0eZSNXpKZJBUnvUVjX4ivnr
+ Ohu2Rjw6O4gPjPnZKw8voCu0Nae1YLeNvFYw48PK7QrqmpHQv1sCd/8zHwIDAQAB
+ AoGAResz7xgfQghjsgnEHk9BGUY7YHhlG9CZWjYJ0Ro+ksYq9vClBuGHeitk/0CC
+ Pq7YVVbGbVPELFd8EvNwF/UcJsMlvFis16FzNS60Hn7M/o82gI6AVhSQmocoGhNs
+ MIKxTnXRqqlKFbCdcSfG+hQP7syHah6Z8UhLYuEA8s/ppd0CQQD99HTSvB4P5FfL
+ rlKTz6w6uh4qBYl53u5cLQxCRFGgXD6HvPnEwdzQf+2LCVM1zIhyxw2Kak1U467Q
+ 6JizEuHDTC2YljEbg/j+/AlpA/Ua5HQYnH5yD3DCK7rQyTvqE5gU+CfRbwTbLGre
+ fk/WJK4iqizgZobNRyUCQGB7jR5b8K7NsX7SoV7v/PFOsoj4G2W5q7LSz4GaoXGl
+ 3F+dSlXPYHdTow3dzfgVDldEzgoThs5UWMTQvBUZch0=
+ -----END RSA PRIVATE KEY-----
/www/osfile/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so