欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS网络层数据安全处理之AES基本介绍

程序员文章站 2024-02-12 17:23:40
...

相关文献

高级加密模式之AES工作原理

iOS AES/CBC/PKCS7Padding加密、解密问题

加密解密工具

iOS Int类型转换成NSData

AES补位填充模式

数据加解之AES篇


前言(摘录自上面文章)

        最近在重构之前写的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的方式进行数据安全传输,也算是对知识的一个回顾,下面还有个处理更简单的版本