在互联网发展趋势迅猛的今天,数据安全的重要性日趋凸显。也成为我们必须了解的互联网知识。
在移动互联网浪潮下,用户的资金安全、企业的信息安全都是我们实际开发中必须考虑的内容。
1. 数据安全
1> 概述
- 数据安全:是一种主动的包含措施,数据本身的安全必须基于可靠的加密算法与安全体系,主要是有对称算法与公开密钥密码体系两种(非对称算法),都包含了数据的加密和解密过程
- 对称算法:对称密码算法有时又叫传统密码算法,是指加密密钥可以从解密密钥中推算出来,反过来也成立。
- 非对称算法:对称密码算法有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,反过来也成立。
- 加密算法有很多种,在iOS开发当中,MD5是我们常用的摘要算法。
2> 术语
- 密钥:密钥是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥(也可以根据用途来分为加密密钥和解密密钥)
- 明文:没有进行加密,能够直接代表原文含义的信息
- 密文:经过加密处理处理之后,隐藏原文含义的信息
- 加密:将明文转换成密文的实施过程
- 解密:将密文转换成明文的实施过程
2. MD5加密
1> 哈希算法
- 哈希算法:哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。
- 哈希值是一段数据唯一且极其紧凑的数值表示形式。数据的哈希值可以检验数据的完整性。一般用于快速查找和加密算法。
- 典型的的哈希算法有:MD2、MD4、MD5 和 SHA-1等。
2> MD5
MD5:Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。
3> MD5算法具有以下特点:
- 压缩性:任意长度的数据,算出的MD5值长度都是固定的(16进制,32位)。
- 容易计算:从原数据计算出MD5值很容易。
- 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- 强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
3> 获取字符串MD5值
- 引入<CommonCrypto/CommonCrypto.h>
- 获取字符串的MD5值
1 + (NSMutableString *)stringMD5:(NSString *)str 2 { 3 // 1. 因为MD5是基于C语言的,所以我们要将字符串进行编码 4 const char *data = [str UTF8String]; 5 6 // 2. 使用字符串数组去存取加密后的相关内容(16进制,32位) 7 // CC_MD5_DIGEST_LENGTH表示长度 8 unsigned char result[CC_MD5_DIGEST_LENGTH]; 9 10 // 3. 进行MD5加密 11 // 参数1:要加密的内容 12 // 参数2:要加密的data的一个长度 13 // 参数3:存储MD5加密后的结果数组 14 CC_MD5(data, (CC_LONG)strlen(data), result); 15 16 // 4. 创建可变字符串,保存结果 17 NSMutableString *mutableStr = [NSMutableString string]; 18 19 // 5. 遍历结果数组,然后添加到可变字符串中 20 for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { 21 22 // 16进制需要大家知道格式修饰符(%x表示16进制,02表示当不足两位的时候,前面补0) 23 [mutableStr appendFormat:@"%02x", result[i]]; 24 } 25 26 return mutableStr; 27 }
在这里我将字符串使用MD5加密封装为NSString的类目,下载地址:https://github.com/AlonerOwl/MD5CommonCrypto
4> 获取其他对象MD5值
- 引入<CommonCrypto/CommonCrypto.h>
- 将其它对象转化为NSData对象(可以将对象事先写入文件)
1 // 1. 创建数组 2 NSArray *array = @[@"小胖", @"Rose"]; 3 4 // 2. 找沙盒路径 5 NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 6 7 // 3. 拼接路径 8 NSString *arrayPath = [documentPath stringByAppendingPathComponent:@"array.plist"]; 9 10 // 4. 写入 11 [array writeToFile:arrayPath atomically:YES]; 12 13 // 从沙盒中取出NSData数据 14 NSData *data = [NSData dataWithContentsOfFile:arrayPath];
- 读取NSData对象的MD5值
1 + (NSMutableString *)dataMD5:(NSData *)data 2 { 3 // 1. 创建MD5对象 4 CC_MD5_CTX md5; 5 6 // 2. 初始化MD5对象 7 CC_MD5_Init(&md5); 8 9 // 3. 准备开始数据加密 10 // 参数1:md5对象 11 // 参数2:要加密的data的字节长度 12 // 参数3:要加密内容的长度 13 CC_MD5_Update(&md5, data.bytes, (CC_LONG)data.length); 14 15 // 4. 准备一个字符串数组用来存储结构 16 unsigned char result[CC_MD5_DIGEST_LENGTH]; 17 18 // 结束加密 19 CC_MD5_Final(result, &md5); 20 21 // 5. 创建一个可变字符串存放加密结构 22 NSMutableString *mutableString = [NSMutableString string]; 23 24 // 6. 遍历数组,给可变字符串赋值 25 for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { 26 [mutableString appendFormat:@"%02x", result[i]]; 27 } 28 return mutableString; 29 }
在这里我将字符串使用MD5加密封装为NSString的类目,下载地址:https://github.com/AlonerOwl/MD5CommonCrypto
3. 钥匙串加密
1> 钥匙串概述
钥匙串:(英文:Keychain)是苹果公司Mac OS中的密码管理系统。它在Mac OS 8.6中和iOS7之后被导入,并且包括在了所有后续的各版本中。一个钥匙串可以包含多种类型的数据:密码(包括网站,FTP服务器,SSH帐户,网络共享,无线网络,群组软件,加密磁盘镜像等),私钥,电子证书和加密笔记等。
2> 钥匙串加密
- 苹果 iOS 和 Mac OS X 系统自带了一套敏感信息保存方案:"钥匙串" (Keychain)。
- 保存在钥匙串的内容相当于系统对其做了保护,在设备锁定时进行了加密处理。
- 钥匙串中的条目称为SecItem,但它是存储在CFDictionary中的。SecItemRef类型并不存在。SecItem有五类:通用密码、互联网密码、证书、密钥和身份。在大多数情况下,我们用到的都是通用密码
- 钥匙串的使用和字典非常的相似
- 用原生的 Security.framework 就可以实现钥匙串的访问、读写。但是只能在真机上进行。通常我们使用KeychainItemWrapper来完成钥匙串的加密。
3> 钥匙串的使用
- 拷贝钥匙串类到工程中,钥匙串类下载地址:https://github.com/AlonerOwl/KeychainItemWrapper
- 引入标准头文件
- 生成钥匙串对象
- 存储加密的数据
- 获得钥匙串对象
- 获取加密的数据
代码:
1 // 1.创建钥匙串对象 2 // 参数1:标识,用于识别加密的内容(回传标识符) 3 // 参数2:分组,一般为nil 4 KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"myItemWrapper" accessGroup:nil]; 5 6 // 常用于加密用户名和密码 7 // 系统提供的键值对中的两个键,非系统的键,是无法添加到字典中的 8 id kUserName = (__bridge id) kSecAttrAccount; 9 id kUserPassword = (__bridge id) kSecValueData; 10 11 [keychainItem setObject:@"MBBoy" forKey:kUserName]; 12 [keychainItem setObject:@"123456" forKey:kUserPassword]; 13 14 // 从keychain中获取存储的数据 15 // 用户 16 NSString *userName = [keychainItem objectForKey:kUserName]; 17 // 密码 18 NSString *userPassword = [keychainItem objectForKey:kUserPassword]; 19 20 NSLog(@"%@,%@", userName, userPassword);
4> 注意
- 引入的头文件是MRC模式的,需要进行混编
- 获取对象的时候Identify必须一致
- 非系统key值是无法添加到字典中的
4. 公钥加密
1> 概述
- 公钥加密也叫非对称加密
- 常用算法有RSA、ElGamal、背包算法、Rabin等等,IOS中用的最多的是RSA
- iOS 使用 RSA 加密, 只需要公钥
2> RSA加密的基本原理
RSA使用"秘匙对"对数据进行加密解密.在加密解密数据前,需要先生成公钥(public key)和私钥(private key).
- 公钥(public key): 用于加密数据. 用于公开, 一般存放在数据提供方, 例如iOS客户端.
- 私钥(private key): 用于解密数据. 必须保密, 私钥泄露会造成安全问题.
3> 使用openssl生成密匙对
Shell代码
1 - #!/usr/bin/env bash 2 - echo "Generating RSA key pair ..." 3 - echo "1024 RSA key: private_key.pem" 4 - openssl genrsa -out private_key.pem 1024 5 - 6 - echo "create certification require file: rsaCertReq.csr" 7 - openssl req -new -key private_key.pem -out rsaCertReq.csr 8 - 9 - echo "create certification using x509: rsaCert.crt" 10 - openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt 11 - 12 - echo "create public_key.der For IOS" 13 - openssl x509 -outform der -in rsaCert.crt -out public_key.der 14 - 15 - echo "create private_key.p12 For IOS. Please remember your password. The password will be used in iOS." 16 - openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt 17 - 18 - echo "create rsa_public_key.pem For Java" 19 - openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout 20 - echo "create pkcs8_private_key.pem For Java" 21 - openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt 22 - 23 - echo "finished."
在你所在的路径下会生成一下公钥文件:
在这些文件中最重要的是:pkcs8_private_key.pem和rsa_public_key.pem
4> 代码取得公钥和私钥的字符串
公钥:
私钥:
5> 代码
// 获取公钥的数据 // 公钥:iOS客户端使用,我们拿到公钥后,只需要根据公钥处理数据就可以了 NSString *publicKey = @"-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfQC1ccd5U8Yz8GOh2eQiMRaJX0izpbmrNsJOGzbFhzU3GzFI3RB6VTV4zZG5zUuSBU+JNgVZLjDmAx4f7461Tb91R8ikBNOA18aze7FPKx1i+6q4hj+fK6/hCqivoHwq047XoDbjbZ+yw+zDhz1oezvQogNXyHEpsCf5YSr/nUwIDAQAB-----END PUBLIC KEY-----"; // 私钥:用于解密数据千万不能泄露,否则数据不安全 NSString *privateKey = @"-----BEGIN PRIVATE KEY-----MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJ9ALVxx3lTxjPwY 6HZ5CIxFolfSLOluas2wk4bNsWHNTcbMUjdEHpVNXjNkbnNS5IFT4k2BVkuMOYDHh/vjrVNv3VHyKQE04DXxrN7sU8rHWL7qriGP58rr+EKqK+gfCrTjtegNuNtn7LD7MOHPWh7O9CiA1fIcSmwJ/lhKv+dTAgMBAAECgYBejvrgQ3Sit3LhVeDiR+e9crN8tf2Y9clRLSHMD2LeEcu22SeQTVBjkrb2iRHUREoOSMK9Bqk43qBeSRwDgyMs0g5v WZnIOFshyrotqsuGpQw29Sph4363dq1iFqLFaZx+NVxAv9KOlh05EwRE42snPu8tR7ED1zCxF/byGCDVEQJBAM5mOFh/9ff+CsrdXnubhq5IKqlZ0Jdq05pWhkW8wjwrSvjwKDRry92apYFY4AJD1WbMSv8knN/RhrFFPyfefu8CQQDFhVpplu12Tksp/qrx47b/AgA6NzShfRdjEptNQjc88pkgSwFHeJGly0SCy4V+aBgNtdEsLpe+nrU2kQ91aN3dAkEAw2oSlO/qELcMvs9iYkN4+09GXPq1PHcfCvQ6wpIZkGAo5pJybldVOGXvpwxfYqWYyXBI3VJr2JkHHpWCWxwULwJALTksCzLoKYchhHrvbrSBaH9vx+Rt4299lExlTFoVxZmR36fsog2D417Xz86DQP7aSwFO4/vC+Hzj5ptt26bn7QJARTIH4/FQ7y7pg2tKqvrSMvzwwVtY40yT2DT15jnA5CunVgQJmxScCkcCaW9LXb8tjJjKJ2ync6IA8Su8mTQ8qg==-----END PRIVATE KEY-----"; // 创建字符串 NSString *testStr = @"小胖子"; // 创建存储公钥的字符串 NSString *encPublicKey; // 创建存储私钥的字符串 NSString *encPrivateKey; // 使用RSA进行加密: // 参数1:需要加密的内容 // 参数2:公钥字符串 encPublicKey = [RSA encryptString:testStr publicKey:publicKey]; NSLog(@"%@", encPublicKey); // 使用RSA进行解密: NSString *result = @""; // 经过加密后需要把加密的内容传送给后台,此时后台会给你返回一个字符串,大家如果感兴趣可以试一试 // 参数1:由Java后台提供 // 参数2:私钥 encPrivateKey = [RSA decryptString:result privateKey:privateKey]; NSLog(@"%@",encPrivateKey);
RSA第三方文件下载地址:https://github.com/AlonerOwl/RSAEncrypt
5. KVO
1> 概述
KVO:(Key-Value-Observer)键值观察者,是观察者设计模式的一种具体实现
KVO触发机制:一个对象(观察者),监测另一对象(被观察者)的某属性是否发生变化,若被监测的属性发生的更改,会触发观察者的一个方法(方法名固定,类似代理方法)
2> 使用步骤
- 注册观察者(为被观察者指定观察者以及被观察属性)
1 // KVO 键值观察,是观察者设计模式 2 @interface ViewController () 3 4 // 观察可变数组的改变情况(苹果官方文档不建议对数组进行观察) 5 @property (nonatomic, strong) NSMutableArray *array; 6 7 @end 8 9 - (void)viewDidLoad { 10 [super viewDidLoad]; 11 // Do any additional setup after loading the view, typically from a nib. 12 13 self.array = [NSMutableArray array]; 14 15 // 第一步:注册观察者 16 // 参数1:添加的观察者对象 17 // 参数2:字符串标识的key 18 // 参数3:触发添加观察者对象的时候 19 /* 20 NSKeyValueObservingOptionNew = 0x01 key或value只要有一个更新的时候就会触发 21 NSKeyValueObservingOptionOld = 0x02 22 NSKeyValueObservingOptionInitial = 0x04 23 NSKeyValueObservingOptionPrior = 0x08 24 */ 25 // 参数4:文本内容 一般为nil 26 [self addObserver:self forKeyPath:@"array" options:NSKeyValueObservingOptionNew context:nil]; 27 }
- ?实现回调方法
1 // 第二步:实现回调 2 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context 3 { 4 5 NSLog(@"keyPath = %@", keyPath); 6 NSLog(@"object = %@", object); 7 NSLog(@"change = %@", change); 8 9 // 可以进行刷新UI的操作 10 }
- ?触发回调方法(被观察属性发生更改)
1 // 第二步:实现回调 2 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context 3 { 4 5 NSLog(@"keyPath = %@", keyPath); 6 NSLog(@"object = %@", object); 7 NSLog(@"change = %@", change); 8 9 // 可以进行刷新UI的操作 10 }
- 移除观察者
在不需要观察者的时候需要把它删除,本人就只在视图将要消失时移除
1 // 视图将要消失的时候 2 - (void)viewWillDisappear:(BOOL)animated 3 { 4 // 在不需要观察者的时候需要把它删除 5 [self removeObserver:self forKeyPath:@"array"]; 6 }