@iblue wrote:
0 前言
逆向的目标之一是评定App安全等级,找到存在的安全隐患,并以有效手段进行反破解。之前实现任意位置打卡的几种方法,都是修改实际的GPS位置信息,并没有从网络、代码层面进行深入的分析。乘着假期,将网络请求的流程梳理出来,看看能否从网络层,获取相关的请求、参数及加密方法。
1 网络流程分析
Charels抓包,网络请求走的都是HTTPS,没有可参考的价值。
回归代码,结合头文件,网络请求用的AFNetworking。既然如此,直接Hook住AFHTTPSessionManager类中最常用的POST请求方法
- POST:parameters:progress:success:failure:
,将url、参数打印出来://Hook的class CHDeclareClass(AFHTTPSessionManager); // - (id)POST:(id)arg1 parameters:(id)arg2 progress:(id)arg3 success:(id)arg4 failure:(id)arg5 CHMethod(5, void, AFHTTPSessionManager, POST, id, arg1, parameters, id, arg2, progress, id, arg3, success, id, arg4, failure, id, arg5) { NSLog(@"%s::%@\n %@", __func__, arg1, arg2); return CHSuper(5, AFHTTPSessionManager, POST, arg1, parameters,arg2, progress, arg3, success, arg4, failure, arg5); } __attribute__((constructor)) static void entry() { CHLoadLateClass(AFHTTPSessionManager); CHClassHook(5, AFHTTPSessionManager, POST, parameters, progress, success, failure); }
1.1 登录请求分析
接下来,打开App进行登录,看看发送了哪些内容。打印信息如下:
https://app.dahuatech.com/GetAndroidDataService.svc/LoginVerify { jsonData = 0e10de0e07c7f127d43b326cb30ceac2566b2dfabd8facbd431e7e31708ffaf8bb310fd0abd77570e10fa3a61c36aac11261b59889c0aa606b64cbde9f43d6814503211a0e0a2a55552646c393e8e9fa72ab0703685cf19308f9a16522d58e405874fa9956a40a99313b01cdb6d3eab702019d3a2eedd819b0a2fab032a8fff52258133eb42b828a80fd4d15df199f4ab83cd4a2df67a5fac0906694ede697abde35c29587de9ca894036f785f0c2d94164a363cb6e92c4a0072d21e1f8b9b0843b1c2af7c90d3c89117318ef8e1c86fa32f99ec9182d57b5191a8e10ad0d80e6ee8de8cb0a3398be971e5e261d77b93858ed103b91af91570a832a95f42ac658737739cd60f46920d21b1154158a0ecae8f8cb7dbcb2ac6c3894e51a290c033715f11bb9e3ce18cb48429f4e5ba69be4e7d17861b355d96ce64d8ac472fafcf04ed891f29a98fa276086fc4d57d10ca; }
得到请求url 【
https://app.dahuatech.com/GetAndroidDataService.svc/LoginVerify
】参数是字典格式
NSDictionary<NSString *, NSString *>
的数据,key值为jsonData
,value值是一串很长的字符串,显然是加密过的。单从value的字符串形式来看,是由十六进制格式的字符组成的。可能是某种加密方式与MD5的结合,但如果进行了MD5,服务端是无法解析数据的;App和服务采用对称加密的方法最常用,AES/DES等,这里有可能先进行了对称加密,再将每个字符进行转换。如果能找到原始的请求参数、加密方法,整个网络请求层应该都可以破解掉了。1.2 请求回溯
要找到原始的参数,就需要找到哪个类调用了POST方法。然后往前一步一步回溯,将每个步骤串起来,就可以倒推网络请求的流程。
借助Hopper,搜索字符串
POST:parameters:progress:success:failure:
,发现ServiceUtil
中的几个方法使用了:queryService: queryListService: updateArrayService: updateService:
登录一般只是检验密码,hook住
queryService
,打印请求参数类型,确实有相应的输出:UserInfoModel
进一步分析,
queryService
并没有被其他类直接调用,而是通过在RegisteredServicesMonitor
函数内,注册DAHUA_MOBILE_ServiceQuery
的通知方法被动调用的。void -[ServiceUtil RegisteredServicesMonitor](void * self, void * _cmd) { ... r0 = [NSNotificationCenter defaultCenter]; r0 = [r0 retain]; stack[2032] = r0; _objc_msgSend(r0, *r0, self, @selector(queryService:), @"DAHUA_MOBILE_ServiceQuery", 0x0); [stack[2032] release]; ... return; }
搜索通知
DAHUA_MOBILE_ServiceQuery
,再根据[LoginViewController login]
伪代码,将整个代码调用顺序串起来,最终发现参数通过DESUtil
类进行加密:2 加密分析
整个请求的流程清晰了,回过头来看UserInfoModel是如何转化为成十六进制字符串的。
queryService
的参数是在ServiceUtil
类的函数dictionaryFormQueryData
被转化了,具体过程分为两步:
- 模型转字典
先用UserInfoModel对象的
convertToUpdateDictionary
方法(其中敏感信息FItemNumber、FPassword作了隐藏处理),将对象转化成字典结构{ FAppVersion = "v4.0.2.Basics (51)"; FItemNumber = 2***9; FMobileType = IOS; FModuleId = 2990; FOSVersion = "10.3.2"; FPassword = "ib***dh"; FVerSion = 51; }
- 字典转查询条件、并加密
ObjectForDesAndReturnData
函数内,将前面步骤的字典转化成加密的字符串,并组成新的字典,作为请求的参数{ jsonData = 0e10de0e07c7f127d43b326cb30ceac25... }
2.1 DES加密
进入最终的加密函数
[DESUtil doCipher:key:context:]
,查看伪代码:void * +[DESUtil doCipher:key:context:](void * self, void * _cmd, void * arg2, void * arg3, unsigned int ret_addr) { r7 = (sp - 0x14) + 0xc; sp = sp - 0x1a4; var_20 = *___stack_chk_guard; objc_storeStrong(r7 - 0x2c, arg2); objc_storeStrong(r7 - 0x30, arg3); var_34 = ret_addr; var_3C = 0x0; if (var_34 == 0x1) { ... } else { r0 = [0x0 dataUsingEncoding:0x4, r0]; r7 = r7; r0 = [r0 retain]; var_C8 = r0; r1 = var_3C; var_3C = [r0 mutableCopy]; [r1 release]; [var_C8 release]; } r0 = [0x0 dataUsingEncoding:0x4, r0]; r7 = r7; r0 = [r0 retain]; var_60 = [r0 mutableCopy]; [r0 release]; [var_60 setLength:0x8, r0]; var_68 = [0x0 length] + 0x8 & 0xfffffff8; var_64 = malloc(var_68); __memset_chk(); var_F0 = [objc_retainAutorelease(var_60) bytes]; var_F8 = [var_60 length]; *(r7 - 0x100) = [objc_retainAutorelease(var_60) bytes]; objc_retainAutorelease(var_3C); *((r7 - 0x100) + 0xfffffffffffffffc) = _objc_msgSend; *((r7 - 0x100) + 0xfffffffffffffff8) = (*((r7 - 0x100) + 0xfffffffffffffffc))(); *((r7 - 0x100) + 0xfffffffffffffff4) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xfffffffffffffff4); *((r7 - 0x100) + 0xfffffffffffffff0) = (r2)(var_3C, @selector(length), r2, var_3C); *(var_34 + 0xffffffffffffffec) = 0x1; r12 = *(var_34 + 0xffffffffffffffec); *(var_34 + 0xffffffffffffffe8) = r7 - 0x6c; *(var_34 + 0xffffffffffffffe4) = var_64; var_70 = 0x0; *((r7 - 0x100) + 0xffffffffffffffe0) = CCCrypt(var_34, 0x1, r12, var_F0, var_F8, *(r7 - 0x100), *((r7 - 0x100) + 0xfffffffffffffff8), *((r7 - 0x100) + 0xfffffffffffffff0), *((r7 - 0x100) + 0xffffffffffffffe4), var_68, *((r7 - 0x100) + 0xffffffffffffffe8)); if (var_34 == 0x1) { ... } else { *((r7 - 0x100) + 0xffffffffffffffc8) = _objc_msgSend; r0 = (*((r7 - 0x100) + 0xffffffffffffffc8))(@class(NSData), @selector(dataWithBytes:length:), var_64, 0x0); r7 = r7; var_74 = [r0 retain]; free(var_64); *((r7 - 0x100) + 0xffffffffffffffc4) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffffc4); (r2)(@class(NSMutableString), @selector(alloc), r2); *((r7 - 0x100) + 0xffffffffffffffc0) = @""; r3 = *((r7 - 0x100) + 0xffffffffffffffc0); *((r7 - 0x100) + 0xffffffffffffffbc) = _objc_msgSend; var_78 = (*((r7 - 0x100) + 0xffffffffffffffbc))(); objc_retainAutorelease(var_74); *((r7 - 0x100) + 0xffffffffffffffb8) = _objc_msgSend; var_7C = (*((r7 - 0x100) + 0xffffffffffffffb8))(); var_80 = 0x0; do { *((r7 - 0x100) + 0xffffffffffffffb4) = _objc_msgSend; r3 = *((r7 - 0x100) + 0xffffffffffffffb4); *((r7 - 0x100) + 0xffffffffffffffb0) = var_80; if (*((r7 - 0x100) + 0xffffffffffffffb0) >= (r3)(var_74, @selector(length), var_80, r3)) { break; } s0 = *@"%x"; *((r7 - 0x100) + 0xffffffffffffffac) = @"%x"; *((r7 - 0x100) + 0xffffffffffffffa8) = _objc_msgSend; r0 = (*((r7 - 0x100) + 0xffffffffffffffa8))(@class(NSString), @selector(stringWithFormat:), *((r7 - 0x100) + 0xffffffffffffffac), s0 & 0xff); r7 = r7; var_84 = [r0 retain]; *((r7 - 0x100) + 0xffffffffffffffa4) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffffa4); if ((r2)(var_84, @selector(length), r2) == 0x1) { r2 = *(("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" | 0x2c0000) + 0x4bd9c); *((r7 - 0x100) + 0xffffffffffffffa0) = @"0%@"; *((r7 - 0x100) + 0xffffffffffffff9c) = _objc_msgSend; r1 = r2; r2 = *((r7 - 0x100) + 0xffffffffffffffa0); r12 = *((r7 - 0x100) + 0xffffffffffffff9c); *((r7 - 0x100) + 0xffffffffffffff98) = var_78; r0 = (r12)(@class(NSString), r1, r2, var_84); r0 = [r0 retain]; r3 = *((r7 - 0x100) + 0xffffffffffffff98); *((r7 - 0x100) + 0xffffffffffffff94) = r0; r0 = r3; *((r7 - 0x100) + 0xffffffffffffff90) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffff94); r3 = *((r7 - 0x100) + 0xffffffffffffff90); r0 = (r3)(r0, @selector(stringByAppendingString:), r2, r3); r7 = r7; r0 = [r0 retain]; *((r7 - 0x100) + 0xffffffffffffff8c) = r0; *((r7 - 0x100) + 0xffffffffffffff88) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffff88); r1 = var_78; var_78 = (r2)(r0, @selector(mutableCopy), r2, r0); [r1 release]; r0 = *((r7 - 0x100) + 0xffffffffffffff8c); [r0 release]; r0 = *((r7 - 0x100) + 0xffffffffffffff94); [r0 release]; } else { *((r7 - 0x100) + 0xffffffffffffff84) = @"%@"; *((r7 - 0x100) + 0xffffffffffffff80) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffff84); r12 = *((r7 - 0x100) + 0xffffffffffffff80); *((r7 - 0x100) + 0xffffffffffffff7c) = var_78; r0 = (r12)(@class(NSString), @selector(stringWithFormat:), r2, var_84); r0 = [r0 retain]; r3 = *((r7 - 0x100) + 0xffffffffffffff7c); *((r7 - 0x100) + 0xffffffffffffff78) = r0; r0 = r3; *((r7 - 0x100) + 0xffffffffffffff74) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffff78); r3 = *((r7 - 0x100) + 0xffffffffffffff74); r0 = (r3)(r0, @selector(stringByAppendingString:), r2, r3); r7 = r7; r0 = [r0 retain]; *((r7 - 0x100) + 0xffffffffffffff70) = r0; *((r7 - 0x100) + 0xffffffffffffff6c) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffff6c); r1 = var_78; var_78 = (r2)(r0, @selector(mutableCopy), r2, r0); [r1 release]; r0 = *((r7 - 0x100) + 0xffffffffffffff70); [r0 release]; r0 = *((r7 - 0x100) + 0xffffffffffffff78); [r0 release]; } objc_storeStrong(r7 - 0x84, 0x0); var_80 = var_80 + 0x1; } while (true); objc_storeStrong(r7 - 0x70, var_78); objc_storeStrong(r7 - 0x78, 0x0); objc_storeStrong(r7 - 0x74, 0x0); } *((r7 - 0x100) + 0xffffffffffffff68) = [var_70 retain]; objc_storeStrong(r7 - 0x70, 0x0); objc_storeStrong(r7 - 0x60, 0x0); objc_storeStrong(r7 - 0x3c, 0x0); objc_storeStrong(r7 - 0x30, 0x0); objc_storeStrong(r7 - 0x2c, 0x0); r0 = *((r7 - 0x100) + 0xffffffffffffff68); r0 = [r0 autorelease]; r1 = *___stack_chk_guard; *((r7 - 0x100) + 0xffffffffffffff64) = r0; if (r1 == var_20) { r0 = *((r7 - 0x100) + 0xffffffffffffff64); } else { r0 = __stack_chk_fail(); } return r0; }
代码很长,看起来很头疼,其实也没必要一行一行去看懂。利用
CCCrypt
的各个参数进行对比分析,大概的加密逻辑也是很容易推测出来的。CCCryptorStatus CCCrypt( CCOperation op, /* kCCEncrypt, etc. */ CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */ CCOptions options, /* kCCOptionPKCS7Padding, etc. */ const void *key, size_t keyLength, const void *iv, /* optional initialization vector */ const void *dataIn, /* optional per op and alg */ size_t dataInLength, void *dataOut, /* data RETURNED here */ size_t dataOutAvailable, size_t *dataOutMoved)
op
,操作类型,由变量var_34控制,即doCipher:key:context
最后一个参数,为0时,进行加密操作
enum { kCCEncrypt = 0, kCCDecrypt, };
alg
,加密算法,伪代码为0x1,即kCCAlgorithmDES
,为DES加密方式enum { kCCAlgorithmAES128 = 0, kCCAlgorithmAES = 0, kCCAlgorithmDES, kCCAlgorithm3DES, kCCAlgorithmCAST, kCCAlgorithmRC4, kCCAlgorithmRC2, kCCAlgorithmBlowfish }; typedef uint32_t CCAlgorithm;
options
,代码对应变量r12 = 0x1,对应枚举为kCCOptionPKCS7Padding
*(var_34 + 0xffffffffffffffec) = 0x1; r12 = *(var_34 + 0xffffffffffffffec)
key
,密钥,对应到doCipher:key:context
的第2个参数,需要转化成char *型,在[DESUtil encode]
中,可以找到相应的密钥02****5a
(8位,属于敏感信息,中间4位隐藏处理),伪代码 对应var_F0
的值:[[DESUtil doCipher:r0 key:@"02****5a" context:stack[2033], stack[2034], stack[2035]] retain] var_F0 = [objc_retainAutorelease(var_60) bytes]
keyLength
,密钥长度,伪代码var_F8 = [var_60 length]
iv
,加密向量,代码对应(r7 - 0x100)
的值,与var_F0
一致*(r7 - 0x100) = [objc_retainAutorelease(var_60) bytes];
dataIn
,需要加密的内容,即doCipher:key:context
的1个参数dataInLength
,加密内容的长度dataOut
,加密后的内容dataOutAvailable
,加密后内容长度var_68 = [0x0 length] + 0x8 & 0xfffffff8;
dataOutMoved
,输出值,不用关心至此,明确了函数使用DES加密方式,并且加密向量与密钥相同,继续住下分析。
2.2 DES结果处理
- 先将加密后的内容转化成NSData值:
r0 = (*((r7 - 0x100) + 0xffffffffffffffc8))(@class(NSData), @selector(dataWithBytes:length:), var_64, 0x0)
- 按字节读取NSData值,并转化成十六进制格式的字符串r2:
r3 = *((r7 - 0x100) + 0xffffffffffffffb4); *((r7 - 0x100) + 0xffffffffffffffb0) = var_80; if (*((r7 - 0x100) + 0xffffffffffffffb0) >= (r3)(var_74, @selector(length), var_80, r3)) { break; } s0 = *@"%x"; *((r7 - 0x100) + 0xffffffffffffffac) = @"%x"; *((r7 - 0x100) + 0xffffffffffffffa8) = _objc_msgSend; r0 = (*((r7 - 0x100) + 0xffffffffffffffa8))(@class(NSString), @selector(stringWithFormat:), *((r7 - 0x100) + 0xffffffffffffffac), s0 & 0xff); r7 = r7; var_84 = [r0 retain]; *((r7 - 0x100) + 0xffffffffffffffa4) = _objc_msgSend; r2 = *((r7 - 0x100) + 0xffffffffffffffa4);
- 判断r2长度,如果长度为1,则在前面补0;这也是为什么在解密代码中,会有高16位、低16位判断的原因
if ((r2)(var_84, @selector(length), r2) == 0x1) { r2 = *(("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" | 0x2c0000) + 0x4bd9c); *((r7 - 0x100) + 0xffffffffffffffa0) = @"0%@"; *((r7 - 0x100) + 0xffffffffffffff9c) = _objc_msgSend; r1 = r2; r2 = *((r7 - 0x100) + 0xffffffffffffffa0); r12 = *((r7 - 0x100) + 0xffffffffffffff9c); ... r0 = *((r7 - 0x100) + 0xffffffffffffff94); [r0 release]; }
- 其实整个循环,就是将NSData的值按字节转化成十六进制格式的字符串:
比如字符串“a”,经过上述DES加密后,得到的NSData值为<ed531e0b 195ec5b7>
,转化成字符串后为ed531e0b195ec5b7
,即为最终加密的结果。而通常AES/DES加密后,会将结果直接转换成Base64。为了测试方便,在NSString增加了一个DES的类别,实现与伪代码相似的功能,具体见附录。
3 ServiceUtil
网络请求部分都在ServiceUtil里面,设置AFNetWorking参数、HTTP请求头等。
3.1 请求头设置
看伪代码,只是简单设置了
Accept
、User-agent
、Accept-Language
、Content-Type
、Content-Length
这几个值,并没有做过多的校验,通过WEB模拟发送POST请求,应该也能通过。void -[ServiceUtil setRequestHead:len:](void * self, void * _cmd, void * arg2, void * arg3) { objc_storeStrong((sp - 0x54) + 0x40, arg2); objc_storeStrong((sp - 0x54) + 0x3c, arg3); [0x0 addValue:@"application/json" forHTTPHeaderField:@"Accept", stack[2027], stack[2028], stack[2029]]; [0x0 addValue:@"Mozilla/5.0" forHTTPHeaderField:@"User-agent", stack[2027], stack[2028], stack[2029]]; [0x0 addValue:@"ZH-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4" forHTTPHeaderField:@"Accept-Language", stack[2027], stack[2028], stack[2029]]; [0x0 addValue:@"application/json" forHTTPHeaderField:@"Content-Type", stack[2027], stack[2028], r2]; [0x0 addValue:0x0 forHTTPHeaderField:@"Content-Length", r1, @"Content-Length"]; objc_storeStrong((sp - 0x54) + 0x3c, 0x0); objc_storeStrong((sp - 0x54) + 0x40, 0x0); return; }
3.2 HTTP端口
代码内部有一个HTTP的端口:
http://app.dahuatech.com:8080
,结合登录url,可以推测,其他的请求都是用特定的功能字符串拼接起来的,用web模拟,发现8080端口也是有效的。
http://app.dahuatech.com:8080/GetAndroidDataService.svc/xxxx
、
https://app.dahuatech.com/GetAndroidDataService.svc/xxxx
void * -[ServiceUtil http](void * self, void * _cmd) { stack[2043] = r4; r7 = (sp - 0x14) + 0xc; *((sp - 0x14) + 0xfffffffffffffffc) = r8; sp = (sp - 0x14) + 0xfffffffffffffffc - 0x40; stack[2042] = self; if (stack[2042]->_http == 0x0) { r0 = (*@"%@%@%@")(@class(NSString), @selector(stringWithFormat:), @"%@%@%@", @"http://", @"app.dahuatech.com:8080", @"/"); r0 = [r0 retain]; stack[2034] = r0; r0 = (*r0)(@class(NSMutableString), @selector(stringWithFormat:), @"%@%@%@", stack[2034], @"GetAndroidDataService.svc", @"/"); ... } r0 = stack[2042]->_http; r0 = loc_239340(r0, *0x31aa04); return r0; }
4 安全检验
登入App后,试了其他几个请求,发现请求参数、请求头并没有登录返回的
FToken
信息,难道登录只是进入App的壳子,后续的请求根本不需要检验?找一个接口一试究竟。查询打卡时间的接口,参数只有工号是可变的:
https://app.dahuatech.com/GetAndroidDataService.svc/GetCheckStatusData { "FOSVersion" : "10.3.2", "FMobileType" : "IOS", "FAppVersion" : "v4.0.2.Basics (51)", "FModuleId" : "3093", "FItemNumber" : "2***9" }
将
FItemNumber
修改为其他的5位数,并对参数加密,得到请求参数(敏感信息隐藏处理):{"jsonData":"0e10de0e07c7f12758af1e31ffdea690c040bb8ecab59985f118ebfbb6d0500cafaa48ff3194a97be6eb6053ec6c6206db03e151be4b528d78db3becbcb1629fc29d0e049cff1a27e5584d684d8b78e7a925a47801cd1511d6a98c7b1c33debbe446eed7fe7674987e52e6b64bd3f73fb9ebfb7a986b5c16537..."}
使用web发送请求,可以得到正常返回:
"FID": "", "IsSuccess": true, "Result": "{\"FStatus\":0,\"FAttendId\":0,\"FCheckInTime\":\"**:**\",\"FCheckOutTime\":\"22:04\"}", "ResultCode": 200 }
再试其他接口,也是可以直接通过的,可见除了登录协议外,其他协议都没有做安全性校验。
5 总结
虽然App是内部用的,但有几点还是可以再提高一下的:
增加请求头、安全检验
增加DES加密的复杂度
关闭8080端口
关键几处函数进行代码混淆,反正不需要AppStore审核附录:加解密代码
#import "NSString+DES.h" #import <CommonCrypto/CommonDigest.h> #import <CommonCrypto/CommonCryptor.h> @implementation NSString (DES) - (NSData *)jm_hexStringConvertToBytesData { //异常字符串 if (self.length % 2 != 0) { return nil; } Byte bytes[1024*3] = {0}; int bytesIndex = 0; for(int i = 0; i < [self length]; i++) { int int_char; /// 两位16进制数转化后的10进制数 unichar hex_charUpper = [self characterAtIndex:i]; ///两位16进制数中的第一位(高位*16) int int_charUpper; if(hex_charUpper >= '0' && hex_charUpper <='9') { int_charUpper = (hex_charUpper - 48 ) * 16; // 0 的Ascll - 48 } else if(hex_charUpper >= 'A' && hex_charUpper <= 'F') { int_charUpper = (hex_charUpper - 55 ) * 16; /// A 的Ascll - 65 } else { int_charUpper = (hex_charUpper - 87 ) * 16; // a 的Ascll - 97 } i++; unichar hex_charLower = [self characterAtIndex:i]; ///两位16进制数中的第二位(低位) int int_charLower; if(hex_charLower >= '0' && hex_charLower <= '9') { int_charLower = (hex_charLower - 48); /// 0 的Ascll - 48 } else if(hex_charUpper >= 'A' && hex_charUpper <='F') { int_charLower = (hex_charLower - 55); /// A 的Ascll - 65 } else { int_charLower = hex_charLower - 87; /// a 的Ascll - 97 } int_char = int_charUpper + int_charLower; bytes[bytesIndex] = int_char; ///将转化后的数放入Byte数组里 bytesIndex++; } NSUInteger dataLength = self.length / 2; NSData *data = [[NSData alloc] initWithBytes:bytes length:dataLength]; return data; } - (NSString *)jm_urlDecode { NSString *decodedString = [self stringByRemovingPercentEncoding]; return decodedString; } - (NSString *)jm_urlEncode { NSString *encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?%#[]"]]; return encodedString; } - (NSString *)jm_encryptUseDESByKey:(NSString *)key iv:(NSString *)iv { NSString *ciphertext; NSString *encode = [self jm_urlEncode]; // NSLog(@"%s encode::%@", __func__, encode); NSData *data = [encode dataUsingEncoding:NSUTF8StringEncoding]; NSUInteger dataLength = data.length; NSUInteger bufferLength = 1024; unsigned char buffer[bufferLength]; memset(buffer, 0, sizeof(char)); size_t numBytesEncrypted = 0; CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionPKCS7Padding, [key UTF8String], kCCKeySizeDES, [iv UTF8String] , //iv向量 [data bytes], dataLength, buffer, bufferLength, &numBytesEncrypted); if (cryptStatus == kCCSuccess) { NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted]; //NSLog(@"%s buffer::%s", __func__, buffer); //NSLog(@"%s data::%@", __func__, data); ciphertext = @""; for (int index = 0; index < data.length; index++) { char byte; [data getBytes:&byte range:NSMakeRange(index, 1)]; NSString *text = [NSString stringWithFormat:@"%x", byte&0xff]; //不足两位,前面补0 if([text length] == 1) { text = [NSString stringWithFormat:@"0%@", text]; } ciphertext = [ciphertext stringByAppendingString:text]; } } NSLog(@"%s encryptText::%@", __func__, ciphertext); return ciphertext; } - (NSString *)jm_decryptUseDesByKey:(NSString *)key iv:(NSString *)iv { NSString *decryptText; NSData *encryptData = [self jm_hexStringConvertToBytesData]; const char *textBytes = [encryptData bytes]; NSUInteger dataLength = encryptData.length; NSUInteger bufferLength = dataLength + 0x8 & 0xfffffff8; unsigned char buffer[bufferLength]; memset(buffer, 0, sizeof(char)); size_t numBytesEncrypted = 0; //将encryptText转化为bytes CCCryptorStatus decryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmDES, kCCOptionPKCS7Padding, [key UTF8String], kCCKeySizeDES, [iv UTF8String] , //iv向量 textBytes, dataLength, buffer, bufferLength, &numBytesEncrypted); if (decryptStatus == kCCSuccess ) { NSLog(@"%s buffer::%s", __func__, buffer); NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted]; NSLog(@"%s data::%@", __func__, data); decryptText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%s decryptText::%@", __func__, decryptText); decryptText = [decryptText jm_urlDecode]; NSLog(@"%s decodeUrl::%@", __func__, decryptText); } return decryptText; } @end
Posts: 2
Participants: 2