夜雨聆风学习资料网

ARTICLE · 1066036

某办公软件Authorization 算法逆向分析还原

某办公软件Authorization 算法逆向分析还原

阅读须知

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号联系作者立即删除

1. 分析目标

目标请求示例:

GET https://xxx.com/api/v3/dev/oauth/wechat/accesstoken?appid=&code=HTTP/1.1Authorization: WPS-2:AqY7ik9XQ92tvO7+NlCRvA==:87cf234b497e595316c29cc759a86ad887f7f28cContent-MD5: d74b6ae81883eb7705c5eddd1697567fContent-Type: application/json; charset=utf-8Date: Tue, 14 Jul 2026 03:02:56 GMT

目标是还原 Authorization 头的生成逻辑。

说明:本报告基于当前 APK/JADX 与 libcp-lib.so/IDA 分析结果


2. 结论摘要

Authorization 格式:

Authorization = "WPS-2:" + AK + ":" + Signature

其中:

AK = AqY7ik9XQ92tvO7+NlCRvA==SK = 6a80e70986ade7855786e399f14bce4620129ba1

签名公式:

Signature = SHA1_HEX(SK + Content-MD5 + Content-Type + Date)

字段顺序严格为:

SKContent-MD5Content-TypeDate

最终:

Authorization: WPS-2:AqY7ik9XQ92tvO7+NlCRvA==:SHA1_HEX(    "6a80e70986ade7855786e399f14bce4620129ba1"    + Content-MD5    + "application/json; charset=utf-8"    + Date)

3. AK/SK 来源

3.1 AK 来源

AK 不是 native 解密得到的,而是 Java 层硬编码:

AqY7ik9XQ92tvO7+NlCRvA==

相关位置包括:

  • cn.wpsx.support.base.net.okhttp3.helper.BaseRequestHelper
  • cn.wps.yunkit.YunConfig
  • cn.wps.yunkit.api.qing.QingBaseApi
  • cn.wps.yunkit.api.company.CompanyBaseApi

BaseRequestHelper 构造函数:

public BaseRequestHelper() {    String sk = InternalGlobal.m376729c().mo170015c();    this.f436857b = sk;    this.f436858c = new SignKeyPair(        "AqY7ik9XQ92tvO7+NlCRvA==",        sk    );}

所以:

AK = "AqY7ik9XQ92tvO7+NlCRvA=="

Base64 解码后为 16 字节:

02a63b8a4f5743ddadbceefe365091bc

它更像服务端分配的 access id / key id,而不是需要解密的密文。


3.2 SK 来源

SK 由 native 层 CpUtil.getPS() 解密得到。

调用链:

BaseRequestHelper()  -> InternalGlobal.m376729c().mo170015c()  -> NetLibFuncConfig.mo170015c()  -> CpUtil.getPS("helper_baserequesthelper_cp")  -> libcp-lib.so / Java_cn_wps_cp_util_CpUtil_getPS  -> RSA public decrypt(blob_0x275EB)  -> 6a80e70986ade7855786e399f14bce4620129ba1

NetLibFuncConfig.mo170015c()

@Overridepublic String mo170015c() {    return CpUtil.getPS("helper_baserequesthelper_cp");}

IDA 中定位:

"helper_baserequesthelper_cp" string address: 0x2547cxref in Java_cn_wps_cp_util_CpUtil_getPS: 0x5b108cipher blob: 0x275EB

解密结果:

helper_baserequesthelper_cp = 6a80e70986ade7855786e399f14bce4620129ba1

4. Authorization 生成代码路径

关键类:

cn.wpsx.support.base.net.okhttp3.helper.SignKeyPair

关键方法:

public void m377103j(HttpRequest httpRequest, InputStream inputStream, String contentType)

核心逻辑:

public void m377103j(HttpRequest httpRequest, InputStream inputStream, String contentType) {    String contentMd5 = m377098c(inputStream, httpRequest.getUrl());    String date = m377095d(new Date());    this.f436874c = m377101h(contentType, contentMd5, date);    String authorization = String.format(        Locale.US,        "WPS-%d:%s:%s",        2,        this.f436872a,        this.f436874c    );    builder.addHeader("Content-Type", contentType);    builder.addHeader("Content-MD5", contentMd5);    builder.addHeader("Date", date);    builder.addHeader("Authorization", authorization);}

5. Signature 计算

关键方法:

public final String m377101h(String contentType, String contentMd5, String date) {    MessageDigest md = MessageDigest.getInstance("SHA1");    md.update(this.f436873b.getBytes("utf-8")); // SK    md.update(contentMd5.getBytes("utf-8"));    md.update(contentType.getBytes("utf-8"));    md.update(date.getBytes("utf-8"));    return hex(md.digest());}

还原为伪代码:

signature = sha1_hex(    SK    + content_md5    + content_type    + date)authorization = f"WPS-2:{AK}:{signature}"

6. Content-MD5 计算规则

关键方法:

public final String m377098c(InputStream inputStream, String url) {    if (inputStream != null) {        byte[] body = readAll(inputStream);        if (body != null && body.length > 0) {            return md5(body);        }    }    return md5(url.getBytes("utf-8"));}

6.1 GET 请求

GET 请求通常无 body,因此:

Content-MD5 = MD5(path + "?" + query)

示例:

MD5("/api/v3/dev/oauth/wechat/accesstoken?appid=wx3ebc9e606b4f9242&code=083gL9Ga1A7s4M0fD2Ga1hEPtK2gL9Gh")= d74b6ae81883eb7705c5eddd1697567f

6.2 POST 请求

POST 请求有 body 时:

Content-MD5 = MD5(raw_body_bytes)

注意必须使用原始请求 body 字节,不能重新格式化 JSON。


7. Date 格式

关键方法:

public static String m377095d(Date date) {    SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ", Locale.US);    sdf.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));    return sdf.format(date).concat("GMT");}

格式:

Tue, 14 Jul 2026 03:02:56 GMT

签名时必须使用请求实际携带的 Date 头,否则第三段签名不同。


8. 抓包验证

8.1 GET 示例

请求:

GET https://xxx.com/api/v3/dev/oauth/wechat/accesstoken?appid=&code=Content-MD5: d74b6ae81883eb7705c5eddd1697567fContent-Type: application/json; charset=utf-8Date: Tue, 14 Jul 2026 03:02:56 GMT

签名源:

6a80e70986ade7855786e399f14bce4620129ba1d74b6ae81883eb7705c5eddd1697567fapplication/json; charset=utf-8Tue, 14 Jul 2026 03:02:56 GMT

计算结果:

SHA1(...) = 87cf234b497e595316c29cc759a86ad887f7f28c

最终:

Authorization: WPS-2:AqY7ik9XQ92tvO7+NlCRvA==:87cf234b497e595316c29cc759a86ad887f7f28c

与抓包一致。


8.2 POST 示例

请求:

POST https://xxx.com/api/v3/app/oauth/verifyContent-MD5: b08cac688dd521f1860e0f8534f659b1Content-Type: application/json; charset=utf-8Date: Tue, 14 Jul 2026 03:08:02 GMT

签名源:

6a80e70986ade7855786e399f14bce4620129ba1b08cac688dd521f1860e0f8534f659b1application/json; charset=utf-8Tue, 14 Jul 2026 03:08:02 GMT

计算结果:

SHA1(...) = 23e2d5a82456409a265d6145d2c7598fdccce8e8

最终:

Authorization: WPS-2:AqY7ik9XQ92tvO7+NlCRvA==:23e2d5a82456409a265d6145d2c7598fdccce8e8

与抓包一致。


9. 最终算法

AK = "AqY7ik9XQ92tvO7+NlCRvA=="SK = CpUtil.getPS("helper_baserequesthelper_cp")   = "6a80e70986ade7855786e399f14bce4620129ba1"if request_body exists and len(body) > 0:    content_md5 = MD5(raw_body_bytes)else:    content_md5 = MD5(path_query.encode("utf-8"))content_type = "application/json; charset=utf-8"date = GMT HTTP date stringsignature = SHA1_HEX(SK + content_md5 + content_type + date)authorization = "WPS-2:" + AK + ":" + signature

免责声明

本报告仅供安全研究和学习目的,请勿用于非法用途。本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号联系作者立即删除

相关学习资料