iOS HTTPS 双向认证
iOS HTTPS 双向认证
搞了半天,记录一下,坑很多。
双向认证,就是在访问网络的时候进行证书认证,首先本地需要一个服务器证书,一个客户端证书。客户端发送请求,服务器返回服务器证书和本地服务器证书比对,然后客户端发送客户端证书到服务器。如果全部匹配就返回加密算法,然后可以访问网络,否则就不能访问。
流程
- 1.需要服务端提供认证证书.crt文件,然后自己导出成.cer文件
- 2.将导出的cer证书加入到项目中,注意勾选相应的target不然可能获取证书路径为nil
- 3.通过cer证书生成证书校验的安全策略
- 4.在AFNetworking的网络请求中设置安全策略:[_manager setSecurityPolicy:[CertificatehttpsTools customSecurityPolicy]];
- 5.通过抓包工具Charles检验请求和返回的内容是否加密
证书
- 需要server.cer 和client.p12两个文件,用的自签名证书
- 配置项目info.plist,增加下面几个属性
- 注意域名和服务器配置有关
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>xxxx.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
网络
- AFN3.0
- 然后就是设置AFN的安全参数
AFSecurityPolicy
- 在初始化网络工具类是指定这个参数
- 需要注意这个baseUrl问题,指定类似这种就行
https://192.168.0.22"
,不然就会报错'Invalid Security Policy', reason: 'A security policy configured with AFSSLPinningModeCertificate can only be applied on a manager with a secure base URL (i.e. https)'
- (void)setupAFNetwork{
AFHTTPSessionManager *manager = [[AFHTTPSessionManager manager] initWithBaseURL:[NSURL URLWithString:BaseURL]];
manager.securityPolicy = [self getCustomHttpsPolicy:manager];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"];
self.httpSessionManager = manager;
}
// 配置安全参数
-(AFSecurityPolicy*) getCustomHttpsPolicy:(AFHTTPSessionManager*)manager{
//https 公钥证书配置
NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"serverapple" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:certFilePath];
NSSet *certSet = [NSSet setWithObject:certData];
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certSet];
policy.allowInvalidCertificates = YES;
policy.validatesDomainName = NO;//是否校验证书上域名与请求域名一致
//https回调 客户端验证
[manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
NSLog(@"setSessionDidBecomeInvalidBlock");
}];
__weak typeof(manager)weakManger = manager;
__weak typeof(self)weakSelf = self;
//客户端请求验证 重写 setSessionDidReceiveAuthenticationChallengeBlock 方法
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *credential =nil;
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if([weakManger.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if(credential) {
disposition =NSURLSessionAuthChallengeUseCredential;
} else {
disposition =NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
// client authentication
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"clientapple"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
if ([[weakSelf class]extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
{
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
const void*certs[] = {certificate};
CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
*_credential = credential;
return disposition;
}];
return policy;
}
+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
OSStatus securityError = errSecSuccess;
//client certificate password
NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"123456"
forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
if(securityError == 0) {
CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
const void*tempIdentity =NULL;
tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
const void*tempTrust =NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
} else {
NSLog(@"Failedwith error code %d",(int)securityError);
return NO;
}
return YES;
}
本文来自博客园,作者:struggle_time,转载请注明原文链接:https://www.cnblogs.com/songliquan/p/12784559.html