ios--https自签CA的双向认证
之前自签https也做过,不过都是单向的,服务端认证客户端的。
这次项目需求遇到需要双向认证的,保证一次接口请求的双方都是符合“安全考验”的。
废话不多说,现在网络上这样的代码示例有很多,下面我来写一下我自己的梳理。
1,整个需求逻辑
1),现在客户端ios--服务端server,要建立https双向认证。
2),ios开发不管是OC的AFN还是swift是Alamofire,都是我们最常用的网络请求框架,我下面拿oc的afnetworking做示例。
3),建立双向认证,咱们客户端需要在工程集成两种证书,一个cer,一个p12。
4),两个证书的来源,确定的说都是来自后台服务端开发,开发过程中都是服务端创建CA根证书,而ios客户端集成需要两种证书都是需要CA根证书签发。
5),校验两种证书的代码,以及info.plist设置
2,代码
info.plist设置看我的这一篇N久前写的简书ios-App使用AFNetworking支持https(CA证书和自签证书)
cer证书代码,上面简书已经有了。下面再啰嗦一遍,多写一遍多熟悉一点。😝
/* cer 证书处理 begin */
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
policy.allowInvalidCertificates = YES;//客户端是否信任非法证书
policy.validatesDomainName = NO;//是否在证书域字段中验证域名
manager.securityPolicy= policy;
//关闭缓存避免干扰测试
manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
/* cer 证书处理 end */
处理p12证书的代码
//校验证书
+ (void)checkCredential:(AFURLSessionManager *)manager
{
[managersetSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
}];
__weaktypeof(manager)weakManager = manager;
[managersetSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasingNSURLCredential*credential =nil;
NSLog(@"authenticationMethod=%@",challenge.protectionSpace.authenticationMethod);
//判断服务器要求客户端的接收认证挑战方式,如果是NSURLAuthenticationMethodServerTrust则表示去检验服务端证书是否合法,NSURLAuthenticationMethodClientCertificate则表示需要将客户端证书发送到服务端进行检验
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 基于客户端的安全策略来决定是否信任该服务器,不信任的话,也就没必要响应挑战
if([weakManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
// 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 确定挑战的方式
if(credential) {
//证书挑战 设计policy,none,则跑到这里
disposition =NSURLSessionAuthChallengeUseCredential;
}else{
disposition =NSURLSessionAuthChallengePerformDefaultHandling;
}
}else{
disposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
}else{//只有双向认证才会走这里
// client authentication
SecIdentityRefidentity =NULL;
SecTrustReftrust =NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManagerfileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData*PKCS12Data = [NSDatadataWithContentsOfFile:p12];
NSLog(@"--PKCS12Data-->%@",PKCS12Data);
if([selfextractIdentity:&identityandTrust:&trustfromPKCS12Data:PKCS12Data])
{
SecCertificateRefcertificate =NULL;
SecIdentityCopyCertificate(identity, &certificate);
constvoid*certs[] = {certificate};
CFArrayRefcertArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
*_credential = credential;
returndisposition;
}];
}
//读取p12文件中的密码
+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentityandTrust:(SecTrustRef*)outTrustfromPKCS12Data:(NSData*)inPKCS12Data {
OSStatussecurityError =errSecSuccess;
//client certificate password 123456
NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"123456"
forKey:(__bridgeid)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError =SecPKCS12Import((__bridgeCFDataRef)inPKCS12Data,(__bridgeCFDictionaryRef)optionsDictionary,&items);
if(securityError ==0) {
CFDictionaryRefmyIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
constvoid*tempIdentity =NULL;
tempIdentity=CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
constvoid*tempTrust =NULL;
tempTrust =CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
}else{
NSLog(@"Failedwith error code %d",(int)securityError);
returnNO;
}
return YES;
}
整体的调用逻辑
遇到,解决,记录,分享~~