折腾:
【已解决】小花生app中调用接口parentChildReadingBookQuery2时timestamp和signature生成的逻辑
期间,需要把找到的java的md5参数加密的代码:
com/huili/readingclub/model/ModelSecurity.java
public static void addSignature(RequestParams paramRequestParams, String paramString)
{
Object localObject = paramString;
if (paramString.startsWith("http://www.xiaohuasheng.cn:83")) {
localObject = paramString.substring("http://www.xiaohuasheng.cn:83".length());
}
if (((String)localObject).startsWith("/UserService.svc/getToken/")) {
return;
}
int i;
if (StringUtil.isNullOrEmpty(MainActivity.userId)) {
i = 0;
} else {
i = Integer.parseInt(MainActivity.userId);
}
int j = i;
if (i < 1) {
j = 0;
}
paramString = "";
if (j != 0) {
paramString = getToken(MainActivity.userId);
}
long l = DateUtils.get1970ToNowSeconds();
if (j == 0)
{
paramString = new StringBuilder();
paramString.append(l);
paramString.append((String)localObject);
paramString.append(“AyGt7ohMR!xx#N");
paramString = StringUtil.md5(paramString.toString());
}
else
{
StringBuilder localStringBuilder = new StringBuilder();
localStringBuilder.append(MainActivity.userId);
localStringBuilder.append(l);
localStringBuilder.append((String)localObject);
localStringBuilder.append(paramString);
localStringBuilder.append(“AyGt7ohMR!xx#N");
paramString = StringUtil.md5(localStringBuilder.toString());
}
if (j != 0) {
paramRequestParams.addHeader("userId", MainActivity.userId);
}
localObject = new StringBuilder();
((StringBuilder)localObject).append(l);
((StringBuilder)localObject).append("");
paramRequestParams.addHeader("timestamp", ((StringBuilder)localObject).toString());
paramRequestParams.addHeader("signature", paramString);
}其中调用的代码是:
com/huili/readingclub/network/XutilsHttpClient.java
if (paramHttpMethod == HttpRequest.HttpMethod.GET) {
ModelSecurity.addSignature(localRequestParams, paramString1);
} else if (paramHttpMethod == HttpRequest.HttpMethod.POST) {
ModelSecurity.addSignature(localRequestParams, (String)JsonUtil.jsonToMap(paramString2).get("J"));
}想办法改造成python的。
并且,找到一组数据,供测试和验证
POST /Reading.svc/parentChildReadingBookQuery2 HTTP/1.1
Content-Type: application/json
Authorization: NSTp9~)NwSfrXp@\
userId: 1134723
timestamp: 1553845899
signature: c687d5dfa015246e6bdc6b3c27c2afea
Content-Length: 212
Host: www.xiaohuasheng.cn:83
User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.2; zh-cn; A0001 Build/KOT49H) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Cookie: ASP.NET_SessionId=sjwpqfwt013elkehr2ytl0sq
Cookie2: $Version=1
Accept-Encoding: gzip
Connection: keep-alive
{"J":"{\"userId\":\"1134723\",\"fieldName\":\"\",\"fieldValue\":\"全部类别\",\"theStageOfTheChild\":\"\",\"parentalEnglishLevel\":\"\",\"supportingResources\":\"有音频\",\"offset\":20,\"limit\":10}","C":0}其中有
J字段值
timestamp值
-》算出的signature是c687d5dfa015246e6bdc6b3c27c2afea
需要用Python复原此过程
然后就是想办法去写Python代码了。
除了写代码还,还要分析对应逻辑。
此处简单看了下,看来只需要关注:
StringBuilder localStringBuilder = new StringBuilder(); localStringBuilder.append(MainActivity.userId); localStringBuilder.append(l); localStringBuilder.append((String)localObject); localStringBuilder.append(paramString); localStringBuilder.append(“AyGt7ohMR!xx#N"); paramString = StringUtil.md5(localStringBuilder.toString());
java substring
而其中:
- MainActivity.userId:应该就是字符串”1134723″
- l:此处的时间戳
- long l = DateUtils.get1970ToNowSeconds();
- 此处直接赋值用于测试
- localObject:
- if (paramString.startsWith(“http://www.xiaohuasheng.cn:83”)) {
- localObject = paramString.substring(“http://www.xiaohuasheng.cn:83”.length());
- }
- -》看来是获取
- http://www.xiaohuasheng.cn:83/Reading.svc/parentChildReadingBookQuery2
- 中的:
- /Reading.svc/parentChildReadingBookQuery2
- paramString:此处传入的J的值
- 固定字符串:”AyGt7ohMR!xxx#N”
然后去计算出md5值
再去看看:
StringUtil.md5
com/huili/readingclub/utils/StringUtil.java
public static String md5(String paramString)
{
return Const.getMD5Str(paramString);
}com/huili/readingclub/utils/Const.java
public static final String getMD5Str(String paramString)
{
char[] arrayOfChar = new char[16];
char[] tmp6_5 = arrayOfChar;
tmp6_5[0] = 48;
char[] tmp12_6 = tmp6_5;
tmp12_6[1] = 49;
char[] tmp18_12 = tmp12_6;
tmp18_12[2] = 50;
char[] tmp24_18 = tmp18_12;
tmp24_18[3] = 51;
char[] tmp30_24 = tmp24_18;
tmp30_24[4] = 52;
char[] tmp36_30 = tmp30_24;
tmp36_30[5] = 53;
char[] tmp42_36 = tmp36_30;
tmp42_36[6] = 54;
char[] tmp49_42 = tmp42_36;
tmp49_42[7] = 55;
char[] tmp56_49 = tmp49_42;
tmp56_49[8] = 56;
char[] tmp63_56 = tmp56_49;
tmp63_56[9] = 57;
char[] tmp70_63 = tmp63_56;
tmp70_63[10] = 97;
char[] tmp77_70 = tmp70_63;
tmp77_70[11] = 98;
char[] tmp84_77 = tmp77_70;
tmp84_77[12] = 99;
char[] tmp91_84 = tmp84_77;
tmp91_84[13] = 100;
char[] tmp98_91 = tmp91_84;
tmp98_91[14] = 101;
char[] tmp105_98 = tmp98_91;
tmp105_98[15] = 102;
tmp105_98;
try
{
paramString = paramString.getBytes();
Object localObject = MessageDigest.getInstance("MD5");
((MessageDigest)localObject).update(paramString);
paramString = ((MessageDigest)localObject).digest();
int i = paramString.length;
localObject = new char[i * 2];
int j = 0;
int k = 0;
while (j < i)
{
int m = paramString[j];
int n = k + 1;
localObject[k] = ((char)arrayOfChar[(m >> 4 & 0xF)]);
k = n + 1;
localObject[n] = ((char)arrayOfChar[(m & 0xF)]);
j++;
}
paramString = new String((char[])localObject);
return paramString;
}
catch (Exception paramString) {}
return null;
}-》看起来,以及,希望是,普通的,md5加密算法
-》去找找python中的md5:
【已解决】Python中计算字符串的md5值
结果不对:
【已解决】为何Python中32字节的md值和小花生中getMD5Str计算出的md5值不同
期间去:
【已解决】小花生中如何得到getToken的计算逻辑以便得到正确的md5值可以正常请求接口
此处得到了user的token:
- userId是:1554276832
- 返回的token是:40d2267f-359e-4526-951a-66519e5868c3
接着再去试试模拟addSignature中的md5值生成的逻辑,看看值是否正确:

from hashlib import md5
def generateSignature(timestampInt, paramString):
userId = "1134723"
timestamp = "%s" % timestampInt
# localObject = "/Reading.svc/parentChildReadingBookQuery2"
# localObject = paramString
userToken = "40d2267f-359e-4526-951a-66519e5868c3"
# fixedSault = “AyGt7ohMR!xx#N"
secretKey = “AyGt7ohMR!xx#N"
# strToCalc = userId + timestamp + localObject + paramString + fixedSault
# strToCalc = timestamp + localObject + fixedSault
strToCalc = userId + timestamp + paramString + userToken + secretKey
print("strToCalc=%s" % strToCalc)
encodedStr = strToCalc.encode()
# encodedStr = strToCalc.encode("UTF-8")
# print("encodedStr=%s" % encodedStr)
md5Result = md5(encodedStr)
# print("md5Result=%s" % md5Result) # md5Result=<md5 HASH object @ 0x1044f1df0>
# md5Result = md5()
# md5Result.update(strToCalc)
# md5Digest = md5Result.digest()
# print("md5Digest=%s" % md5Digest) #
# print("len(md5Digest)=%s" % len(md5Digest))
md5Hexdigest = md5Result.hexdigest()
print("md5Hexdigest=%s" % md5Hexdigest)
print("len(md5Hexdigest)=%s" % len(md5Hexdigest))
# md5Hexdigest=585ad2765d147c7e918478a4ce843ed2
# md5Hexdigest=321ed614f7211147be90a45bbca94127
# md5Hexdigest=defc3b231167e16932ea923d616e2d40
# md5Hexdigest=c687d5dfa015246e6bdc6b3c27c2afea
return md5Hexdigest
# return md5Digest
if __name__ == "__main__":
timestampInt = 1553845899
# paramString = "{\"userId\":\"1134723\",\"fieldName\":\"\",\"fieldValue\":\"全部类别\",\"theStageOfTheChild\":\"\",\"parentalEnglishLevel\":\"\",\"supportingResources\":\"有音频\",\"offset\":20,\"limit\":10}"
paramString = """{"userId":"1134723","fieldName":"","fieldValue":"全部类别","theStageOfTheChild":"","parentalEnglishLevel":"","supportingResources":"有音频","offset":20,"limit":10}"""
generatedSignature = generateSignature(timestampInt, paramString)
# print("timestampInt=%d, paramString=%s-> %s" % (timestampInt, paramString, generatedSignature))即:
11347231553845899{"userId":"1134723","fieldName":"","fieldValue":"全部类别","theStageOfTheChild":"","parentalEnglishLevel":"","supportingResources":"有音频","offset":20,"limit":10}40d2267f-359e-4526-951a-66519e5868c3AyGt7ohMR!xxx#N算出的md5是:
c687d5dfa015246e6bdc6b3c27c2afea
和最开始抓包抓到的:
signature: c687d5dfa015246e6bdc6b3c27c2afea
是一致的了-》终于计算出正确的几个参数合并后的md5的signature值了。
【总结】
此处对于之前
src/main/java/com/huili/readingclub/model/ModelSecurity.java
的addSignature:
public static void addSignature(RequestParams requestParams, String str) {
if (str.startsWith(MyConfig.SERVER_PORT)) {
str = str.substring(MyConfig.SERVER_PORT.length());
}
if (!str.startsWith("/UserService.svc/getToken/")) {
StringBuilder stringBuilder;
int parseInt = StringUtil.isNullOrEmpty(MainActivity.userId) ? 0 : Integer.parseInt(MainActivity.userId);
if (parseInt < 1) {
parseInt = 0;
}
String str2 = "";
if (parseInt != 0) {
str2 = getToken(MainActivity.userId);
}
long j = DateUtils.get1970ToNowSeconds();
if (parseInt == 0) {
stringBuilder = new StringBuilder();
stringBuilder.append(j);
stringBuilder.append(str);
stringBuilder.append(MyConfig.SECRET_KEY);
str = StringUtil.md5(stringBuilder.toString());
} else {
StringBuilder stringBuilder2 = new StringBuilder();
stringBuilder2.append(MainActivity.userId);
stringBuilder2.append(j);
stringBuilder2.append(str);
stringBuilder2.append(str2);
stringBuilder2.append(MyConfig.SECRET_KEY);
str = StringUtil.md5(stringBuilder2.toString());
}
if (parseInt != 0) {
requestParams.addHeader(USER.USERID, MainActivity.userId);
}
stringBuilder = new StringBuilder();
stringBuilder.append(j);
stringBuilder.append("");
requestParams.addHeader("timestamp", stringBuilder.toString());
requestParams.addHeader("signature", str);
}
}中的md5核心计算逻辑:
StringBuilder stringBuilder2 = new StringBuilder(); stringBuilder2.append(MainActivity.userId); stringBuilder2.append(j); stringBuilder2.append(str); stringBuilder2.append(str2); stringBuilder2.append(MyConfig.SECRET_KEY); str = StringUtil.md5(stringBuilder2.toString());
对应的Python实现是:
from hashlib import md5
def generateSignature(timestampInt, paramString):
userId = "1134723"
timestamp = "%s" % timestampInt
# localObject = "/Reading.svc/parentChildReadingBookQuery2"
# localObject = paramString
userToken = "40d2267f-359e-4526-951a-66519e5868c3"
# fixedSault = “AyGt7ohMR!xx#N"
secretKey = “AyGt7ohMR!xx#N"
# strToCalc = userId + timestamp + localObject + paramString + fixedSault
# strToCalc = timestamp + localObject + fixedSault
strToCalc = userId + timestamp + paramString + userToken + secretKey
print("strToCalc=%s" % strToCalc)
encodedStr = strToCalc.encode()
# encodedStr = strToCalc.encode("UTF-8")
# print("encodedStr=%s" % encodedStr)
md5Result = md5(encodedStr)
# print("md5Result=%s" % md5Result) # md5Result=<md5 HASH object @ 0x1044f1df0>
# md5Result = md5()
# md5Result.update(strToCalc)
md5Digest = md5Result.digest()
print("md5Digest=%s" % md5Digest) #
print("len(md5Digest)=%s" % len(md5Digest))
md5Hexdigest = md5Result.hexdigest()
print("md5Hexdigest=%s" % md5Hexdigest)
print("len(md5Hexdigest)=%s" % len(md5Hexdigest))
# md5Hexdigest=c687d5dfa015246e6bdc6b3c27c2afea
return md5Hexdigest
# return md5Digest
if __name__ == "__main__":
timestampInt = 1553845899
paramString = "{\"userId\":\"1134723\",\"fieldName\":\"\",\"fieldValue\":\"全部类别\",\"theStageOfTheChild\":\"\",\"parentalEnglishLevel\":\"\",\"supportingResources\":\"有音频\",\"offset\":20,\"limit\":10}"
# paramString = """{"userId":"1134723","fieldName":"","fieldValue":"全部类别","theStageOfTheChild":"","parentalEnglishLevel":"","supportingResources":"有音频","offset":20,"limit":10}"""
generatedSignature = generateSignature(timestampInt, paramString)
# print("timestampInt=%d, paramString=%s-> %s" % (timestampInt, paramString, generatedSignature))运行效果:

要计算的逻辑
userId + timestamp + paramString + userToken + secretKey
对应的字符串
11347231553845899{"userId":"1134723","fieldName":"","fieldValue":"全部类别","theStageOfTheChild":"","parentalEnglishLevel":"","supportingResources":"有音频","offset":20,"limit":10}40d2267f-359e-4526-951a-66519e5868c3AyGt7ohMR!xxx#N的md5值是:
c687d5dfa015246e6bdc6b3c27c2afea
另外去网上验证:
也是对的:

【后记20190410】
后来发现,源码中:

对于GET来说,参数还略有不同,原先的paramString其实就从POST的jValue变成了urlEndpoint了。
所以最后代码改为:
def generateSignature(self, timestampInt, jValueOrUrlEndpoint):
# print("generateSignature: timestampInt=%d, jValueOrUrlEndpoint=%s" % (timestampInt, jValueOrUrlEndpoint))
# userId = "1134723"
userId = gUserId
timestamp = "%s" % timestampInt
# localObject = "/Reading.svc/parentChildReadingBookQuery2"
# localObject = jValueOrUrlEndpoint
# userToken = "40d2267f-359e-4526-951a-66519e5868c3"
userToken = gUserToken
# fixedSault = “AyGt7ohMR!xx#N"
# secretKey = “AyGt7ohMR!xx#N"
secretKey = gSecretKey
# strToCalc = userId + timestamp + localObject + jValueOrUrlEndpoint + fixedSault
# strToCalc = timestamp + localObject + fixedSault
strToCalc = userId + timestamp + jValueOrUrlEndpoint + userToken + secretKey
# print("strToCalc=%s" % strToCalc)
encodedStr = strToCalc.encode()
# encodedStr = strToCalc.encode("UTF-8")
# print("encodedStr=%s" % encodedStr)
md5Result = md5(encodedStr)
# print("md5Result=%s" % md5Result) # md5Result=<md5 HASH object @ 0x1044f1df0>
# md5Result = md5()
# md5Result.update(strToCalc)
# md5Digest = md5Result.digest()
# print("md5Digest=%s" % md5Digest) #
# print("len(md5Digest)=%s" % len(md5Digest))
md5Hexdigest = md5Result.hexdigest()
# print("md5Hexdigest=%s" % md5Hexdigest)
# print("len(md5Hexdigest)=%s" % len(md5Hexdigest))
# md5Hexdigest=c687d5dfa015246e6bdc6b3c27c2afea
# print("md5=%s from %s" % (md5Hexdigest, strToCalc))
return md5Hexdigest
# return md5Digest
def generateCurrentHeaders(self, jValueOrUrlEndpoint):
curHeaders = copy.deepcopy(gHeaders)
curTimestampInt = getCurTimestamp()
curTimestampStr = str(curTimestampInt)
curHeaders["timestamp"] = curTimestampStr
curSignature = self.generateSignature(curTimestampInt, jValueOrUrlEndpoint)
curHeaders["signature"] = curSignature
return curHeaders
urlEndpoint = "/Reading.svc/viewEnglishSeries2/%s/%s" % (gUserId, seriePrimayKey)
fullUrl = "%s%s" % (gServerPort, urlEndpoint)
# /Reading.svc/viewEnglishSeries2/1134723/31
curHeaders = self.generateCurrentHeaders(urlEndpoint)从而也支持GET类的api请求的signature参数的生成。