iOS开发,让数据更安全的几个加密方式

任何应用的开发中安全都是重中之重,在信息交互异常活跃的现在,信息加密技术显得尤为重要。在app应用开发中,我们需要对应用中的多项数据进行加密处理,从而来保证应用上线后的安全性,给用户一个安全保障。这篇文章就介绍在iOS开发中最常用的数据加密方式。

文中证书锁定内容部分参考了博客
http://blog.csdn.net/dd864140130/article/details/52625666。

iOS中数据加密的几方式

1、使用数字证书锁定来保证不被中间人拦截,将服务器返回的数据和我的当地证书进行对比,确保是从服务器返回回来的。证书有ca证书,也可以自己给自己签发证书。像12306购票。
2、使用https协议请求网页,post来请求网页数据,保证用户的账号密码不被被人获取到。
3、使用苹果自己的SSKeyChain钥匙串,将用户的账号密码保存在钥匙串中。钥匙串拱了错误处理,如果保存出错,会在判断后打印出出错的信息。
4、最保险的加密算法是非对称加密。非对称加密公钥加密私钥解密。缺点是要耗费时间。

证书锁定

当我们上网浏览网页,从网上获取数据的时候,我们知道,不管是http还是https协议,都是服务端被动,客户端主动。所以,客户端第一次发出请求之后,通常无法确定服务端是不是合法。就很可能就会出现以下情景,正常情况下,我们想要根据文章aid查看某篇文章内容,其流程如下:

但如果遭受黑客攻击,流程就会这样的:

此时恶意服务端完全可以发起双向攻击:对上可以欺骗服务端,对下可以欺骗客户端,更严重的是客户端段和服务端完全感知不到已经被攻击了。这就是中间人攻击。
关于中间人攻击维基百科上有更深入的定义:
中间人攻击(Man-in-the-middle attack,缩写:MITM)是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通讯双方的通话并插入新的内容。在许多情况下这是很简单的(例如,在一个未加密的Wi-Fi无线接入点的接受范围内的中间人攻击者,可以将自己作为一个中间人插入这个网络)。
一个中间人攻击能成功的前提条件是攻击者能将自己伪装成每一个参与会话的终端,并且不被其他终端识破。中间人攻击是一个(缺乏)相互认证的攻击。大多数的加密协议都专门加入了一些特殊的认证方法以阻止中间人攻击。例如,SSL协议可以验证参与通讯的一方或双方使用的证书是否是由权威的受信任的数字证书认证机构颁发,并且能执行双向身份认证。

那么我们来看下证书锁定是怎么样提高安全性,避免中间人攻击的,用一张简单的流程图来说明:

不难看出,通过证书锁定能有有效的避免中间人攻击。

证书锁定的缺点

证书锁定尽管带了较高的安全性,但是这种安全性的提高却牺牲了灵活性。一旦当证书发生变化时,我们的客户端也必须随之升级,除此之外,我们的服务端不得不为了兼容以前的客户端而做出一些妥协或者说直接停用以前的客户端,这对开发者和用户来说并不是那么的友好。
但实际上,极少情况下我们才会变动证书。因此,如果产品安全性要求比较高还是启动证书锁定吧。
在iOS开发中,我们可以自己给自己签发数字证书,就类似于12306购票网站。从而保证了数据的安全性。下面是生成证书的过程。

// 生成1024位私钥
 openssl genrsa -out private_key.pem 1024 // 根据私钥生成CSR文件 openssl req -new -key private_key.pem -out rsaCertReq.csr // 根据私钥和CSR文件生成crt文件 openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt

 // 为IOS端生成公钥der文件 openssl x509 -outform der -in rsaCert.crt -out public_key.der

 // 将私钥导出为这p12文件 openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt

使用post

下面是两段post和get的代码的对比。

//GET请求的URLhttp://localhost/php/login/login.php?username=zhangsan&password=zhang//POST请求的URLhttp://localhost/php/login/login.php
我们可以很清楚地看到使用GET方式发起请求的URL中包含了用户的账号和密码信息。如果使用GET发起请求,如果有人使用Charles拦截我们的请求,就很容易地从我们发起请求的URL中获取到我们的账号信息。所以发起数据请求的时候避免使用GET,而使用POST。

SSKeyChain

SSKeyChain

使用苹果自己的SSKeyChain钥匙串,我们也能保证用户的数据安全,我们将用户的账号信息保存到钥匙串中能保证数据安全的原因是因为只有苹果公司才知道钥匙串保存在内存中的哪个位置。
使用SSKeyChain我们进行下面两步骤操作:
1、 在工程中加入Security.framework框架。
2、 把SSKeychain.h和SSKeychain.m加到项目文件夹。
加入了需要的文件夹后,SSKeyChain的作者samsoffes在实例代码中给出了使用SSKeyChain的方法。

我们通过下面方法来使用SSKeyChain。

//获取所有账号+ (NSArray *)allAccounts;//通过账号名字获取服务名+ (NSArray *)accountsForService:(NSString *)serviceName;//通过服务名和账号获取密码+ (NSString *)passwordForService:(NSString*)serviceNameaccount:(NSString *)account;//通过服务名和账号删除密码+ (BOOL)deletePasswordForService:(NSString*)serviceNameaccount:(NSString *)account;//通过服务名和账号设置密码+ (BOOL)setPassword:(NSString *)passwordforService:(NSString*)serviceName account:(NSString *)account;

下面是具体的使用方法,通过上面几个方法,我们可以很方便地将用户账号保存到钥匙串,或者从钥匙串中取出来。
项目地址https://github.com/samsoffes/sskeychain

#import <SenTestingKit/SenTestingKit.h>#import "SSKeychain.h"//用变量接受服务名,账号和密码static NSString *kSSToolkitTestsServiceName = @"SSToolkitTestService";static NSString *kSSToolkitTestsAccountName = @"SSToolkitTestAccount";static NSString *kSSToolkitTestsPassword = @"SSToolkitTestPassword";@interface SSKeychainTests : SenTestCase//判断钥匙串所有账号中是否包含一个指定的账号- (BOOL)_accounts:(NSArray *)accounts containsAccountWithName:(NSString *)name;@[email protected] SSKeychainTests- (void)testAll {// Getting & Setings Passwords[SSKeychain setPassword:kSSToolkitTestsPassword forService:kSSToolkitTestsServiceName account:kSSToolkitTestsAccountName];NSString *password = [SSKeychain passwordForService:kSSToolkitTestsServiceName account:kSSToolkitTestsAccountName];

STAssertEqualObjects(password, kSSToolkitTestsPassword, @"Password reads and writes");// Getting AccountsNSArray *accounts = [SSKeychain allAccounts];

STAssertTrue([self _accounts:accounts containsAccountWithName:kSSToolkitTestsAccountName], @"All accounts");

accounts = [SSKeychain accountsForService:kSSToolkitTestsServiceName];

STAssertTrue([self _accounts:accounts containsAccountWithName:kSSToolkitTestsAccountName], @"Account for service");// Deleting Passwords[SSKeychain deletePasswordForService:kSSToolkitTestsServiceName account:kSSToolkitTestsAccountName];

password = [SSKeychain passwordForService:kSSToolkitTestsServiceName account:kSSToolkitTestsAccountName];

STAssertNil(password, @"Password deletes");
}

- (BOOL)_accounts:(NSArray *)accounts containsAccountWithName:(NSString *)name {for (NSDictionary *dictionary in accounts) {      if ([[dictionary objectForKey:@"acct"] isEqualToString:name]) {return YES;
}

}return NO;
}

上面的方法是用来保存用户的账号信息,将账号信息保存到钥匙串中,因为钥匙串的不可见性,就已经足够地保证了用户的账号信息安全。如果我们还想让用户的账号信息得到更安全的保证,我们可以先将用户信息进行MD5加密,然后加盐。再将加密后的账号信息保存到钥匙串中。因为MD5编码的不可逆性,就更进一步地保证了用户信息的安全。
关于MD5加密我在我之前写的一篇MD5加密的博客中有详细的说明。

非对称加密

刚才介绍方法是用来保证用户信息的安全。在金融类app中,要保证财务数据的安全,就需要使用到更加安全的非对称加密(维基百科上叫公开密钥加密)。维基百科对于非对称加密的定义是:一种密码学算法类型,在这种密码学方法中,需要一对密钥,一个是私人密钥,另一个则是公开密钥。这两个密钥是数学相关,用某用户密钥加密后所得的信息,只能用该用户的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。称公开的密钥为公钥;不公开的密钥为私钥。

使用公开加密方式中的公钥和私钥可以进行数字签名,原理是这样子的:用私钥加密的信息,可以用公钥对其解密,用于客户验证持有私钥一方发布的数据或文件是完整准确的,接收者由此可知这条信息确实来自于拥有私钥的某人,这被称作数字签名,公钥的形式就是数字证书。例如,从网上下载的安装程序,一般都带有程序制作者的数字签名,可以证明该程序的确是该作者(公司)发布的而不是第三方伪造的且未被篡改过(身份认证/验证)。
常见的公钥加密算法有
RSA、ElGamal、背包算法、Rabin(RSA的特例)、迪菲-赫尔曼密钥交换协议中的公钥加密算法、椭圆曲线加密算法(英语:Elliptic Curve Cryptography, ECC)。使用最广泛的是RSA算法(由发明者Rivest、Shmir和Adleman姓氏首字母缩写而来)是著名的公开秘钥加密算法。在这里我们也是使用RSA来进行公钥加密。
通过下面的代码,我们能够自己生成一个数字证书。

// 生成1024位私钥 openssl genrsa -out private_key.pem 1024 // 根据私钥生成CSR文件 openssl req -new -key private_key.pem -out rsaCertReq.csr // 根据私钥和CSR文件生成crt文件 openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt

 // 为IOS端生成公钥der文件 openssl x509 -outform der -in rsaCert.crt -out public_key.der

 // 将私钥导出为这p12文件 openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt

得到公钥和私钥后就可以将数据进行加密了。我们把一个字符串用RAS算法加密后查看加密后的字符串,再反编码看到解密后的字符串,查看加密和解密的效果。

NSString *encryptString = [self rsaEncryptText:@"123456好哇好哇哈"]; 

NSLog(@"加密:123456好哇好哇哈:%@", encryptString); NSLog(@"解密结果为:%@", [self rsaDecryptWithText:encryptString]);

然后我们自己定义加密和解密的方法,等下我们就要使用加密和解密的方法来进行数据的加密和解密。

插入自定义加密和解密的方法。
#import <Foundation/Foundation.h>@interface HYBRSAEncrypt : NSObject// 加密相关- (void)loadPublicKeyWithPath:(NSString *)derFilePath;
- (void)loadPublicKeyWithData:(NSData *)derData;
- (NSString *)rsaEncryptText:(NSString *)text;
- (NSData *)rsaEncryptData:(NSData *)data;// 解密相关- (void)loadPrivateKeyWithPath:(NSString *)p12FilePath password:(NSString *)p12Password;
- (void)loadPrivateKeyWithData:(NSData *)p12Data password:(NSString *)p12Password;
- (NSString *)rsaDecryptText:(NSString *)text;
- (NSData *)rsaDecryptData:(NSData *)data;@end

加密过程中的思路是:
1、定义一个方法,把证书文件需要加密的数据传入一个方法中,生成一个公钥。

2、创建一个能够将数据进行base64加密的方法。将需要加密的文本通过base加密,加密完成后再调用公钥加密的方法对base加密后的数据进行二次加密。加密时是讲二进制数据分段,切片后进行加密再拼接到二进制数据的变量中。

#import "HYBRSAEncrypt.h"@interface HYBRSAEncrypt () {
  SecKeyRef _publicKey;
  SecKeyRef _privateKey;
}@[email protected] HYBRSAEncrypt- (void)dealloc {  if (nil != _publicKey) {    CFRelease(_publicKey);
  }  if (nil != _privateKey) {    CFRelease(_privateKey);
  }
}#pragma mark - 加密相关//用本地证书加载公钥- (void)loadPublicKeyWithPath:(NSString *)derFilePath {  NSData *derData = [[NSData alloc] initWithContentsOfFile:derFilePath];  if (derData.length > 0) {
    [self loadPublicKeyWithData:derData];
  } else {    NSLog(@"load public key fail with path: %@", derFilePath);
  }
}//加载公钥方法- (void)loadPublicKeyWithData:(NSData *)derData {
  SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)derData);
  SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
  SecTrustRef myTrust;
  OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
  SecTrustResultType trustResult;  if (status == noErr) {
    status = SecTrustEvaluate(myTrust, &trustResult);
  }

  SecKeyRef securityKey = SecTrustCopyPublicKey(myTrust);  CFRelease(myCertificate);  CFRelease(myPolicy);  CFRelease(myTrust);

  _publicKey = securityKey;
}//将文本内容加密- (NSString *)rsaEncryptText:(NSString *)text {  NSData *encryptedData = [self rsaEncryptData:[text hdf_toData]];  NSString *base64EncryptedString = [NSString hdf_base64StringFromData:encryptedData
                                 length:encryptedData.length];  return base64EncryptedString;
}//分段再加密数据- (NSData *)rsaEncryptData:(NSData *)data {
  SecKeyRef key = _publicKey;

  size_t cipherBufferSize = SecKeyGetBlockSize(key);
  uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
  size_t blockSize = cipherBufferSize - 11;
  size_t blockCount = (size_t)ceil([data length] / (double)blockSize);  NSMutableData *encryptedData = [[NSMutableData alloc] init] ;  for (int i = 0; i < blockCount; i++) {
    size_t bufferSize = MIN(blockSize,[data length] - i * blockSize);    NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
    OSStatus status = SecKeyEncrypt(key,
                    kSecPaddingPKCS1,
                    (const uint8_t *)[buffer bytes],
                    [buffer length],
                    cipherBuffer,
                    &cipherBufferSize);    if (status == noErr) {      NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer
                              length:cipherBufferSize];
      [encryptedData appendData:encryptedBytes];
    } else {      if (cipherBuffer) {
        free(cipherBuffer);
      }      return nil;
    }
  }  if (cipherBuffer){
    free(cipherBuffer);
  }  return encryptedData;
}

然后我们可以通过私钥解密。解密思路和加密过程相同。

#pragma mark - 解密相关- (void)loadPrivateKeyWithPath:(NSString *)p12FilePath password:(NSString *)p12Password {  NSData *data = [NSData dataWithContentsOfFile:p12FilePath];  if (data.length > 0) {
    [self loadPrivateKeyWithData:data password:p12Password];
  } else {    NSLog(@"load private key fail with path: %@", p12FilePath);
  }
}//生成私钥- (void)loadPrivateKeyWithData:(NSData *)p12Data password:(NSString *)p12Password {
  SecKeyRef privateKeyRef = NULL;  NSMutableDictionary * options = [[NSMutableDictionary alloc] init];

  [options setObject:p12Password forKey:(__bridge id)kSecImportExportPassphrase];  CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
  OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)p12Data,
                      (__bridge CFDictionaryRef)options,
                      &items);  if (securityError == noErr && CFArrayGetCount(items) > 0) {    CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
    SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                     kSecImportItemIdentity);
    securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);    if (securityError != noErr) {
      privateKeyRef = NULL;
    }
  }

  _privateKey = privateKeyRef;  // CFRelease(items);}//调用下面方法进行解密,最后返回一个字符串- (NSString *)rsaDecryptText:(NSString *)text {  NSData *data = [NSData hdf_base64DataFromString:text];  NSData *decryptData = [self rsaDecryptData:data];  NSString *result = [[NSString alloc] initWithData:decryptData encoding:NSUTF8StringEncoding];  return result;
}//用私钥解密的方法,被上面方法调用- (NSData *)rsaDecryptData:(NSData *)data {
  SecKeyRef key = _privateKey;

  size_t cipherLen = [data length];  void *cipher = malloc(cipherLen);

  [data getBytes:cipher length:cipherLen];
  size_t plainLen = SecKeyGetBlockSize(key) - 12;  void *plain = malloc(plainLen);
  OSStatus status = SecKeyDecrypt(key, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen);  if (status != noErr) {    return nil;
  }  NSData *decryptedData = [[NSData alloc] initWithBytes:(const void *)plain length:plainLen];  return decryptedData;
}@end

上面就是iOS开发中常用的几种数据加密方式。

时间: 2024-10-03 00:34:27

iOS开发,让数据更安全的几个加密方式的相关文章

iOS开发网络数据之AFNetworking使用

iOS开发网络数据之AFNetworking使用 如何选择AFNetworking版本 首先得下载AFNetworking库文件,下载时得首先弄清楚,你将要开发的软件兼容的最低版本是多少.AFNetworking 2.0或者之后的版本需要xcode5.0版本并且只能为IOS6或更高的手机系统上运行,如果开发MAC程序,那么2.0版本只能在MAC OS X 10.8或者更高的版本上运行. AFNetworking 2.0的下载地址https://github.com/AFNetworking/AF

iOS开发系列--数据存取

概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库.例如前面IOS开发系列—Objective-C之Foundation框架的文章中提到归档.plist文件存储,包括偏好设置其本质都是存储为文件,只是说归档或者plist文件存储可以选择保存到沙盒中,而偏好设置系统已经规定只能保存到沙盒的Library/Preferences目录.当然,文件存储并不作为本文的重点内容.本文重点还是说数据库存储,做过数据库开发的朋友应该知道,可以通过SQL直接访问数据库,也可以

IOS开发中数据持久化的几种方法--NSUserDefaults

IOS开发中数据持久化的几种方法--NSUserDefaults IOS 开发中,经常会遇到需要把一些数据保存在本地的情况,那么这个时候我们有以下几种可以选择的方案: 一.使用NSUserDefault是最简单直接的一个办法: 1)保存数据: 1 // 实例化一个NSUserDefaults单例对象 2 NSUserDefaults *user = [NSUserDefaults standardUserDefaults]; 3 // 把一个数组array保存在key为allContact的键值

李洪强iOS开发之数据存储

李洪强iOS开发之数据存储 iOS应用数据存储的常用方式 1.lXML属性列表(plist)归档 2.lPreference(偏好设置) 3.lNSKeyedArchiver归档(NSCoding) 4.lSQLite3 5.lCore Data Documents: 保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录.例如,游戏应用可将游戏存档保存在该目录 tmp: 保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除.应用没有运行时,系统也可能会清除该目录

iOS 开发之 - 关闭键盘 退出键盘 的5种方式

iOS 开发之 - 关闭键盘 退出键盘 的5种方式 1.点击编辑区以外的地方(UIView) 2.点击编辑区域以外的地方(UIControl) 3.使用制作收起键盘的按钮 4.使用判断输入字元 5.关于键盘遮蔽的问题 1,点击编辑区以外的地方(UIView) 这是一种很直觉的方法,当不再需要使用虚拟键盘时,只要点击虚拟键盘和编辑区域外的地方,就可以将键盘收起,下面程式码是在 UIView 中内建的触碰事件方法函式,您可以参考 Touch Panel / 触碰萤幕 / 压力感应器的基本使用方式一文

iOS开发-数据持久化Realm的简单使用

Realm是和SQLite一样用于数据存储,但是它有几个特点比其它的数据库要好用: 1.跨平台 :现在绝大多数的应用开发并不仅仅只在 iOS 平台上进行开发,还要兼顾到 Android 平台的开发.为两个平台设计不同的数据库是愚蠢的,而使用 Realm 数据库, iOS 和 Android 无需考虑内部数据的架构,调用 Realm 提供的 API 就可以完成数据的交换,实现 “ 一个数据库,两个平台无缝衔接 ” . 2.简单易用 : Core Data 和 SQLite 冗余.繁杂的知识和代码足

iOS开发之数据持久化存储

概论 数据持久化存储:所谓持久化存储就是将数据保存到硬盘中,使得应用程序或者机器在重启后可以访问之前保存的数据. 常见方式: plist文件(属性列表) preference(偏好设置) NSKeyedArchiver(归档) SQLite3(数据库) CoreData(苹果基于数据库封装的持久化存储工具,这种方式效率不高,因为会帮我们动态生成很多重复的代码,我只有写XMPP的时候会用一下,因为XMPP里面的存储用的就是CoreData) 沙盒 说到持久化存储就不得不说一下苹果的沙盒机制,苹果的

iOS开发网络数据之AFNetworking使用1

链接地址:http://blog.csdn.net/daiyelang/article/details/38421341 如何选择AFNetworking版本 官网下载2.5版本:http://afnetworking.com/ 此文章基于AFNetworking2.0,如果您使用的是2.5版本的,请看这篇文章:AFNetworking2.5使用 首先得下载AFNetworking库文件,下载时得首先弄清楚,你将要开发的软件兼容的最低版本是多少.AFNetworking 2.0或者之后的版本需要

IOS开发网络数据---- AFNetworking的使用

http网络库是集XML解析,Json解析,网络图片下载,plist解析,数据流请求操作,上传,下载,缓存等网络众多功能于一身的强大的类库.最新版本支持session,xctool单元测试.网络获取数据一直是手机软件的重中之重,如果处理的不好,会造成很差的用户体验.随着ASIHTTPRequest的停止更新,更换网络库是必然的事情,AFNetworking就是很好的替代品.而且都是轻量级,不要担心加入太多库会多软件性能有影响. 1.为什么用第三方网络库?先说如果不用网络库,我曾有一次觉得什么都用