史上最全iOS端数据加密类

前言

iOS系统由于其封闭性其安全系数要比安卓高不少,但是依然有插件可以捕捉到iOS端敏感数据,特别是以plist文件形式进行存储的数据,Plist 文件主要用于存储用户设置及 App 的配置信息,但 App 可能使用 Plist 文件存储明文的用户名、密码 或其它一些个人敏感信息。而保存在 Plist 文件中的二进制格式文件数据则可以使用 Plist 文件编辑器(如 plutil)进行查看或修改,即使在一个没有越狱的设备上,plist 文件内容也可以通过工具 iExplorer 获取。对 于以编码、未加密或弱加密形式存储的敏感信息就可能会导致敏感信息泄露了。这就需要对一些敏感信息做一些本地加密存储和解密读取,其实,对于用户的一些敏感信息,建议最好使用归档解档的方式做本地数据持久化。

简介

最近项目由一家专业的测试公司做了次渗透测试,测试出的主要是数据安全问题,对于攻击者,从iOS端获取到敏感数据主要有以下三种方式:

  • 恶意程序
    借助iOS系统的安全弱点,攻击者可以设计出一种远程偷取iPhone上文件的恶意程序。
    备份
    当iPhone连接至iTunes后,如果iPhone信任了所连接的电脑后,iTunes将自动对设备上的所有数据进行备份。通过备份,敏感数据也将会保存到电脑上。因此,攻击者如果可以接触到那台电脑,则可以通过备份文件读取到敏感信息。
    物理接触
    用户iPhone的丢失或被盗非常常见。在这两种情形下,攻击者都将可以物理接触到设备,并读取设备上存储的敏感信息。
    所以,对本地持久化的数据加密显得尤为重要,加密方式有很多种:MD5、AES(ECB/CBC)、RSA等,根据不同需求选择加密方法,和后台交互过程的加密可以和后台自行商定。

加密类

  • .h文件
    #import <Foundation/Foundation.h>
    
     ///  加密工具类
     ///  提供RSA & AES加密方法
     @interface CryptorTools : NSObject
    
     #pragma mark - AES 加密/解密
     ///  AES 加密
     ///
     ///  @param data      要加密的二进制数据
     ///  @param keyString 加密密钥
     ///  @param iv        IV向量
     ///
     ///  @return 加密后的二进制数据
      + (NSData *)AESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv;
    
      ///  AES 加密字符串
      //
      ///  @param string    要加密的字符串
      ///  @param keyString 加密密钥
      ///  @param iv        IV向量
      ///
      ///  @return 加密后的 BASE64 编码字符串
       + (NSString *)AESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;
    
      ///  AES 解密
      ///
      ///  @param data      要解密的二进制数据
      ///  @param keyString 解密密钥
      ///  @param iv        IV向量
       ///
       ///  @return 解密后的二进制数据
       + (NSData *)AESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv;
    
       ///  AES 解密
      / //
      ///  @param string    要解密的 BASE64 编码字符串
      / //  @param keyString 解密密钥
      ///  @param iv        IV向量
      ///
      ///  @return 解密后的二进制数据
       + (NSString *)AESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;
    
      #pragma mark - RSA 加密/解密算法
     ///  加载公钥
     ///
     ///  @param filePath DER 公钥文件路径
     - (void)loadPublicKeyWithFilePath:(NSString *)filePath;
    
     ///  加载私钥
     ///
     ///  @param filePath P12 私钥文件路径
      ///  @param password P12 密码
     - (void)loadPrivateKey:(NSString *)filePath password:(NSString *)password;
    
     ///  RSA 加密数据
     ///
     ///  @param data 要加密的数据
     ///
     ///  @return 加密后的二进制数据
    - (NSData *)RSAEncryptData:(NSData *)data;
    
      ///  RSA 加密字符串
        ///
      ///  @param string 要加密的字符串
     ///
      ///  @return 加密后的 BASE64 编码字符串
     - (NSString *)RSAEncryptString:(NSString *)string;
    
       ///  RSA 解密数据
        ///
     ///  @param data 要解密的数据
     ///
    ///  @return 解密后的二进制数据
    - (NSData *)RSADecryptData:(NSData *)data;
    
      ///  RSA 解密字符串
       ///
      ///  @param string 要解密的 BASE64 编码字符串
      ///
      ///  @return 解密后的字符串
       - (NSString *)RSADecryptString:(NSString *)string;
    
     @end
    
    .m文件
         // 填充模式
            #define kTypeOfWrapPadding        kSecPaddingPKCS1
    
            @interface CryptorTools() {
            SecKeyRef _publicKeyRef;                             // 公钥引用
            SecKeyRef _privateKeyRef;                            // 私钥引用
          }
    
           @end
    
         @implementation CryptorTools
    
          #pragma mark - AES 加密/解密
          #pragma mark 加密
          + (NSData *)AESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv {
            return [self AESData:data operation:kCCEncrypt keyString:keyString iv:iv];
         }
          + (NSString *)AESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
            NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
           NSData *result = [self AESEncryptData:data keyString:keyString iv:iv];
    
          // BASE 64 编码
         return [result base64EncodedStringWithOptions:0];
         }
    
           #pragma mark 解密
             + (NSData *)AESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv {
           return [self AESData:data operation:kCCDecrypt keyString:keyString iv:iv];
          }
          + (NSString *)AESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
          // BASE 64 解码
         NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
         NSData *result = [self AESDecryptData:data keyString:keyString iv:iv];
    
        return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
    }
    
     #pragma mark AES 加密&解密
     + (NSData *)AESData:(NSData *)data operation:(CCOperation)operation keyString:(NSString *)keyString iv:(NSData *)iv {
    
        // 设置密钥
       NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
     uint8_t cKey[kCCKeySizeAES128];
        bzero(cKey, sizeof(cKey));
        [keyData getBytes:cKey length:kCCKeySizeAES128];
    
        // 设置 IV 向量
        uint8_t cIv[kCCBlockSizeAES128];
        bzero(cIv, kCCBlockSizeAES128);
        int option = kCCOptionPKCS7Padding | 
        kCCOptionECBMode;
        if (iv) {
      [iv getBytes:cIv length:kCCBlockSizeAES128];
      option = kCCOptionPKCS7Padding;
       }
    
       // 设置输出缓冲区
        size_t bufferSize = [data length] + kCCBlockSizeAES128;
        void *buffer = malloc(bufferSize);
    
       // 加密或解密
       size_t cryptorSize = 0;
       CCCryptorStatus cryptStatus = CCCrypt(operation,
                                        kCCAlgorithmAES,
                                        option,
                                        cKey,
                                        kCCKeySizeAES128,
                                        cIv,
                                        [data bytes],
                                        [data length],
                                        buffer,
                                        bufferSize,
                                        &cryptorSize);
    
        NSData *result = nil;
       if (cryptStatus == kCCSuccess) {
          result = [NSData dataWithBytesNoCopy:buffer 
         length:cryptorSize];
          } else {
      free(buffer);
      NSLog(@"[错误] 加密或解密失败 | 状态编码: %d", cryptStatus);
       }
    
       return result;
    }
    
     #pragma mark - RSA 加密/解密算法
     - (void)loadPublicKeyWithFilePath:(NSString *)filePath; {
    
          NSAssert(filePath.length != 0, @"公钥路径为空");
    
          // 删除当前公钥
         if (_publicKeyRef) CFRelease(_publicKeyRef);
    
           // 从一个 DER 表示的证书创建一个证书对象
          NSData *certificateData = [NSData 
          dataWithContentsOfFile:filePath];
          SecCertificateRef certificateRef = 
          SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge 
          CFDataRef)certificateData);
          NSAssert(certificateRef != NULL, @"公钥文件错误");
    
          // 返回一个默认 X509 策略的公钥对象,使用之后需要调用 
           CFRelease 释放
           SecPolicyRef policyRef = SecPolicyCreateBasicX509();
        // 包含信任管理信息的结构体
           SecTrustRef trustRef;
    
         // 基于证书和策略创建一个信任管理对象
         OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
         NSAssert(status == errSecSuccess, @"创建信任管理对象失败");
    
           // 信任结果
         SecTrustResultType trustResult;
         // 评估指定证书和策略的信任管理是否有效
         status = SecTrustEvaluate(trustRef, &trustResult);
          NSAssert(status == errSecSuccess, @"信任评估失败");
    
          // 评估之后返回公钥子证书
          _publicKeyRef = SecTrustCopyPublicKey(trustRef);
          NSAssert(_publicKeyRef != NULL, @"公钥创建失败");
    
          if (certificateRef) CFRelease(certificateRef);
          if (policyRef) CFRelease(policyRef);
          if (trustRef) CFRelease(trustRef);
    }
    
        - (void)loadPrivateKey:(NSString *)filePath password:
         (NSString *)password {
    
           NSAssert(filePath.length != 0, @"私钥路径为空");
    
         // 删除当前私钥
          if (_privateKeyRef) CFRelease(_privateKeyRef);
    
              NSData *PKCS12Data = [NSData 
             dataWithContentsOfFile:filePath];
            CFDataRef inPKCS12Data = (__bridge 
            CFDataRef)PKCS12Data;
            CFStringRef passwordRef = (__bridge CFStringRef)password;
    
        // 从 PKCS #12 证书中提取标示和证书
           SecIdentityRef myIdentity;
           SecTrustRef myTrust;
           const void *keys[] = {kSecImportExportPassphrase};
           const void *values[] = {passwordRef};
           CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
           CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    
         // 返回 PKCS #12 格式数据中的标示和证书
         OSStatus status = SecPKCS12Import(inPKCS12Data, 
         optionsDictionary, &items);
    
            if (status == noErr) {
      CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
      myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
      myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
           }
    
        if (optionsDictionary) CFRelease(optionsDictionary);
    
            NSAssert(status == noErr, @"提取身份和信任失败");
    
               SecTrustResultType trustResult;
              // 评估指定证书和策略的信任管理是否有效
               status = SecTrustEvaluate(myTrust, &trustResult);
              NSAssert(status == errSecSuccess, @"信任评估失败");
    
             // 提取私钥
             status = SecIdentityCopyPrivateKey(myIdentity, 
         &_privateKeyRef);
           NSAssert(status == errSecSuccess, @"私钥创建失败");
           CFRelease(items);
        }
    
       - (NSString *)RSAEncryptString:(NSString *)string {
            NSData *cipher = [self RSAEncryptData:[string 
           dataUsingEncoding:NSUTF8StringEncoding]];
    
             return [cipher base64EncodedStringWithOptions:0];
        }
    
     - (NSData *)RSAEncryptData:(NSData *)data {
               OSStatus sanityCheck = noErr;
               size_t cipherBufferSize = 0;
               size_t keyBufferSize = 0;
    
               NSAssert(data, @"明文数据为空");
               NSAssert(_publicKeyRef, @"公钥为空");
    
               NSData *cipher = nil;
              uint8_t *cipherBuffer = NULL;
    
              // 计算缓冲区大小
              cipherBufferSize = SecKeyGetBlockSize(_publicKeyRef);
               keyBufferSize = data.length;
    
              if (kTypeOfWrapPadding == kSecPaddingNone) {
              NSAssert(keyBufferSize <= cipherBufferSize, @"加密内容
    太大");
           } else {
      NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密内容太大");
        }
    
               // 分配缓冲区
             cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
              memset((void *)cipherBuffer, 0x0, cipherBufferSize);
    
             // 使用公钥加密
           sanityCheck = SecKeyEncrypt(_publicKeyRef,
                              kTypeOfWrapPadding,
                              (const uint8_t *)data.bytes,
                              keyBufferSize,
                              cipherBuffer,
                              &cipherBufferSize
                              );
    
           NSAssert(sanityCheck == noErr, @"加密错误,OSStatus == %d", sanityCheck);
    
                  // 生成密文数据
             cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
    
           if (cipherBuffer) free(cipherBuffer);
    
            return cipher;
      }
    
            - (NSString *)RSADecryptString:(NSString *)string {
              NSData *keyData = [self RSADecryptData:[[NSData alloc] 
             initWithBase64EncodedString:string options:0]];
    
         return [[NSString alloc] initWithData:keyData 
         encoding:NSUTF8StringEncoding];
     }
    
    - (NSData *)RSADecryptData:(NSData *)data {
         OSStatus sanityCheck = noErr;
         size_t cipherBufferSize = 0;
         size_t keyBufferSize = 0;
    
         NSData *key = nil;
         uint8_t *keyBuffer = NULL;
    
         SecKeyRef privateKey = _privateKeyRef;
         NSAssert(privateKey != NULL, @"私钥不存在");
    
          // 计算缓冲区大小
        cipherBufferSize = SecKeyGetBlockSize(privateKey);
        keyBufferSize = data.length;
    
        NSAssert(keyBufferSize <= cipherBufferSize, @"解密内容太大");
    
        // 分配缓冲区
       keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
       memset((void *)keyBuffer, 0x0, keyBufferSize);
    
        // 使用私钥解密
           sanityCheck = SecKeyDecrypt(privateKey,
                              kTypeOfWrapPadding,
                              (const uint8_t *)data.bytes,
                              cipherBufferSize,
                              keyBuffer,
                              &keyBufferSize
                              );
    
           NSAssert1(sanityCheck == noErr, @"解密错误,OSStatus == %d", sanityCheck);
    
           // 生成明文数据
             key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
    
            if (keyBuffer) free(keyBuffer);
    
             return key;
         }
    
     @end
    

调用示例

  //
  //  WKCryptorToolDemoVC.m
  //  -对称加密加密演练-
  //
  //  Created by egs on 2017/9/26.
  //  Copyright © 2017年 恋guang年. All rights reserved.
  //

  #import "WKCryptorToolDemoVC.h"

  #import "CryptorTools.h"
 @interface UIViewController ()

 @end

 @implementation WKCryptorToolDemoVC 

 - (void)viewDidLoad {
     [super viewDidLoad];
  }

 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:
(UIEvent *)event
   {

      ////  非对称加密
     //创建加密工具对象
      CryptorTools *tool = [[CryptorTools alloc]init];
     //加密内容
      NSString *content = @"i love you";
      //加载公钥
     [tool loadPublicKeyWithFilePath:[[NSBundle 
     mainBundle]pathForResource:@"rsacert.der" ofType:nil]];
     //加密
     NSString *encodeStr = [tool RSAEncryptString:content];

     //加载私钥 password是导出P12文件的密码
[tool loadPrivateKey:[[NSBundle mainBundle]pathForResource:@"p.p12" ofType:nil] password:@"815476562"];
       //解密
     NSString *decondeStr = [tool RSADecryptString:encodeStr];
     NSLog(@"RSA 加密结果 = %@ , 解密结果 =%@",encodeStr,decondeStr);
  }



 /**
  *  对称加密 加密和解密用的同一把密钥
 */


  - (void)test
  {
      //声明一个密钥
       NSString *key = @"itcast";
      //要加密的内容
      NSString *content = @"i love you";

//ECB 加密和界面 电子密码本,就是每个块都是独立加密的 AES高级加密标准
NSString *encodeStr = [CryptorTools AESEncryptString:content keyString:key iv:nil];
NSString *decondeStr = [CryptorTools AESDecryptString:encodeStr keyString:key iv:nil];
NSLog(@" ----ECB加密结果 = %@ , 解密结果 = %@-----",encodeStr,decondeStr);

//  CBC:密码块链,每个明文块的加密结果都会参与下一个块的加密,使用一个密钥和一个初始化向量对数据进行加密转换.开发中推荐使用CBC,ECB少用.
//声明iv
uint8_t iv[9] = {1,2,3,4,5,6,7,8,11};
//将iv转换成NSData
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
NSString *encryStr = [CryptorTools AESEncryptString:content keyString:key iv:ivData];
NSString *decryStr = [CryptorTools AESDecryptString:encryStr keyString:key iv:ivData];
NSLog(@" ----CBC加密结果 = %@ , 解密结果 = %@-----",encryStr,decryStr);
 }
  @end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容