iOS 记录一下AES对称加密遇到的一些坑
以前用过AES256加密,最近项目被恶意注册要用AES加密,后台也没说用什么模式,有点无语,后来我看后台他们在用线上AES调试工具
原来用的是AES 128,EBC模式,没有偏移量的加密和解密,接下来就是使用常用的AES加解密的方法:
我们的加密是放在body里面最初不知道,后台说body里面传过去的是空,才知道他们需要放在body里面传过去
contentype类型也需要设置AF默认是
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
//我们使用下面的方法:
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"text/xml;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
}
NSString *jsonStr = [self convertToJsonData:parameters];
NSString *aesStr = [self AES128_Encrypt:@"AB9CD09788EFGHJK" encryptData:jsonStr];
[mutableRequest setHTTPBody:[aesStr dataUsingEncoding:self.stringEncoding]];
}
//字典转Json字符串
- (NSString *)convertToJsonData:(NSDictionary *)dict
{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString;
if (!jsonData) {
NSLog(@"%@",error);
}else{
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
}
//这里去掉转json中出现的一些转义字符,后台老说他们会接受到一些乱码,需要处理一下
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\\" withString:@""];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\t" withString:@""];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\0" withString:@""];
jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
return jsonString;
}
//iOS AES加密
+ (NSString *)AES128_Encrypt:(NSString *)key encryptData:(NSString *)dataStr{
// json字符串转换为data类型
// const char *cStr = [dataStr cStringUsingEncoding:NSUTF8StringEncoding];
// NSData *data = [NSData dataWithBytes:cStr length:[dataStr length]];
//注意这里一个小坑:这样转成的字符串长度是精确的,参数少的情况下有的用上面转成char在转成data有时候长度不精确影响下面的加密,我用上面的方法解密出来最后一个参数少几个字符所以不建议用上面的方法
NSData* data = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
// 加密方法===
char keyPtr[kCCKeySizeAES128+1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
//底**释的是带自定义向量的设置参数,没有就不用加了
// char ivPtr[kCCKeySizeAES128+1];
// memset(ivPtr, 0, sizeof(ivPtr));
// [gIv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding|kCCOptionECBMode,//这个模式设置一定要和你们公司对应上
keyPtr,
kCCBlockSizeAES128,
NULL,//ivPtr(带自定义向量这里写ivPtr)
[data bytes],
dataLength,
buffer,
bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSData *result = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
if (result && result.length > 0) {
Byte *datas = (Byte*)[result bytes];
NSMutableString *output = [NSMutableString stringWithCapacity:result.length * 2];
for(int i = 0; i < result.length; i++){
[output appendFormat:@"%02x", datas[i]];
}
return output;
}
}
free(buffer);
return nil;
}
// 普通字符串转换为十进
- (NSString *)hexStringFromData:(NSData *)data {
Byte *bytes = (Byte *)[data bytes];
// 下面是Byte 转换为16进制。
NSString *hexStr = @"";
for(int i=0; i<[data length]; i++) {
NSString *newHexStr = [NSString stringWithFormat:@"%02x",bytes[i]]; //我看后台给的接口文档例子是10进制数,所以给他们应该也是10进制数,他们没说,有的公司用的16进制数
if([newHexStr length] == 1) {
newHexStr = [NSString stringWithFormat:@"02%@",newHexStr];
}
hexStr = [hexStr stringByAppendingString:newHexStr];
}
return hexStr;
}
//普通字符串转换为十六进制字符串的。
+ (NSString *)hexStringFromString:(NSString *)string {
NSData *myD = [string dataUsingEncoding:NSUTF8StringEncoding];
Byte *bytes = (Byte *)[myD bytes];
//下面是Byte 转换为16进制。
NSString *[email protected]"";
for(int i=0;i<[myD length];i++){
NSString *newHexStr = [NSString stringWithFormat:@"%x",bytes[i]&0xff];///16进制数
if([newHexStr length]==1){
hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr];
}else{
hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr];
}
}
//AES128解密data(不带自定义向量)
+ (NSString *)AES128_Decrypt:(NSString *)key encryptData:(NSString *)encryptText{
//保证encryptText不能为空,刚开始后台老是返回空崩溃
if ([Function isBlankString:[NSString stringWithFormat:@"%@",encryptText]]) {
return nil;
}
//转换为2进制Data
NSMutableData *data = [NSMutableData dataWithCapacity:encryptText.length / 2];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [encryptText length] / 2; i++) {
byte_chars[0] = [encryptText characterAtIndex:i*2];
byte_chars[1] = [encryptText characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
// 解密方法===
char keyPtr[kCCKeySizeAES256+1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
//带自定义向量ivPtr
// char ivPtr[kCCKeySizeAES128+1];
// memset(ivPtr, 0, sizeof(ivPtr));
// [gIv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding|kCCOptionECBMode,//这个模式设置一定要和你们公司对应上
keyPtr,
kCCBlockSizeAES128,
NULL,//ivPtr(带自定义向量这里写ivPtr)
[data bytes],
dataLength,
buffer,
bufferSize,
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
NSData *jsonData = [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
NSString *jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return jsonStr;
}
free(buffer);
return nil;
}
//还有一个坑是后台有个字典中的value参数是数组转json应为OC数组转json就是一个字符串类型的数组,在aes加密后字典又json一次导致十进制无法json反序列化,后台就有点不淡定了,说安卓的就可以你们就不能,老是这样比较,Java中有数组转json的原生方法,我记得是,很久不搞安卓都忘了,就是数组字符串多了“”这个,这是另一个iOS负责的那个接口是这样的,加密是我做的,说你加密有问题,让我看,说没加密之是好的,他调了一会说你写的加密后台接受到的怎么会多出n这样的字符,哈,其它接口都没事,就这个接口有问题,我也纳闷了,那就我来调吧,看了一下还是转义字符的问题,他说他打印的看的没有,那我就让他调一下一下我的字典转json的方法,去掉一些遇到的转义字符,他用了一下确实没有n了,后台无法json反序列化,那就直接字典接收数组整体json后加密成十进制数后台接受后说可以正常json了,数据也都正常了,但是参数错误,那就是签名的问题了,签名都是一些字典的key和value按规定的顺序拼接的字符串,那肯定和我给他传的字典value的数组有关系了,后台让我打印一下看看,我说不用打印,就是数组的问题,那就把数组value给他转成字符串,就用iOS数组转json的方法来转一下,预料之中可以了