(0079)iOS开发之安全策略之HTTPS(1)

最近公司产品在搞加强安全,所以有必要先了解一下HTTPS。这里只是找了两篇文章写的很好。特此拿来存到个人博客里。以便以后复习。

1.HTTPS传输流程
2.常用加密算法
3.AFN证书校验策略及核心方法
4.SSL Pinning
5.CA证书申请流程

HTTPS经由超文本传输协议进行通信,但利用SSL/TLS来对数据包进行加密。HTTPS开发的主要目的,是提供对网络服务器的身份认证,保护交换数据的隐私与完整性

1.HTTPS传输流程

HTTPS传输流程

由于不相信服务器生成的随机数,所以需要客户端生成pre-master-securet,然后再由服务端加密,最后得到master-securet(主密钥),建立密道通信。(减少被猜中的可能)

2.常用加密算法

非对称加密算法:RSA,DSA/DSS
对称加密算法:AES,RC4,3DES
HASH算法:MD5,SHA1,SHA256

非对称加密算法用于握手过程中的加密生成的密码,
对称加密算法用于对真正传输的数据进行加密,
HASH算法用于验证数据的完整性,是否被篡改等

非对称加密生成密码是整个加密过程的关键,非对称加密会生成公钥和私钥,
公钥负责数据加密,(可以随意传输)
私钥负责解密,(重中之重)

3.AFN证书校验策略及核心方法

NSURLSession 封装了HTTPS链接的建立加密解密功能,但并没有验证证书的合法性,无法避免中间人攻击,要真正的安全通讯,需要我们手动去验证证书,

客户端校验策略

sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; 
* AFSSLPinningModeNone 不做SSL pinning 只信任证书颁发机构证书,自己生成证书不通过 
* AFSSLPinningModeCertificate 客户端保存证书拷贝 第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。 
* AFSSLPinningModePublicKey 客户端保存证书拷贝 只是验证时只验证证书里的公钥,不验证证书的有效期等信息

核心证书校验方法

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
{
    // **** AFSSLPinningModeNone 不做SSL Pinning 当需要使用证书验证域名时,需要使用AFSSLPinningModePublicKey或AFSSLPinningModeCertificate
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {

        // According to the docs, you should only trust your provided certs for evaluation. Pinned certificates are added to the trust. Without pinned certificates, there is nothing to evaluate against.

        /*
        From Apple Docs:
        "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors). Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
         */


        NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
        return NO;

    }

    NSMutableArray *policies = [NSMutableArray array];

    //**** 需要验证域名时,添加一个域名验证策略
    if (self.validatesDomainName) {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];

    }

    //**** 设置验证策略
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        //**** AFSSLPinningModeNone 时,allowInvalidCertificates为YES ,则代表服务器任何证书都能验证 **** 如果是NO,则需要判断此服务器是否是系统信任证书
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);

    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        // **** 如果服务器证书不是系统信任证书,且不允许不信任证书通过验证则返回NO
        return NO;

    }

    switch (self.SSLPinningMode) {
        case AFSSLPinningModeNone:
            default:return NO;

        case AFSSLPinningModeCertificate:
        {
            //**** AFSSLPinningModeCertificate 是直接将本地证书设置为信任的根证书,然后来进行判断,并且比较本地证书内容和服务器证书内容是否一致,如果有一个相同则返回YES
            NSMutableArray *pinnedCertificates = [NSMutableArray array];
            for (NSData *certificateData in self.pinnedCertificates) {
                [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];

            }
            // **** 设置本地证书为根证书
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
            //**** 通过本地证书来判断服务器证书是否可信,不可信则不通过
            if (!AFServerTrustIsValid(serverTrust)) {
                return NO;
            }

            // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)

            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            // **** 判断本地证书和服务器证书是否相同
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            } return NO;

        }

        case AFSSLPinningModePublicKey:
        {
            NSUInteger trustedPublicKeyCount = 0;
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
            // **** AFSSLPinningModePublicKey 是通过比较证书中公钥部分来进行校验。通过SecTrustCopyPublicKey方法获取本地证书和服务器证书,进行比较,如果有一个相同则验证通过
            for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;

                    }
                }
            }
            return trustedPublicKeyCount > 0;
        }
    } return NO;
}

4.SSL Pinning

  • SSL Pinning

SSL Pinning 即证书绑定,客户端直接保存服务端证书,建立HTTPS连接时会校验服务端返回证书和客户端证书是否一致,一致则不再去信任证书机构里验证。

  • SSL Pinning 为什么安全
    其实如果中间人从客户端取出证书(公钥),并使用证书冒充服务器与客户端进行通信时,也可以通过证书验证,但后续流程走不下去,因为客户端会用公钥加密,中间人从客户端截取的证书是公钥,缺少对应私钥即使截获了信息也无法解密。所以能够最大程度保护信息安全

PS: 从上面的通信过程中,最重要的是存储在服务器的私钥。因为只有私钥生成了在通信过程中传递的证书(公钥),且只有通过私钥才能对公钥加密的信息进行解密,所以在开发过程中保护好私钥的安全。
什么时候使用SSL Pinning

如果证书是从受信任的CA机构颁布的,验证是没有问题的,如果是自己颁发证书,无法通过系统受信任的CA机构列表验证证书时,需要通过SSL Pinnig的方式来验证

如何使用SSL Pinning

1.将.cer 证书放进工程中
2.设置securityPolicy证书校验策略

#pragma mark - SecurityPolicy 
+ (void)securityPolicy:(AFHTTPSessionManager *)sessionManager{ 
sessionManager.securityPolicy = [AFSecurityPolicy defaultPolicy];
sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; 

/** allowInvalidCertificates 是否允许不信任的证书(证书无效、证书时间过期)通过验证 ,默认为NO */
[sessionManager.securityPolicy setAllowInvalidCertificates:NO]; 

/** validatesDomainName 是否验证域名证书的CN(common name)字段 默认YES*/ 
sessionManager.securityPolicy.validatesDomainName = YES; 

/** NSSet<NSData*> *pinnedCertificates 根据验证模式来返回用于验证服务器的证书。*/ 
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"XXXX" ofType:@"cer"]; 
NSData *certData = [NSData dataWithContentsOfFile:cerPath]; [sessionManager.securityPolicy setPinnedCertificates:[NSSet setWithArray:@[certData]]]; 
}

补充:SSL 与 TLS 协议

SSL协议(传输层安全协议)工作方式:客户端要收发几个握手信号:
1.发送一个“ClientHello”消息。内容包括支持的协议版本,比如TLS1.0版
2.收到一个“ServerHello”消息。内容包括支持的协议版本,
3.客户端与服务端交换证书。(依靠被选择的公钥系统)
4.服务端请求客户端公钥。客户端有证书即双向身份认证,没证书时随机生成公钥
5.客户端与服务端通过公钥保密协议共同的主私钥(伪随机数)

加强

在SSL 3.0中发现设计缺陷后,SSL 被禁用,之后 ETF将SSL标准化,即 RFC 2246 ,并将其称为TLS(Transport Layer Security),即TLS 1.0
TLS 1.0包括可以降级到SSL 3.0的实现,这削弱了连接的安全性
TLS 1.1 添加对CBC攻击的保护:隐式IV被替换成一个显式的IV。更改分组密码模式中的填充错误。支持IANA登记的参数。
TLS 1.2 可使用密码组合选项指定伪随机函数使用SHA-256替换MD5-SHA-1组合。AES加密的支持
TLS 1.3 草案 2016年1月

作者:StarkShen
链接:https://www.jianshu.com/p/309d5359d5cb
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关链接:
http://www.jianshu.com/p/668b263befc8
https://runningyoung.github.io/2016/05/16/2016-05-16-HTTPS/

猜你喜欢

转载自blog.csdn.net/shifang07/article/details/79304628