服务器验证
通过创建NSURLProtectionSpace,来确认用户访问的是安全的服务器。从一个需要验证的服务器请求资源,过程如下:
当服务器收到请求,响应发出HTTP状态码401,报头:WWW-Authenticate,NSURLConnection接收到后,告知APP调用willSendRequestForAuthenticationChallenge:;验证服务器工作就在这个函数中完成。
实现委托:
通过验证全部或部分属性,保证APP只连接指定服务器。
- - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
1.验证服务器全部属性
创建信任的服务器的protection space:
- NSURLProtectionSpace *defaultSpace = [[NSURLProtectionSpace alloc] initWithHost:@"yourbankingdomain.com"
- port:443
- protocol:NSURLProtectionSpaceHTTPS
- realm:@"mobile"
- authenticationMethod:NSURLAuthenticationMethodDefault];
然后验证challenge.protectionSpace是否是这个信任的服务器的protection space。
2.验证部分指定的服务器属性
获取challenge.protectionSpace的各个属性,如:challenge.protectionSpace.host,challenge.protectionSpace.port,
challenge.protectionSpace.protocol等。依次检查各个属性是否是指定值,即可。
每一个网络连接操作都是NSOperation,在后台线程执行,所以,如果这些过程中需要更新UI,需要特别注意要在主线程中完成。服务器验证很重要,但其本身不足以防范所有攻击。例如,它不能防止网络通信被偷听。
HTTP认证
两种认证方式:标准和加速。
1.标准认证模式--用户名/密码
标准认证模式有:
- HTTP Basic Authentication(基本认证)
- HTTP Digest Authentication(摘要认证)
- NTLM Authentication (NTLM认证)
- // <span style="font-family:Helvetica;">HTTPBasic</span>
- if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic) {
- // 验证
- if (challenge.previousFailureCount == 0) {
- NSURLCredential *creds = [[NSURLCredential alloc] initWithUser:username
- password:password
- persistence:NSURLCredentialPersistenceForSession];
- [challenge.sender useCredential:creds forAuthenticationChallenge:challenge];
- // 之前验证失败
- } else {
- [[challenge sender] cancelAuthenticationChallenge:challenge];
- }
- }
2.客户端证书认证
(这一章就是个坑啊……我这种没恒心的人先跳过好了)
哈希(HASH)和加密(ENCRYPTION)保证数据完整性
APP必须保证数据在传输过程中是安全的、不会被修改,利用哈希和加密算法可以保护数据。
1.哈希
iOS CommonCrypto中包括常用的MD5、SHA-1、SHA-256和一些不常用的哈希算法。以MD5为例:
哈希算法的结果长度一定,且不可逆,无法根据结果推断原文。
- NSString *str = @"test string";//原字符串
- const charchar *ptr = [str UTF8String];
- unsigned char buffer[CC_MD5_DIGEST_LENGTH];
- CC_MD5(ptr, strlen(ptr), buffer);
- NSMutableString *hashString = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];//经过哈希算法后的字符串
- for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
- [hashString appendFormat:@"%02x",buffer[i]];
- }
哈希算法用于检测数据完整性时,将数据及数据经过哈希算法计算出的值一起明文发送给服务器,服务器接收到后对数据进行哈希计算,将得到的值与接收到的哈希值比较,如果不等则认为数据不完整。
哈希算法可以应用于验证登录,用户注册时,将密码应用哈希算法计算出一个杂乱无章的值存储在数据库,每一次登录时,输入密码,然后将密码利用哈希算法计算出哈希值,与存储在数据库中的密码哈希值进行比较,相等,则登录成功。
2.加密
加密算法的结果长度随原文长度变化,可以,可以根据结果推断出原文。
加密发送的数据不是明文的,下图是数据传输过程中加密和解密的步骤示意图。可以看到,
加密过程中,
- 生成iv(初始向量)。
- 利用iv和加密key对数据进行加密。
- 根据HMAC key生成MAC,将加密后的数据、iv及MAC组成request body发送至服务器。
- 根据共享的加密key、iv,解密数据。
- 利用共享的HMAC key和解密后的数据生成服务器端MAC。
- 比较两个MAC值是否相等,如果否,则认为数据不安全。
MAC(Message Authentication Codes)
MAC用于检测数据是否被修改。计算MAC算法类似哈希算法,但是MAC是成对的,更安全。APP会计算一个MAC随请求一起发送,服务器也会计算一个MAC,两个MAC比较,如果不一致,则数据被修改了。iOS和大多数服务器支持HMAC(Hash-Based Message Authentication Code),它的强度取决于密钥和哈希算法强度。
- const charchar *ptr = [@"test string" UTF8String];
- const charchar *keyPtr = [kMACKey UTF8String];
- unsigned char buffer[CC_SHA256_DIGEST_LENGTH];
- // 计算哈希值
- CCHmac(kCCHmacAlgSHA256, // 算法
- keyPtr, kCCKeySizeAES256, // key 和 key的长度
- ptr, strlen( ptr ), // 原字符串 和 原字符串长度
- buffer); // 哈希值,哈希值长度等于所采用的哈希算法摘要长度
- NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
- for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
- [output appendFormat:@"%02x",buffer[i]];
- }
加密与解密算法
已经生成了MAC,就可以对数据进行加密,然后将其发送至服务器。这里主要讨论两种加密方式:高级加密标准(Advanced Encryption Standard,AES)和数据加密标准(Data Encryption Standard,DES)。DES占用资源较大,不适合手机设备,而AES从速度、强度和资源占用方面更适合手机设备。Objective-C中,CommonCryptor库中CCCrypt函数,可以实现加密和解密操作。
以AES为例,加密过程,首先需要生成iv,这里利用SecRandomCopyBytes函数,创建一组随机字节。加密代码如下:
- NSMutableData *iv = [NSMutableData dataWithLength:kCCBlockSizeAES128];
- SecRandomCopyBytes(kSecRandomDefault,kCCBlockSizeAES128,iv.mutableBytes);
- NSString *str = @"test string";
- NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
- NSData *keyData = [kAESEncryptionKey dataUsingEncoding:NSUTF8StringEncoding];
- size_t dataMoved;
- NSMutableData *encryptedData = [NSMutableData dataWithLength:data.length + kCCBlockSizeAES128];
- CCCryptorStatus status = CCCrypt(kCCEncrypt, // 加密
- kCCAlgorithmAES128, // 加密标准
- kCCOptionPKCS7Padding,
- keyData.bytes,
- keyData.length,
- iv.bytes,
- data.bytes,
- data.length,
- encryptedData.mutableBytes,
- encryptedData.length,
- &dataMoved);
- if (status == kCCSuccess) {
- encryptedData.length = dataMoved;
- }
- NSString *encryptedString = [encryptedData base64Encoding];
解密算法与加密类似,唯一区别在于,CCCrypt函数第一个参数指定为解密操作。
- NSMutableData *decryptedData = [NSMutableData dataWithLength:encryptedData.length + kCCBlockSizeAES128];
- CCCryptorStatus result = CCCrypt(kCCDecrypt, // kCCEncrypt or kCCDecrypt
- kCCAlgorithmAES128,
- kCCOptionPKCS7Padding, // Padding option for CBC Mode
- keyData.bytes,
- keyData.length,
- iv.bytes,
- encryptedData.bytes,
- encryptedData.length,
- decryptedData.mutableBytes, // encrypted data out
- decryptedData.length,
- &dataMoved); // total data moved
- if (result == kCCSuccess) {
- decryptedData.length = dataMoved;
- }
- NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
- NSLog(@"%@",decryptedString);
http://blog.csdn.net/pingshw/article/details/17845735