折腾:
【已解决】小花生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参数的生成。