史上最全iOS端数据加密类

作者: laitys | 来源:发表于2017-09-26 16:20 被阅读444次

    前言

    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

    相关文章

      网友评论

        本文标题:史上最全iOS端数据加密类

        本文链接:https://www.haomeiwen.com/subject/mezjextx.html