前段是時間做項目要求進行雙向認證屡萤,網(wǎng)上的查了很多都是不全的灯抛,今天將我整理之后的分享給大家沃疮。
廢話不多說直接上最主要的代碼(這里主要是在NSURLSession的 didReceiveChallenge: 代理方法里進行操作)职辅。
// 當對https請求時都會走此代理
```
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil;
// 服務器驗證
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
/* 調用自定義的驗證過程 */
if ([self myCustomValidation:challenge]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
}
} else {
/* 無效的話蹬挺,取消 */
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
}else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
{//客戶端認證
NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data);
SecIdentityRef identity;
// 讀取p12證書中的內容
OSStatus result = [self extractP12Data:inPKCS12Data toIdentity:&identity];
if(result != errSecSuccess){
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate (identity, &certificate);
const void *certs[] = {certificate};
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
credential = [NSURLCredential credentialWithIdentity:identity certificates:(NSArray*)CFBridgingRelease(certArray) persistence:NSURLCredentialPersistencePermanent];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
```
-(OSStatus) extractP12Data:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity {
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("the_password");//證書密碼
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inP12Data, options, &items);
if (securityError == 0) {
CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
}
if (options) {
CFRelease(options);
}
return securityError;
}
```
- (BOOL)myCustomValidation:(NSURLAuthenticationChallenge *)challenge
{
//獲取trust object
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType result;
//導入證書
NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"cer"];; //證書的路徑
NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
NSMutableArray *trustedCertificates = [NSMutableArray new];
[trustedCertificates addObject:(__bridge_transfer id )(certificate)];
//trustedCertificates = @[CFBridgingRelease(certificate)];
//注意:這里將之前導入的證書設置成下面驗證的Trust Object的anchor certificate
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)trustedCertificates);
//2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設置的證書或者系統(tǒng)默認提供的證書,對trust進行驗證
OSStatus status = SecTrustEvaluate(trust, &result);
if (status == errSecSuccess &&
(result == kSecTrustResultProceed ||
result == kSecTrustResultUnspecified)){
return YES;
}
return NO;
}
```