iOS网络层数据安全处理之AES基本介绍
相关文献
iOS AES/CBC/PKCS7Padding加密、解密问题
前言(摘录自上面文章)
最近在重构之前写的HTTP代理,这个代理是由代理客户端和代理服务端组成的,二者之前使用SSL保证通信内容不会受到中间人(MITM)攻击。而新的实现打算移除SSL,因为SSL握手的开销过大,尤其是客户端与服务端之间隔了个太平洋,另一方面本月中旬的时候Google安全团队证明了SSLv3已经是不安全的了,需要升级到TLS,但TLS同样有握手的开销。在新的实现中客户端和服务端之间的通信将使用AES加密,每个连接使用独立的随机生成的**和初始化向量。客户端在向服务端发起连接后使用非对称加密算法RSA将**和初始化向量加密后发送给服务端,服务端在收到**和初始化向量后就全部使用AES加密通信,这保证了通信内容不会被窃听(但可能被篡改)。
NSData扩展AES的加解密
.h文件
#import <Foundation/Foundation.h>
@interface NSData (AES)
//加密
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv;
//解密
- (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv;
@end
.m文件
#import "NSData+AES.h"
#import <CommonCrypto/CommonCryptor.h>
@implementation NSData (AES)
//加密
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv
{
return [self AES128operation:kCCEncrypt key:key iv:iv];
}
//解密
- (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv
{
return [self AES128operation:kCCDecrypt key:key iv:iv];
}
- (NSData *)AES128operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv
{
char keyPtr[kCCKeySizeAES128 + 1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
// IV
char ivPtr[kCCBlockSizeAES128 + 1];
bzero(ivPtr, sizeof(ivPtr));
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
size_t bufferSize = [self length] + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptorStatus = CCCrypt(operation, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
ivPtr,
[self bytes], [self length],
buffer, bufferSize,
&numBytesEncrypted);
if(cryptorStatus == kCCSuccess){
NSLog(@"Success");
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}else{
NSLog(@"Error");
}
free(buffer);
return nil;
}
@end
解密例子
要加密的字符:{"body":"aaa","service":"test"} // 最后得到的东西
key:664c4a6b60491e4d2371893479f2758e // md5
[email protected][
data=YYMandP9Mb/U+UZQ5++Trel57rrSJO6a16rNtr3j6S09xFKcw3WrWpZ6lWuigXJ2fHkm05CtZRR3zmU72BUs1w==
timestamp=1519711287
nonce=2x18OyzMUmyarlAj
signature=e93a7eda7c5a5a8b2cb9fdc954894c965f600473
]
解密顺序
1.signature校验
NSString *data = [NSString stringWithFormat:@"%@",response[@"data"]];
NSString *nonce = [NSString stringWithFormat:@"%@",response[@"nonce"]];
NSString *signature = [NSString stringWithFormat:@"%@",response[@"signature"]];
NSString *timestamp = [NSString stringWithFormat:@"%@",response[@"timestamp"]];
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:data];
[arr addObject:nonce];
[arr addObject:timestamp];
[arr addObject:@"mkjtoken"];
[arr sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 compare:obj2];
}];
NSString *shaStr = [arr componentsJoinedByString:@""];
NSString *sha111 = [MKJEncryptAndDecrypt sha1:shaStr];
NSLog(@"~~~~~~~~~~~~%@~~~~~~~~~~~~",sha111);
结果:e93a7eda7c5a5a8b2cb9fdc954894c965f600473
通过data,nonce,timestamp和协定好的字符串例如@"mkjtoken" 组成字符串 sha1和signature比较,校验成功没有被篡改继续下一步骤
2.AES解密
NSString *dataStr = data;
NSData *originData = [self base64Decode:dataStr];
////
NSString *key = [NSString stringWithFormat:@"%@%@",@"mkj",timestamp];
NSString *md5Str = [[MKJEncryptAndDecrypt md5:key] substringToIndex:16];
NSLog(@"md5--->%@",md5Str);
NSData *result = [originData AES128DecryptWithKey:md5Str iv:md5Str];
NSLog(@"解密前数据块%@",result);
结果:<32783138 4f797a4d 556d7961 726c416a 0000001f 7b22626f 6479223a 22616161 222c2273 65727669 6365223a 22746573 74227d6d 6b6a6170 704964> 该数据没有补位符
// 去掉AES之后的补位
+ (NSData *)deCodeData:(NSData *)data{
Byte *bytes = (Byte *)data.bytes;
int pad = (int)bytes[data.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return [data subdataWithRange:NSMakeRange(0, data.length - pad)];
}
NSData *noCoverResult = [self deCodeData:result];
NSLog(@"去掉补位数据块%@",noCoverResult)
// 计算头字节里面的正文字节长度
+ (NSInteger)revertLength:(NSData *)datas{
NSInteger sourceNumber = 0;
Byte *bytes = (Byte *)[datas bytes];
for (NSInteger i = 0; i < 4; i++) {
sourceNumber <<= 8;
sourceNumber |= bytes[i] & 0xff;
}
return sourceNumber;
}
NSInteger lengthOrigin = [self revertLength:[noCoverResult subdataWithRange:NSMakeRange(16, 4)]];
NSLog(@"原始数据长度%ld",lengthOrigin);
结果:0000001f -> 31
// <7b22626f 6479223a 22616161 222c2273 65727669 6365223a 22746573 74227d>
NSData *originData = [result subdataWithRange:NSMakeRange(20, lengthOrigin)];
// {"body":"aaa","service":"test"}
NSString *originStr = [[NSString alloc] initWithData:originData encoding:NSUTF8StringEncoding];
// <6d6b6a61 70704964>
NSData *tailData = [result subdataWithRange:NSMakeRange(20 + lengthOrigin, result.length - 20 - lengthOrigin)];
// mkjappId 这个字段是需要进行比对再操作的,这里就没写那么严谨了,只是一个简单的Demo解密
NSString *tailStr = [[NSString alloc] initWithData:tailData encoding:NSUTF8StringEncoding];
>1 先对后台返回的数据进行base64Decode
>2 AES的key是md5 截取前16位
>3 AES解密出后台包装好的二进制数据
>4 由于AES是会自动填充128位进行加密的,因此加密之后是会有占位符的,具体操作可以看头部的文献,所以解密之后的占位符要先去掉(后台协定前面20字节的最后4个字节代表原始数据的长度,剩下的最后字节就是固定字符串,需要进行校验)
>5 分离前20位的字节,取16-20来区间原始数据
>6 取剩下的尾部appId进行校验 取appid=20+dataLength - 总字符长度,验证appid
>7 解析取data=20-(20+dataLength)
结尾
后台的业务是这样的,我们只是根据这个业务进行解密,记录下基本解密逻辑,除了https之外,还可以在http上面通过RSA+AES的方式进行数据安全传输,也算是对知识的一个回顾,下面还有个处理更简单的版本
推荐阅读