1 HTTPS以及SSL/TSL SSL(Secure Sockets Layer, 安全套接字层),因为原先互联网上使用的HTTP协议是明文的,存在很多缺点,比如传输内容会被偷窥和篡改。SSL协议的作用就是在传输层对网络连接进行加密。
到了1999年,SSL 因为应用广泛,已经成为互联网上的事实标准。IETF就在那年把SSL标准化。标准化之后的名称改为 TLS(Transport Layer Security,传输层安全协议)。SSL与TLS可以视作同一个东西的不同阶段。
简单来说,HTTPS = HTTP + SSL/TLS, 也就是HTTP over SSL
或HTTP over TLS
,这是后面加S的由来。
HTTPS和HTTP异同:HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。HTTP的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。
2 HTTPS的握手
1客户端发出请求(ClientHello) 首先,客户端(通常是浏览器)先向服务器发出加密通信的请求,这被叫做ClientHello请求。在这一步,客户端主要向服务器提供以下信息。
(1)支持的协议版本,比如TLS1.0版。
(2)一个客户端生成的随机数,稍后用于生成”对话密钥”。
(3)支持的加密方法,比如RSA公钥加密。
(4)支持的压缩方法。
2服务器回应(SeverHello) 服务器收到客户端请求后,向客户端发出回应,这叫做SeverHello。服务器的回应包含以下内容。
(1)确认使用的加密通信协议版本,比如TLS1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。
(2)一个服务器生成的随机数,稍后用于生成”对话密钥”。
(3)确认使用的加密方法,比如RSA公钥加密。
(4)服务器证书。
3客户端回应 客户端收到服务器回应以后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。
(1)一个随机数。该随机数用服务器公钥加密,防止被窃听。
(2)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。
上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称”pre-master key”。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把”会话密钥”。
4服务器的最后回应 服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的”会话密钥”。然后,向客户端最后发送下面信息。
(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。
至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用”会话密钥”加密内容。
3 数字证书 上面握手阶段的第二步服务器给客户端的证书就是数字证书,该证书包含了公钥等信息,一般是由服务器发给客户端,接收方通过验证这个证书是不是由信赖的CA签发,或者与本地的证书相对比,来判断证书是否可信;假如需要双向验证,则服务器和客户端都需要发送数字证书给对方验证。
数字证书是一个电子文档,其中包含了持有者的信息、公钥以及证明该证书有效的数字签名。而数字证书以及相关的公钥管理和验证等技术组成了PKI(公钥基础设施)规范体系。一般来说,数字证书是由数字证书认证机构(Certificate authority,即CA)来负责签发和管理,并承担PKI体系中公钥合法性的检验责任;数字证书的类型有很多,而HTTPS使用的是SSL证书。
怎么来验证数字证书是由CA签发的,而不是第三方伪造的呢? 在回答这个问题前,我们需要先了解CA的组织结构。首先,CA组织结构中,最顶层的就是根CA,根CA下可以授权给多个二级CA,而二级CA又可以授权多个三级CA,所以CA的组织结构是一个树结构。对于SSL证书市场来说,主要被Symantec(旗下有VeriSign和GeoTrust)、Comodo SSL、Go Daddy 和 GlobalSign 瓜分。 了解了CA的组织结构后,来看看数字证书的签发流程:
数字证书的签发机构CA,在接收到申请者的资料后进行核对并确定信息的真实有效,然后就会制作一份符合X.509标准的文件。证书中的证书内容包括了持有者信息和公钥等都是由申请者提供的,而数字签名则是CA机构对证书内容进行hash加密后等到的,而这个数字签名就是我们验证证书是否是有可信CA签发的数据。
接收端接到一份数字证书Cer1后,对证书的内容做Hash等到H1;然后在签发该证书的机构CA1的数字证书中找到公钥,对证书上数字签名进行解密,得到证书Cer1签名的Hash摘要H2;对比H1和H2,假如相等,则表示证书没有被篡改。但这个时候还是不知道CA是否是合法的,我们看到上图中有CA机构的数字证书,这个证书是公开的,所有人都可以获取到。而这个证书中的数字签名是上一级生成的,所以可以这样一直递归验证下去,直到根CA。根CA是自验证的,即他的数字签名是由自己的私钥来生成的。合法的根CA会被浏览器和操作系统加入到权威信任CA列表中,这样就完成了最终的验证。所以,一定要保护好自己环境(浏览器/操作系统)中根CA信任列表,信任了根CA就表示信任所有根CA下所有子级CA所签发的证书,不要随便添加根CA证书。
4 SSL Pinning 可以理解为证书绑定,是指客户端直接保存服务端的证书,建立https连接时直接对比服务端返回的和客户端保存的两个证书是否一样,一样就表明证书是真的,不再去系统的信任证书机构里寻找验证。这适用于非浏览器应用,因为浏览器跟很多未知服务端打交道,无法把每个服务端的证书都保存到本地,但CS架构的像手机APP事先已经知道要进行通信的服务端,可以直接在客户端保存这个服务端的证书用于校验。
为什么直接对比就能保证证书没问题?如果中间人从客户端取出证书,再伪装成服务端跟其他客户端通信,它发送给客户端的这个证书不就能通过验证吗?确实可以通过验证,但后续的流程走不下去,因为下一步客户端会用证书里的公钥加密,中间人没有这个证书的私钥就解不出内容,也就截获不到数据,这个证书的私钥只有真正的服务端有,中间人伪造证书主要伪造的是公钥。
为什么要用SSL Pinning?正常的验证方式不够吗?如果服务端的证书是从受信任的的CA机构颁发的,验证是没问题的,但CA机构颁发证书比较昂贵,小企业或个人用户可能会选择自己颁发证书,这样就无法通过系统受信任的CA机构列表验证这个证书的真伪了,所以需要SSL Pinning这样的方式去验证。
5 iOS的HTTPS请求 下面我会实现自签名证书(12306)、SSL信任证书(baidu)、系统证书(苹果)三种情况的实现来看他们的区别,百度和12306的证书已经被我下载到我的项目里面了,具体可以去Demo里面看实现过程。
1 自签名证书 我们手动指定securityPolicy
认证属性。通过12306证书来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 - (IBAction )buttion1:(id )sender { NSURL *url = [NSURL URLWithString:@"https://kyfw.12306.cn/otn/leftTicket/init" ]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; manager.securityPolicy = [self ticketSecurityPolicy]; AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer]; responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html" ]; manager.responseSerializer = responseSerializer; NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { NSLog (@"%@-----%@" ,[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding ],error); }]; [dataTask resume]; } -(AFSecurityPolicy*)ticketSecurityPolicy { NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"12306" ofType:@"cer" ]; NSData *certData = [NSData dataWithContentsOfFile:cerPath]; NSSet *set = [NSSet setWithObject:certData]; AFSecurityPolicy *securityPolicy; if (true ) { securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:set]; }else { securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; } securityPolicy.allowInvalidCertificates = YES ; securityPolicy.validatesDomainName = NO ; return securityPolicy; }
2 SSL信任证书 我们手动指定securityPolicy
认证属性。通过百度证书来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 - (IBAction )button2:(id )sender { NSURL *url = [NSURL URLWithString:@"https://www.baidu.com" ]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; manager.securityPolicy = [self baiduSecurityPolicy]; AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer]; responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html" ]; manager.responseSerializer = responseSerializer; NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { NSLog (@"%@-----%@" ,[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding ],error); }]; [dataTask resume]; } -(AFSecurityPolicy*)baiduSecurityPolicy { NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"baidu" ofType:@"cer" ]; NSData *certData = [NSData dataWithContentsOfFile:cerPath]; NSSet *set = [NSSet setWithObject:certData]; AFSecurityPolicy *securityPolicy; if (true ) { securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone withPinnedCertificates:set]; }else { securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; } securityPolicy.allowInvalidCertificates = NO ; securityPolicy.validatesDomainName = YES ; return securityPolicy; }
3 SSL证书AFN默认处理 这里我们不做任何额外的处理,直接使用AFN的默认证书处理机制。通过AFURLSessionManager
的securityPolicy
默认实现。它会和存在系统中的做对比来验证证书。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 - (IBAction )button3:(id )sender { NSURL *url = [NSURL URLWithString:@"https://www.apple.com/" ]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer]; responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html" ]; manager.responseSerializer = responseSerializer; NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { NSLog (@"%@-----%@" ,[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding ],error); }]; [dataTask resume]; }
6 AFSecurityPolicy源码解析 AFSecurityPolicy分三种验证模式
AFSSLPinningModeNone: 这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。
AFSSLPinningModeCertificate: 这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。这里还没弄明白第一步的验证是怎么进行的,代码上跟去系统信任机构列表里验证一样调用了SecTrustEvaluate,只是这里的列表换成了客户端保存的那些证书列表。若要验证这个,是否应该把服务端证书的颁发机构根证书也放到客户端里?
AFSSLPinningModePublicKey: 这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。
SecTrustRef 这是一个需要验证的信任对象,包含待验证的证书和支持的验证方法等。
SecTrustResultType 表示验证结果。其中 kSecTrustResultProceed表示serverTrust验证成功,且该验证得到了用户认可(例如在弹出的是否信任的alert框中选择always trust)。 kSecTrustResultUnspecified表示 serverTrust验证成功,此证书也被暗中信任了,但是用户并没有显示地决定信任该证书。 两者取其一就可以认为对serverTrust验证成功。
SecTrustEvaluate 证书校验函数,在函数的内部递归地从叶节点证书到根证书验证。需要验证证书本身的合法性(验证签名完整性,验证证书有效期等);验证证书颁发者的合法性(查找颁发者的证书并检查其合法性,这个过程是递归的).而递归的终止条件是证书验证过程中遇到了锚点证书(锚点证书:嵌入到操作系统中的根证书,这个根证书是权威证书颁发机构颁发的自签名证书)。
AFSecurityPolicy
的源码细节如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 typedef NS_ENUM (NSUInteger , AFSSLPinningMode) { AFSSLPinningModeNone, AFSSLPinningModePublicKey, AFSSLPinningModeCertificate, }; static id AFPublicKeyForCertificate(NSData *certificate) { id allowedPublicKey = nil ; SecCertificateRef allowedCertificate; SecPolicyRef policy = nil ; SecTrustRef allowedTrust = nil ; SecTrustResultType result; allowedCertificate = SecCertificateCreateWithData(NULL , (__bridge CFDataRef )certificate); __Require_Quiet(allowedCertificate != NULL , _out ); policy = SecPolicyCreateBasicX509(); __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out ); __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out ); allowedPublicKey = (__bridge_transfer id )SecTrustCopyPublicKey(allowedTrust); _out : if (allowedTrust) { CFRelease (allowedTrust); } if (policy) { CFRelease (policy); } if (allowedCertificate) { CFRelease (allowedCertificate); } return allowedPublicKey; } static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { BOOL isValid = NO ; SecTrustResultType result; __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out ); isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); _out : return isValid; } static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger )certificateCount]; for (CFIndex i = 0 ; i < certificateCount; i++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; } return [NSArray arrayWithArray:trustChain]; } static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { SecPolicyRef policy = SecPolicyCreateBasicX509(); CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger )certificateCount]; for (CFIndex i = 0 ; i < certificateCount; i++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); SecCertificateRef someCertificates[] = {certificate}; CFArrayRef certificates = CFArrayCreate (NULL , (const void **)someCertificates, 1 , NULL ); SecTrustRef trust; __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out ); SecTrustResultType result; __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out ); [trustChain addObject:(__bridge_transfer id )SecTrustCopyPublicKey(trust)]; _out : if (trust) { CFRelease (trust); } if (certificates) { CFRelease (certificates); } continue ; } CFRelease (policy); return [NSArray arrayWithArray:trustChain]; } #pragma mark - @interface AFSecurityPolicy ()@property (readwrite , nonatomic , assign ) AFSSLPinningMode SSLPinningMode;@property (readwrite , nonatomic , strong ) NSSet *pinnedPublicKeys;@end @implementation AFSecurityPolicy + (NSSet *)certificatesInBundle:(NSBundle *)bundle { NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"." ]; NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]]; for (NSString *path in paths) { NSData *certificateData = [NSData dataWithContentsOfFile:path]; [certificates addObject:certificateData]; } return [NSSet setWithSet:certificates]; } + (NSSet *)defaultPinnedCertificates { static NSSet *_defaultPinnedCertificates = nil ; static dispatch_once_t onceToken; dispatch_once (&onceToken, ^{ NSBundle *bundle = [NSBundle bundleForClass:[self class ]]; _defaultPinnedCertificates = [self certificatesInBundle:bundle]; }); return _defaultPinnedCertificates; } + (instancetype )defaultPolicy { AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = AFSSLPinningModeNone; return securityPolicy; } + (instancetype )policyWithPinningMode:(AFSSLPinningMode)pinningMode { return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]]; } + (instancetype )policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = pinningMode; [securityPolicy setPinnedCertificates:pinnedCertificates]; return securityPolicy; } - (instancetype )init { self = [super init]; if (!self ) { return nil ; } self .validatesDomainName = YES ; return self ; } - (void )setPinnedCertificates:(NSSet *)pinnedCertificates { _pinnedCertificates = pinnedCertificates; if (self .pinnedCertificates) { NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self .pinnedCertificates count]]; for (NSData *certificate in self .pinnedCertificates) { id publicKey = AFPublicKeyForCertificate(certificate); if (!publicKey) { continue ; } [mutablePinnedPublicKeys addObject:publicKey]; } self .pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys]; } else { self .pinnedPublicKeys = nil ; } } #pragma mark - - (BOOL )evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { if (domain && self .allowInvalidCertificates && self .validatesDomainName && (self .SSLPinningMode == AFSSLPinningModeNone || [self .pinnedCertificates count] == 0 )) { 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) { return self .allowInvalidCertificates || AFServerTrustIsValid(serverTrust); } else if (!AFServerTrustIsValid(serverTrust) && !self .allowInvalidCertificates) { return NO ; } switch (self .SSLPinningMode) { case AFSSLPinningModeNone: default : return NO ; case AFSSLPinningModeCertificate: { 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 ; } 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 ; } #pragma mark - NSKeyValueObserving + (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { return [NSSet setWithObject:@"pinnedCertificates" ]; } #pragma mark - NSSecureCoding + (BOOL )supportsSecureCoding { return YES ; } - (instancetype )initWithCoder:(NSCoder *)decoder { self = [self init]; if (!self ) { return nil ; } self .SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class ] forKey:NSStringFromSelector (@selector (SSLPinningMode))] unsignedIntegerValue]; self .allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector (@selector (allowInvalidCertificates))]; self .validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector (@selector (validatesDomainName))]; self .pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class ] forKey:NSStringFromSelector (@selector (pinnedCertificates))]; return self ; } - (void )encodeWithCoder:(NSCoder *)coder { [coder encodeObject:[NSNumber numberWithUnsignedInteger:self .SSLPinningMode] forKey:NSStringFromSelector (@selector (SSLPinningMode))]; [coder encodeBool:self .allowInvalidCertificates forKey:NSStringFromSelector (@selector (allowInvalidCertificates))]; [coder encodeBool:self .validatesDomainName forKey:NSStringFromSelector (@selector (validatesDomainName))]; [coder encodeObject:self .pinnedCertificates forKey:NSStringFromSelector (@selector (pinnedCertificates))]; } #pragma mark - NSCopying - (instancetype )copyWithZone:(NSZone *)zone { AFSecurityPolicy *securityPolicy = [[[self class ] allocWithZone:zone] init]; securityPolicy.SSLPinningMode = self .SSLPinningMode; securityPolicy.allowInvalidCertificates = self .allowInvalidCertificates; securityPolicy.validatesDomainName = self .validatesDomainName; securityPolicy.pinnedCertificates = [self .pinnedCertificates copyWithZone:zone]; return securityPolicy; } @end
最后原文地址 ,demo地址 。