美文网首页
AFNetworking3.0 HTTPS请求

AFNetworking3.0 HTTPS请求

作者: Sniper_Zheng | 来源:发表于2016-06-24 11:29 被阅读1108次

    最近公司把网络请求从HTTP改成HTTPS 加密 证书这些东西 在这里整理一下

    HTTPS

    HTTPS连接建立过程大致是,客户端和服务端建立一个连接,服务端返回一个证书,客户端里存有各个受信任的证书机构根证书,用这些根证书对服务端 返回的证书进行验证,经验证如果证书是可信任的,就生成一个pre-master secret,用这个证书的公钥加密后发送给服务端,服务端用私钥解密后得到pre-master secret,再根据某种算法生成master secret,客户端也同样根据这种算法从pre-master secret生成master secret,随后双方的通信都用这个master secret对传输数据进行加密解密。

    1.证书是怎样验证的?怎样保证中间人不能伪造证书?

    首先要知道非对称加密算法的特点,非对称加密有一对公钥私钥,用公钥加密的数据只能通过对应的私钥解密,用私钥加密的数据只能通过对应的公钥解密。

    我们来看最简单的情况:一个证书颁发机构(CA),颁发了一个证书A,服务器用这个证书建立https连接。客户端在信任列表里有这个CA机构的根证书。

    首先CA机构颁发的证书A里包含有证书内容F,以及证书加密内容F1,加密内容F1就是用这个证书机构的私钥对内容F加密的结果。(这中间还有一次hash算法,略过。)

    建立https连接时,服务端返回证书A给客户端,客户端的系统里的CA机构根证书有这个CA机构的公钥,用这个公钥对证书A的加密内容F1解密得 到F2,跟证书A里内容F对比,若相等就通过验证。整个流程大致是:F->CA私钥加密->F1->客户端CA公钥解密->F。 因为中间人不会有CA机构的私钥,客户端无法通过CA公钥解密,所以伪造的证书肯定无法通过验证。

    2.什么是SSL Pinning?

    可以理解为证书绑定,是指客户端直接保存服务端的证书,建立https连接时直接对比服务端返回的和客户端保存的两个证书是否一样,一样就表明证书 是真的,不再去系统的信任证书机构里寻找验证。这适用于非浏览器应用,因为浏览器跟很多未知服务端打交道,无法把每个服务端的证书都保存到本地,但CS架 构的像手机APP事先已经知道要进行通信的服务端,可以直接在客户端保存这个服务端的证书用于校验。

    为什么直接对比就能保证证书没问题?如果中间人从客户端取出证书,再伪装成服务端跟其他客户端通信,它发送给客户端的这个证书不就能通过验证吗?确 实可以通过验证,但后续的流程走不下去,因为下一步客户端会用证书里的公钥加密,中间人没有这个证书的私钥就解不出内容,也就截获不到数据,这个证书的私 钥只有真正的服务端有,中间人伪造证书主要伪造的是公钥。

    为什么要用SSL Pinning?正常的验证方式不够吗?如果服务端的证书是从受信任的的CA机构颁发的,验证是没问题的,但CA机构颁发证书比较昂贵,小企业或个人用户 可能会选择自己颁发证书,这样就无法通过系统受信任的CA机构列表验证这个证书的真伪了,所以需要SSL Pinning这样的方式去验证。

    3.利用AFNetworking完成HTTPS请求 (AFSecurityPolicy)

    首先把证书放在工程的根目录下

    AFNetworking部分

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
            
    securityPolicy.allowInvalidCertificates = YES;
    securityPolicy.validatesDomainName = YES;
            
    manager.securityPolicy = securityPolicy;
    

    我遇到的坑:

    1.每次请求都崩溃原因是往数组里加了空对象

    断点跟代码
    AFNetworking的AFSecurityPolicy.m里

    for (NSData *certificateData in self.pinnedCertificates) {
        [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
    }
    

    在这里一直崩溃
    原因是因为本地的那个证书里没有公钥!!!!

    //从本地证书里取public key
    static id AFPublicKeyForCertificate(NSData *certificate) {
        id allowedPublicKey = nil;
        
        ...
        
        // 这里的allowedPublicKey 一直是空!! 
        return allowedPublicKey;
    }
    

    换了一个正确的证书之后 这个问题解决了 但是出别的问题

    证书总是验证不过 (本地证书没问题 服务器给的公钥也OK)

    if (!AFServerTrustIsValid(serverTrust)) {
        return NO;
    }
    

    总是返回NO

    跟进去

    static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
        BOOL isValid = NO;
        SecTrustResultType result;
        __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
        //kSecTrustResultUnspecified:证书通过验证,但用户没有设置这些证书是否被信任
        //kSecTrustResultProceed:证书通过验证,用户有操作设置了证书被信任,例如在弹出的是否信任的alert框中选择always trust
        isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
        // 总是返回5  也就是kSecTrustResultRecoverableTrustFailure
    _out:
        return isValid;
    }
    

    原因:是因为Charles的问题!!! 就这个问题纠结了好久 偶然间发现的

    参考:
    http://www.jianshu.com/p/c6a903da8346
    http://my.oschina.net/vimfung/blog/494687
    http://www.cocoachina.com/ios/20140916/9632.html

    相关文章

      网友评论

          本文标题:AFNetworking3.0 HTTPS请求

          本文链接:https://www.haomeiwen.com/subject/yneodttx.html