iOS开发加解密算法-基础篇(3)<大文件加解密>

一、 需求驱动功能开发,项目的需求是文件先经过服务端加密,客户端下载完成后再进行解密后使用,最终确认的方案是对文件分段加密,加密后再拼接成新的文件,最后在实现的时候遇到过一些坑<内存问题>。该篇只是介绍我们项目中使用的文件加密方案,并不代表移动端开发主流文件加密的方案

</br>

二、实现:对大文件进行分段切割

首先想到的就是OC自带的文件处理类NSFileHandle,不过在使用的时候遇到了一个运存无法消除的问题,虽然可以用NSFilehandlereadDataOfLengthseekToFileOffset的两个方法达到分段读取文件的功能,但是每次调用的时候程序的内存是叠加的,文件多大需要消耗的内存就有多大,当需要的内存大于程序的上限的时候,就会造成程序的闪退。


OC的方法虽然能实现功能,但是性能有不满足要求,因为C语言自带标准I/O库,就试着用C语言写了个文件分段读取的方法,并实现了内存的即时的释放,保证了App的性能需求。调用的方法解释可以参考:C语言文件操作函数|c语言文件读写函数fopen方法参数说明参见百科,具体方法实现如下:

/**
 *  解密视文件,主要思路是先找到待解密的文件,将解密的文件存放与临时文件下,
 *  解密完成将加密的文件删除再将解密完成的文件替换成原文件的名字
 *
 *  @param mediaPath 文件路径
 *  @param index     文件路径下标
 *
 *  @return 是否解密成功
 */
- (BOOL)addDecryptFileWithMediaPath:(NSString *)mediaPath index:(NSInteger)index  header:(NSInteger)header key:(NSInteger)key{
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *subPath = mediaPath;
    // 文件类型
    NSString *pathExtension = [subPath pathExtension];
    // 文件路径的前路径
    NSString *prePath = [subPath stringByDeletingLastPathComponent];
    // 当前文件名 主要采用的是创建一个
    NSString *tempfilePath = [NSString stringWithFormat:@"%@/ios_temp_%ld.%@",prePath,index,pathExtension];
    [manager removeItemAtPath:tempfilePath error:nil];
    [manager createFileAtPath:tempfilePath contents:nil attributes:nil];
    // 创建读写FILE
    FILE *readFile;
    FILE *writeFile;
    //打开发文件
    readFile = fopen([subPath cStringUsingEncoding:NSUTF8StringEncoding], "rb+");
    writeFile = fopen([tempfilePath cStringUsingEncoding:NSUTF8StringEncoding], "a+");
   //获取文件属性信息
    NSDictionary *attributes = [manager attributesOfItemAtPath:subPath error:nil];
    //获取文件字节总长度
    self.fileLength = ((NSNumber *)[attributes objectForKey:@"NSFileSize"]).integerValue;
    int decodeKey = (int)key;
    //根据我们的加密头和加密参数判断是否需要解密
    if (self.fileLength > header) {
        //如果符合解密条件将当前初始解密文件长度设置成头的长度<header是加密的时候添加上的在解密的时候需要
先将header的长度去除再进行解密>
        self.currnentLength = header;
        //调用解密方法
        BOOL isSuc =  decodeFile(readFile, self.fileLength, self.currnentLength, writeFile,decodeKey);
        if (isSuc) {
            if([manager removeItemAtPath:mediaPath error:nil]){
                isSuc =  [manager moveItemAtPath:tempfilePath toPath:mediaPath error:nil];
            }else{
                isSuc = NO;
            }
        }
        return isSuc;
    }else{
        return NO;
    }
}

=============================我是分割线============================

/**
 *  C函数解密文件的方法
 *
 *  @param file         待解密的文件File
 *  @param fileLength   文件长度
 *  @param currentLegth 当前解密的文件长度
 *  @param direcFile    目标存储目录
 */
BOOL decodeFile(FILE *file,long fileLength,long currentLegth,FILE *direcFile,int key){
    BOOL isSucessed;
//分段最大读取长度 1MB
    NSInteger readLength = 1024 * 1024 * 1;
    // 如果文件长度减去当前解密到的文件长度还大于分段最大读取长度则采用递归调用解密
    if (fileLength - currentLegth > readLength) {
        // 指针指向首字节
        //读取文件
        // 成功,返回0,失败返回-1
        int set = fseek(file, currentLegth, SEEK_SET);
        if (set == 0) {
            //设置buffer保存分段读取文件的Data值
            Byte *buffer = malloc(sizeof(Byte) * readLength);
            fread(buffer, readLength, sizeof(Byte), file);
           //读取成功后设置当前解密长度
            currentLegth += readLength;
            //设置读取位置
            // 解密方法  可以在此基础上采用其他的加解密<RSA/AES>
            for (int i = 0 ; i < readLength; i++) {
                *(buffer + i ) -= key;
            }
            // 将解密后的文件流写入解密目标文件
            size_t writedData =  fwrite(buffer, sizeof(Byte), readLength, direcFile);
            if (writedData == readLength) {
                isSucessed = YES;
            }else{
                isSucessed = NO;
            }
          // 释放内存  
            free(buffer);
            if (isSucessed) {
                // 递归调用
                return  decodeFile(file, fileLength, currentLegth,direcFile,key);
            }else{
                return isSucessed;
            }}}else{//如果文件长度减去当前长度小于最小分段读取长度 则跳出递归完成解密
                // 大于0的话继续解密
              if(fileLength - currentLegth  > 0){
            long length = fileLength - currentLegth;
            int set = fseek(file, currentLegth, SEEK_SET);
            if (set == 0) {
                Byte *buffer = malloc(sizeof(Byte) * length);
                fread(buffer, length, sizeof(Byte), file);
                currentLegth += length;
                //设置读取位置
                for (int i = 0 ; i < length; i++) {
                    *(buffer + i ) -= key;
                }
                size_t writedData = fwrite(buffer, sizeof(Byte), length, direcFile);
                printf("最终-fseek--%d",set);
                if (writedData == length) {
                    isSucessed = YES;
                }else{
                    isSucessed = NO;
                }
                // 释放内存
                free(buffer);
                // 关闭文件
                fclose(file);
                fclose(direcFile);
            }
        }
    }
    return isSucessed;
}

Demo地址

如有不当之处欢迎指正。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 项目也快两年了,项目这么长时间下来经历了各种加解密算法,坑也踩过不少.现在把项目中使用过一些常用的加解密算法总结一...
    踏遍青山阅读 2,046评论 1 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,597评论 18 139
  • 首先罗列一些知识点: 1.加密算法通常分为对称性加密算法和非对称性加密算法:对于对称性加密算法,信息接收双方都需事...
    JonesCxy阅读 1,367评论 2 4
  • 嘟哝嘟哝:最近接到一个任务:在客户端动态生成RSA密钥对,然后向服务器发送这个密钥对中的公钥字符串,由服务器进行公...
    TimmyR阅读 8,009评论 19 21
  • 忘掉今天犯的错误,其实是对自己的一种宽恕。是人就会犯错误,有时候就是当时心念一转的瞬间,于是犯下一些自己铭记的错误...
    夏烟阅读 172评论 0 0