保护网络传输的实现

一、验证应用与正确的服务器通信 (验证 Protection Space)

  在 willSendRequestForAuthenticationChallenge 回调方法中,检查 challenge,确定是否想要响应服务器的认证 challenge,同时发出适当的 challenge 响应。

  防止:如果发现处于恶意网络之上,传输被重新路由到第三方的服务器上,那么保护空间验证就会因不匹配主机而失败,后续的通信也会终止。

  创建保护空间,讲奇遇 challenge 中包含的信息做对比。

NSURLProtectionSpace *defaultSpace = [[NSURLProtectionSpace alloc] initWithHost:@"xxx.com" port:443 protocol:NSURLProtectionSpaceHTTPS realm:@"mobile" authenticationMethod:NSURLAuthenticationMethodDefault];

  支持的 NSURLProtectionSpace 协议:

  •   NSURLProtectionHTTP: 80/8080
  •   NSURLProtectionHTTPS: 443
  •   NSURLProtectionFTP: 21/22

  basic challenge 响应的过程:

  1.1 方式一:此方式的 Protection Space 验证,需要包含备份认证服务器或其它备选服务器,包含多个 Pretection Space 可以实现一定程度的灵活。

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

    NSURLProtectionSpace *defaultSpace = [[NSURLProtectionSpace alloc] initWithHost:@"xxx.com" port:443 protocol:NSURLProtectionSpaceHTTPS realm:@"mobile" authenticationMethod:NSURLAuthenticationMethodDefault];

    NSURLProtectionSpace *trustSpace = [[NSURLProtectionSpace alloc] initWithHost:@"xxx.com" port:443 protocol:NSURLProtectionSpaceHTTPS realm:@"mobile" authenticationMethod:NSURLAuthenticationMethodClientCertificate];

    NSArray *validSpaces = [NSArray arrayWithObjects:defaultSpace, trustSpace, nil];

    if (![validSpaces containsObject:challenge.protectionSpace]) {
        NSString *errorMessage = @"We‘re unable to establish a secure connection. Please check your network connection and try again.";

        dispatch_async(dispatch_get_main_queue(), ^{
           // show alertView
        });
        [challenge.sender cancelAuthenticationChallenge:challenge];
    }

}

    

  1.2 方式二:实现后端灵活性,只验证 challenge 的某些属性,比如主机、端口、协议是否与遇险定义号的匹配。

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

    if (![challenge.protectionSpace.host isEqualToString:@"xxx.com"] ||
        (challenge.protectionSpace.port != 443) ||
        ![challenge.protectionSpace.protocol isEqualToString:NSURLProtectionSpaceHTTPS]) {
        NSString *errorMessage = @"We‘re unable to establish a secure connection. Please check your network connection and try again.";

        dispatch_async(dispatch_get_main_queue(), ^{
            // show alertView
        });

        [challenge.sender cancelAuthenticationChallenge:challenge];
    }
}

    

二、使用 HTTP 认证

  2.1 标准认证

    HTTP Basic、HTTP Digest、NTLM 使用用户名与密码认证 challenge,三者认证响应的逻辑类似,以下以 HTTP Basic 为例子。

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic) {
        if (challenge.previousFailureCount == 0) {
            NSURLCredential *cred = [[NSURLCredential alloc] initWithUser:userName password:password persistence:NSURLCredentialPersistenceForSession];

            [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
        }
        else { // 之前有发生过认证失败,需要根据具体情况处理,一般是封锁住用户
            [challenge.sender cancelAuthenticationChallenge:challenge];

            // 警告用户认证失败
            NSString *message = @"Invalid username / passeord.";
            dispatch_async(dispatch_get_main_queue(), ^{
               // 显示视图提示
            });
        }
    }
}

    如果 challenge 认证失败,需要提示用户,并取消 challenge 认证,因为 willSendRequestForAuthenticationChallenge 可能会被多次调用。根据配置,如果用户的认证信息不合法而又没有恰当地进行检查,那就有可能在用户提交过一次不合法的认证信息后,账户被锁定。如果进来的 challenge 认证方法并不是应用能处理的类型,则不发出响应。

  

  

  2.2 快速认证

    让用户注册设备,然后使用 PIN 进行验证,没次认证的时候无需使用用户名和密码。为了确保快速认证的安全性,在设备注册成功后,服务器响应就需要包含一个用户证书(使用 Base64 编码成 PKCS P12),客户端应用保存下这个证书,并在随后启动时检查。

    2.2.1: 注册设备完成保存服务器返回来的客户端证书

    服务器注册成功返回的证书数据

{
  "result": "SUCCESS",
  "message": "Authentication Successful",
  "certificate": "<BASE64 Encoded Certificate>"
}

    注册成功后,解码 Base64 的 .p12 这书,并存储到 keychain 中。

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSError *error = nil;
    NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];

    // ... 设备注册完成操作

    // 保存服务器返回来的证书
    if (isDeviceRegisted) {
        NSString *certString = responseDict[@"certificate"];
        NSData *certData = [Base64 decodeString:certString];

        SecIdentityRef identity = NULL;
        SecCertificateRef certificate = NULL;
        [Utils identity:&identity certificate:&certificate fromPKCS12Data:certData withPhrase:@"test"];

        if (identity) {
            // 把 identity, certificate 保存到 keychain 中
            NSArray *cerArray = [NSArray arrayWithObject:(__bridge id)certificate];
            NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:cerArray persistence:NSURLCredentialPersistencePermanent];

            NSURLProtectionSpace *certSpace = [[NSURLProtectionSpace alloc] initWithHost:@"xxx.com" port:443 protocol:NSURLProtectionSpaceHTTPS realm:@"mobileApp" authenticationMethod:NSURLAuthenticationMethodClientCertificate];

            [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:certSpace];
        }
    }
}

  从 NSData 中获取 identity,certificate

+ (void)identity:(SecIdentityRef *)identity certificate:(SecCertificateRef *)certificate fromPKCS12Data:(NSData *)certData withPhrase:(NSString *)phrase {

    // bridge the import data to foundation objects
    CFStringRef importPassphrase = (__bridge CFStringRef)phrase;
    CFDataRef importData = (__bridge CFDataRef)certData;

    // create dictionary of options for the PKCS12 import
    const void *keys[] = {kSecImportExportPassphrase};
    const void *values[] = {importPassphrase};
    CFDictionaryRef importOptions = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

    // create array to store import results
    CFArrayRef importResults = CFArrayCreate(NULL, 0, 0, NULL);

    OSStatus pkcs12ImportStatus = SecPKCS12Import(importData, importOptions, &importResults);

    if (pkcs12ImportStatus == errSecSuccess) {
        CFDictionaryRef identityAndTrust = CFArrayGetValueAtIndex(importResults, 0);

        // retrieve the identity from the certificate imported
        const void *tempIdentity = CFDictionaryGetValue(identityAndTrust, kSecImportItemIdentity);
        *identity = (SecIdentityRef)tempIdentity;

        // extract certificate from the identity
        SecCertificateRef tmpCertificate = NULL;
        SecIdentityCopyCertificate(*identity, &tmpCertificate);
        *certificate = (SecCertificateRef)tmpCertificate;
    }

    // clean up
    if (importOptions) {
        CFRelease(importOptions);
    }
    if (importResults) {
        CFRelease(importResults);
    }
}

    2.2.2 由于 willSendRequestForAuthenticationChallenge 会在服务器与客户端认证过程中多次被调用,因此需要判断需要处理那个 challenge。以下代码用于确定是否应该发出客户端证书或者是使用辨准的用户凭证进行认证。

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

    // 使用客户端证书认证
    NSURLProtectionSpace *clientCertificateProtectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"xxx.com" port:443 protocol:NSURLProtectionSpaceHTTPS realm:@"mobileApp" authenticationMethod:NSURLAuthenticationMethodClientCertificate];

    if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate && isDeviceRegisted) {
        if (challenge.previousFailureCount == 0) {
            // 读取本地存储下来的客户端证书
            NSURLCredential *cred = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:clientCertificateProtectionSpace];

            if (cred) {
                [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
            }
        }
        else {
            [challenge.sender cancelAuthenticationChallenge:challenge];

            // 并提示用户认证失败
        }
    }
    else {
        // 使用其他的授权方式,例如 HTTP Basic, HTTP Digest, NTLM, 见 2.1 标准认证代码
    }

    // 如果不是应用能处理的类型,则不响应认证,继续连接
    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];

}

  在服务层,可以通过 openssl_x509_parse() 函数检索到证书的属性。在获取证书属性后,可以使用服务器层的很多认证选项。其中一个选项好似验证请求发起人,然后已知的私钥列表中查找该用户。另一个选项是在应用中使用 PIN 机制,从而在向认证发出客户端证书之前进行验证。

  如果是自签的 SSL 证书,NSURLConnection 会拦截掉不带信任证书的服务器响应。因此需要将服务器证书(.cer 文件扩展),以邮件的形式发送给设备上配置好的 email 账户,就可以单机并安装证书。

  

时间: 2024-10-12 16:54:36

保护网络传输的实现的相关文章

Android 密钥保护和 C/S 网络传输安全理论指南

注:本文将着重讲解 Android KeyStore.so 库保护 app key / secret.HTTPS 原理及其防中间人***措施. 谈到 Android 安全性话题,Android Developers 官方网站给出了许多很好的建议和讲解,涵盖了存储数据.权限.网络.处理凭据.输入验证.处理用户数据.加密等方方面面,甚至对于动态加载代码也提供了建议,具体可以看看 training 的?security tips?章节.而今天,我想特别来讲一讲在 Android 密钥保护和 C/S 网

网络传输基本协议

介绍 定义 在配置管理中,终端服务特性提供了设备配置的管理接入接口和交互界面,为用户提供操作场所. 主要包括: Console口登录 Telnet Server/Client SSH登录,支持Password.RSA验证.DSA验证 支持定制User-interface,提供对登录用户多种方式的认证和授权功能 文件传输特性可以提供系统文件.配置文件的传输控制和文件系统的远程简单管理. 主要包括: FTP Server/Client TFTP Client 基于SSH协议的文件传输SFTP Cli

网络传输协议

网络传输协议 1.常见协议 1.HTTP.HTTPS 超文本传输协议 2.FTP 文件传输协议 3.SMTP 简单邮件传输协议 2.http协议 超文本传输协议(HTTP,HyperText Transfer Protocol) 网站是基于HTTP协议的, 例如网站的图片.CSS.JS等都是基于HTTP协议进行传输的. HTML Hypertext Markup Language HTTP协议是由从客户机到服务器的请求(Request)和从服务器到客户机的响应(Response)进行了约束和规范

【转】android实时视频网络传输方案总结(一共有五套)

最近研究了Android的实时视频网络传输问题,在视频处理方面花费了大量精力进行研究,总结出以下五套方案,并加以比较 以320×240大小的视频传输为例 方案 压缩率 压缩/传输方式 实时性 平均流量消耗  传输距离 用camera的回调函数发送原始的yuv420数据 0 无压缩,按帧传输 高(20~30 fps) 很高(6.5 Mbps)太恐怖了O_O  近距离有线或无线 用MediaRecorder对yuv420进行H264硬编码后发送 高(95%) 帧间压缩,视频流传输 高(20 fps)

使用加密解密技术和CA认证解决网络传输中的安全隐患

服务端:xuegod63.cn   IP:192.168.1.63 客户端:xuegod64.cn   IP:192.168.1.64   网络安全: 网络传输中的安全隐患-.   中间人攻击 全隐患:        解决方法 1.窃听-- >  加密 2.篡改 ->  哈西算法:MD5,sha1 (检查数据完整性) 3.伪装(钩鱼网站,伪装WIFI)  ->  身份认证(用户名/密码.数字证书) 4.网络中断 (内网冒冲网关,DDOS )  –>绑定静态arp地址: 加大服务器和

atitit.二进制数据无损转字符串网络传输

1. gbk的网络传输问题,为什么gbk不能使用来传输二进制数据 1 2. base64 2 3. iso-8859-1  (推荐) 2 4. utf-8 (不能使用) 2 1. gbk的网络传输问题,为什么gbk不能使用来传输二进制数据 gbk会造成信息丢失 由于有些字符在gbk字符集中找不到对应的字符,所以默认使用编码63代替,也就是?(问号)...gbk仅仅能兼容低位asc编码(英文字母),高位编码要使用来编码汉字了... 作者::老哇的爪子Attilax艾龙,EMAIL:[email p

Oracle工具之--ASM与文件系统及跨网络传输文件

Oracle工具之--ASM与文件系统及跨网络传输文件   Oracle DBMS_FILE_TRANSFER可以实现文件系统和ASM磁盘组之间实现文件传输及ASM磁盘组之间跨网络的传输. DBMS_FILE_TRANSFER:   The DBMS_FILE_TRANSFER package provides procedures to copy a binary file within a database or to transfer a binary file between datab

浅谈跨国网络传输

在这个大数据,云部署不断映入眼帘的时代,也许很多人作为公司IT架构的管理者都会觉得有些无助和迷惘.新兴的科技确实给日常的IT工作带来了便利,但亦带来了种种挑战和不可预期的困难. 数据的存储,传输的便利固然重要,但是数据的安全却要重要的多.你永远都不会希望把自己的核心数据放到公共的存储空间中,也随即诞生了私有云等一系列的概念,但是终究还是第三方的架构方案,这种不可控性随时都可发生. 对于跨国的数据传输,国内的网络提供商无论是电信和联通都无法给出完美的答案,因为国内伟大的防火墙的原因,速度慢之又慢,

Android网络传输中必用的两个加密算法:MD5 和 RSA (附java完成测试代码)

MD5和RSA是网络传输中最常用的两个算法,了解这两个算法原理后就能大致知道加密是怎么一回事了.但这两种算法使用环境有差异,刚好互补. 一.MD5算法 首先MD5是不可逆的,只能加密而不能解密.比如明文是yanzi1225627,得到MD5加密后的字符串是:14F2AE15259E2C276A095E7394DA0CA9  但不能由后面一大串倒推出yanzi1225627.因此可以用来存储用户输入的密码在服务器上.现在下载文件校验文件是否中途被篡改也是用的它,原理参见:http://blog.c