iOS学习——数据加密
在加密使用中,一种是散列函数(HASH),它最著名的特点就是不可逆性,我们无法通过加密出来的结果反向解密出内容,其最突出的代表就是MD5加密。MD5加密会无视内容大小,加密成一串32位字符串。面对其不可逆和无视内容大小特性,我们可以用它来做很多事情。
1.使用MD5做传统的登陆密码加密,服务器保留的并不是用户的密码明文,而是一串MD5加密过后的数据,仅仅用来做登陆验证。当然,由于MD5加密后类似指纹的特性,即不同的数据加密结果不一样,但是相同数据加密结果一直是相同的。一些网站可以通过保存加密后的数据与对应明文,来对照获取到与加密数据相对应的明文输入。面对这种情况,加密需要做一些改变,首先一种是,每个用户注册的时候为其生成相对应的盐,这样采用MD5+盐的方式加密或者其它方式MD5(MD5)+盐等其它方式,其内容就极其难以破解,并且也不会因为使用统一加盐的方式导致人为泄漏。另外,针对抓包工具的横行,防止被抓包后,黑客采用直接使用加密后的数据登陆,MD5加密后的数据可以加上时间再次加密上传,一般可以采用分钟作为单位,这样服务器采用相同的加时间,当前时间和前后一分钟进行验证,这样让登陆账号密文时效性足以验证登陆而抓包者又难以使用。ps:再多的加密仍旧难以100%阻止登陆信息泄漏,因此在登陆就诞生了qq登陆这种验证方式。当用户设定好一台手机作为本机,服务器会记录对应的id,当在别处登陆的时候,首先会上传账号名,服务器根据账号找到对应本机,并发出询问信息,是否允许登陆,如果拒绝,登陆将无法执行,同意后,服务器才向登陆者发送允许登陆,然后再执行登陆操作。
2.使用MD5可以进行搜索功能,例如百度搜索引擎等,上面的数据无以计数,如何准确搜索到想要的数据,就可以将数据加密,利用这种类似指纹的特性,快速找到用户想要的内容并展示。
3.MD5可以做的另外一个特性就是版权。譬如视频,当作者发布在网上后,别人下载了,然后改掉信息重新发布,我们如何识别到谁是真正的发布者,就可以通过MD5来识别。作者上传到网上后,原版并不会被发不出来,所有放出来给用户下载观看的都是网站处理过后的视频,MD5的特性保证,任何一点不同都会加密出不同的MD5字符串,因此,别人下载再次上传,对比下加密过后的MD5可以很明显识别版权所有。
有不可逆加密,肯定也是有可逆加密的使用。对称加密——传统加密方式,都是采用明文——密钥——密文,密文——密钥——明文方式进行数据加解密。这种加密方式对密钥的要求很高,而且要保证不会泄漏,一旦泄漏就会被破解加密,而且需要定期更换,密钥使用得越旧越不安全。其最常用的加密算法是:DES、3DES、AES等,当然还有其它的方式,但就不一一赘述了。DES和3DES算法都是类似的,只不过DES加密强度太低,所以出现了使用三个密钥进行三次DES算法这种方式,泄漏一个两个密钥毫无影响。而AES则是苹果这边最常见的加密方式,例如钥匙串就是使用的AES进行加密。对称加密算法有两种加密方式:ECB和CBC,一个视频数据可能非常大,一次加密不太现实,因此会将数据拆分开来,一块一块的单独加密,这就是ECB的加密形式。而CBC,则是在ECB的基础上加上一个向量,进行链式加密,只有加密完了第一个数据才加密第二个数据,并且,第一个数据的加密结果会成为第二个数据的加密密钥,及时部分数据泄漏或被破解,仍然破解所有数据,因为不知道其处于链条的哪一部分。
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv { // 设置秘钥 NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding]; uint8_t cKey[self.keySize]; bzero(cKey, sizeof(cKey)); [keyData getBytes:cKey length:self.keySize]; // 设置iv uint8_t cIv[self.blockSize]; bzero(cIv, self.blockSize); int option = 0; if (iv) { [iv getBytes:cIv length:self.blockSize]; option = kCCOptionPKCS7Padding; } else { option = kCCOptionPKCS7Padding | kCCOptionECBMode; } // 设置输出缓冲区 NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; size_t bufferSize = [data length] + self.blockSize; void *buffer = malloc(bufferSize); // 开始加密 size_t encryptedSize = 0; //CCCrypt 苹果对称加密核心算法 /**参数说明 1.加密或者解密 kCCEncrypt kCCDecrypt 2.加密方式:AES,DES,blowfish等等所有的对称加密方式 3.ECB 或者CBC ECB:kCCOptionPKCS7Padding | kCCOptionECBMode CBC:kCCOptionPKCS7Padding 4.加密的密钥 5.密钥长度 6.向量 7.加密的数据 8.数据大小 9.密文内存地址 10.密文内存缓冲区大小 11.加密结果大小 */ CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, self.algorithm, option, cKey, self.keySize, cIv, [data bytes], [data length], buffer, bufferSize, &encryptedSize); NSData *result = nil; if (cryptStatus == kCCSuccess) { result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize]; } else { free(buffer); NSLog(@"[错误] 加密失败|状态编码: %d", cryptStatus); } return [result base64EncodedStringWithOptions:0]; }
ECB加密终端测试命令,加密:$ openssl enc -des-ecb -K 616263 -nosalt -in msg1.txt -out msg1.bin;解密:$ openssl enc -des-ecb -K 616263 -nosalt -in msg1.bin -out msg1.txt -d。CBC终端测试命令,加密:$ openssl enc -des-cbc -K 616263 -iv 0000000000000000 -nosalt -in a.txt -out msg1.bin 解密:$ openssl enc -des-cbc -K 616263 -iv 0000000000000000 -nosalt -in msg1.bin -out msg4.txt -d,文件查看命令$ xxd msg1.bin
对称加密的对应面肯定就是非对称加密(RSA)。这种加密方式采用了两种密钥,公钥/私钥。用公钥加密,私钥解密或者是私钥加密,公钥解密。其优点是非常之安全,就是采用大量的乘法运算得到一个加密结果,没有密钥用现在的手段基本无法解密。然后同样因为其安全的加密方式带来的后果是,加密非常缓慢,基本大数据不用想使用RSA加密。其加密原理就是:1.找到两个“很大”的质数,P&Q,然后N = P*Q,M=(P-1)*(N-1),公钥就是找到一个整数E与M互质,即除了1以外,没有其他公约数,这个数可以很简单,因为一般是对外公开使用。私钥:找到一个整数D,使得E*D除以M余1;然后就是进行加密:(明文 ^E)%N等到密文,解密:(密文^D)%N 得到明文。一般数字签名就可以用RSA,例如支付就需要使用RSA,其原理就是,将“支付金额”使用HASH得到“hash密文”,然后将“hash密文”使用RSA公钥加密得到“数字签名”,最后将数字签名和“支付金额”发送给服务器。服务器将“支付金额”加密得到“hash密文”,并同时使用私钥解密“数字签名”得到“hash密文”,将两个“hash密文”进行对比,正确就确认支付,这样可以防止金额被篡改。
#pragma mark - 加密 & 解密数据 - (NSData *)encryptData:(NSData *)plainData { OSStatus sanityCheck = noErr; size_t cipherBufferSize = 0; size_t keyBufferSize = 0; NSAssert(plainData != nil, @"明文数据为空"); NSAssert(publicKeyRef != nil, @"公钥为空"); NSData *cipher = nil; uint8_t *cipherBuffer = NULL; // 计算缓冲区大小 cipherBufferSize = SecKeyGetBlockSize(publicKeyRef); keyBufferSize = [plainData 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 *)[plainData 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; }