客户端 A 向 服务端 B 发起请求,传输了 A 所支持的 SSL/TLS 协议版本列表、支持的对称加密算法列表、随机数 a。
B 选择一个 SSL/TLS 协议版本、对称加密算法、包含自己公钥的证书、随机数 b 发送给 A。
A 对 B 发送的 证书 进行验证,包括 域名是否一致、证书是否过期吊销、证书是否可信。对于证书是否可信,设备中一般会有一个证书信任列表,存储了这些证书机构的公钥,证书中包含了对 B 公钥的加密,和机构对信息的签名,设备用机构的公钥进行验签,验证通过则代表 B 公钥的可信度。这时候 A 生成一个随机数,使用 B 公钥进行加密,传输给 B 。
最后一步,B 用自己的私钥解密,得到 c ,这时候A he B 都得到了 a b c,使用 a b c 生成对称加密的密钥,后续中就使用这个密钥进行数据加密传输。
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html // 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."); /// 假如我们配置的需要验证域名,那么 SSLPinningNode 就不能设置为 None,必须设置为其他类型。 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()]; }
// 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); /// 获取证书中的公钥数组,与自己设置的证书中的公钥数组进行比对,有一个相同即可。 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; }
可以看到,我们使用 AF 进行 https 请求时。
假如我们想无条件的信任服务器的证书,就将 manager.securityPolicy.SSLPinningMode = AFSSLPinningModeNone,manager.securityPolicy.allowInvalidCertificates = NO ,这样服务端要求进行 服务端证书认证 时,是直接信任的。 直接信任服务端的证书,会有 中间人攻击 的隐患,我们使用 Charles 进行 HTTPS 抓包时,对于 客户端 ,Charles 是服务端,对于 服务端,Charles 是客户端,客户端收到的 证书认证中,证书 是 Charles 的证书,系统会通过认证。