URL載入系統之四:認證與TLS鏈驗證

一個蘿蔔壹個坑發表於2018-01-03

一個NSURLRequest物件經常會遇到認證請求,或者需要從其所連線的服務端請求證書。當需要認證請求時,NSURLConnection、NSURLSession和NSURLDownload類會通知它們的代理物件,以便能正確地做處理。不過需要注意的是,URL載入系統只有在服務端響應包含WWW-Authenticate頭時才會呼叫代理來處理認證請求,而類似於代理認證和TLS信任驗證這樣的認證型別則不需要這個頭。

確定如何響應一個認證請求

如果一個NSURLRequest物件需要認證時,則認證請求方式取決於使用的物件的型別:

如果請求是與NSURLSession物件關聯,則所有認證請求都會傳遞給代理,而不考慮認證的型別。

如果請求是與NSURLConnection或NSURLDownload物件,則物件的代理接收一個connection:canAuthenticateAgainstProtectionSpace: (或者 download:canAuthenticateAgainstProtectionSpace:) 訊息。這允許代理物件在嘗試再次認證前分析服務端的屬性,包括協議和認證方法。如果我們的代理物件不準備認證服務端的受保護空間,則返回NO,且系統嘗試使用使用者的keychain的資訊進行認證。

如果NSURLConnection或NSURLDownload的代理物件沒有實現connection:canAuthenticateAgainstProtectionSpace: (或者 download:canAuthenticateAgainstProtectionSpace:)方法,且保護空間使用客戶端證書認證或服務端信任認證,則系統假設我們返回NO。而物件其它所有型別,系統都返回YES。

下一步,如果我們的代理物件同意處理認證,但是沒有有效的證書(不管是作為請求URL的一部分或者在NSURLCredentialStorage中共享),則代理以收到以下訊息:

URLSession:didReceiveChallenge:completionHandler:2. URLSession:task:didReceiveChallenge:completionHandler:3. connection:didReceiveAuthenticationChallenge:4. download:didReceiveAuthenticationChallenge:

為了讓連線能夠繼續,則代理物件有三種選擇:

提供認證證書

嘗試在沒有證書的情況下繼續

取消認證查詢

為了確保操作的正確流程,傳遞給這些方法的NSURLAuthenticationChallenge例項會包含一些資訊,包括是什麼觸發了認證查詢、查詢的嘗試次數、任何先前嘗試的證書、請求證書的NSURLProtectionSpace物件,及查詢的傳送者。

如果認證請求事先嚐試認證且失敗了(如使用者在服務端修改了密碼),我們可以通過在認證請求呼叫proposedCredential來獲取嘗試憑據。代理可以使用這些證書來填充一個顯示給使用者的話框。

呼叫認證請求的previousFailureCount可以返回身份驗證嘗試次數,這些嘗試包括不同認證協議的嘗試請求。代理可以將這些方法提供給使用者,以確定先前提供的證書是否失敗,或限制最大認證嘗試次數。

響應認證請求

前面說過三種響應我們響應connection:didReceiveAuthenticationChallenge:代理方法的方式,我們將逐一介紹:

提供證書

為了進行認證,程式需要使用服務端期望的認證資訊建立一個NSURLCredential物件。我們可以呼叫authenticationMethod來確定服務端的認證方法,這個認證方法是在提供的認證請求的保護空間中。NSURLCredential支援一些方法:

HTTP基本認證(NSURLAuthenticationMethodHTTPBasic):需要使用者名稱和密碼。提示使用者輸入必要資訊並使用credentialWithUser:password:persistence:方法建立一個NSURLCredential物件。

HTTP數字認證(NSURLAuthenticationMethodHTTPDigest):類似於基本認證,需要使用者名稱和密碼。提示使用者輸入必要資訊並使用credentialWithUser:password:persistence:方法建立一個NSURLCredential物件。

客戶端證書認證(NSURLAuthenticationMethodClientCertificate): 需要系統標識和所有服務端認證所需要的證書。然後使用credentialWithIdentity:certificates:persistence:來建立一個NSURLCredential物件。

服務端信任認證(NSURLAuthenticationMethodServerTrust)需要一個由認證請求的保護空間提供的信任。使用credentialForTrust:來建立一個NSURLCredential物件。

在建立NSURLCredential物件後

對於NSURLSession,使用提供的完成處理block將該物件傳遞給認證請求傳送者

對於NSURLConnection和NSURLDownload,使用useCredential:forAuthenticationChallenge:方法將物件傳遞給認證請求傳送者。

嘗試在沒有證書的情況下繼續

如果代理選擇不提供證書,可以嘗試繼續操作:

對於NSURLSession,傳遞下面的值給完成處理block: NSURLSessionAuthChallengePerformDefaultHandling:處理請求。儘管代理沒有提供代理方法來處理認證請求 NSURLSessionAuthChallengeRejectProtectionSpace:拒絕請求。依賴於服務端響應允許的認證型別,URL載入類可能多次呼叫這個代理方法。

對於NSURLConnection和NSURLDownload,在[challenge sender]中呼叫continueWithoutCredentialsForAuthenticationChallenge:。

依賴於協議的實現,這種處理方法可能會導致連線失敗而以送connectionDidFailWithError:訊息,或者返回可選的不需要認證的URL內容。

取消連線

代理可以選擇取消認證請求

對於NSURLSession,傳遞NSURLSessionAuthChallengeCancelAuthenticationChallenge給完成處理block

對於NSURLConnection和NSURLDownload,在[challenge sender]中呼叫cancelAuthenticationChallenge:。代理接收connection:didCancelAuthenticationChallenge:訊息,以提供使用者反饋的機會。

下面的程式碼演示了使用使用者名稱和密碼建立NSURLCredential物件來響應認證請求

-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

{

if ([challenge previousFailureCount] == 0)

{

NSURLCredential *newCredential;

newCredential = [NSURLCredential credentialWithUser:[self preferencesName] password:[self preferencesPassword] persistence:NSURLCredentialPersistenceNone];

[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];

}

else

{          [[challenge sender] cancelAuthenticationChallenge:challenge];          // inform the user that the user name and password          // in the preferences are incorrect        [self showPreferencesCredentialsAreIncorrectPanel:self];        }

}

如果代理沒有實現connection:didReceiveAuthenticationChallenge:,而請求需要認證,則有效的證書必須位於URL證書儲存中或作為請求URL的一部分。如果證書無效或者認證失敗,則底層實現會傳送一個continueWithoutCredentialForAuthenticationChallenge:訊息。

執行自定義TLS鏈驗證

在NSURL系統的AIP中,TLS鏈驗證由應用的認證代理方法來處理,但它不是提供證書給服務端以驗證使用者,而是在TLS握手的過程中校驗服務端提供的證書,然後再告訴URL載入系統是否應該接受還是拒絕這些證書。

如果需要以非標準的方法(如接收一個指定的自標識的證書用於測試)來執行鏈驗證,則應用必須如下處理:

對於NSURLSession,實現URLSession:didReceiveChallenge:completionHandler:和URLSession:task:didReceiveChallenge:completionHandler:代理方法。如果實現了兩者,由會話級別的方法負責處理認證。

對於NSURLConnection和NSURLDownload,實現connection:canAuthenticateAgainstProtectionSpace:和download:canAuthenticateAgainstProtectionSpace:方法,如果保護空間有一個NSURLAuthenticationMethodServerTrust型別的認證,則返回YES。然後,實現connection:didReceiveAuthenticationChallenge:或download:didReceiveAuthenticationChallenge:方法來處理認證。

在認證處理代理方法中,我們需要確認認證保護空間是否有NSURLAuthenticationMethodServerTrust型別的認證,如果有,則從保護空間獲取serverTrust資訊。

相關文章