HTTPS從最終的數(shù)據(jù)解析的角度,與HTTP相同。HTTPS將HTTP協(xié)議數(shù)據(jù)包放到SSL/TSL層加密后,在TCP/IP層組成IP數(shù)據(jù)報(bào)去傳輸,以此保證傳輸數(shù)據(jù)的安全;而對于接收端,在SSL/TSL將接收的數(shù)據(jù)包解密之后,將數(shù)據(jù)傳給HTTP協(xié)議層什湘。
SSL/TSL包括四次握手,主要交換三個(gè)信息:
1.數(shù)字證書;
2.三個(gè)隨機(jī)數(shù);
3.加密通訊協(xié)議;
其通訊過程如下示意圖:
數(shù)字證書
該證書一般是由服務(wù)器發(fā)送給客戶端,接收方通過驗(yàn)證這個(gè)證書是不是由信賴的CA簽發(fā),或者與本地的證書相對比,來判斷證書是否可信;假如需要雙向驗(yàn)證,則服務(wù)器和客戶端都需要發(fā)送數(shù)字證書給對方驗(yàn)證。
數(shù)字證書的生成是分層級的,從葉節(jié)點(diǎn)證書往根證書層層驗(yàn)證(有效期,簽名等等),遇到根證書時(shí),發(fā)現(xiàn)作為可信錨點(diǎn)的它存在于可信證書列表中,那么通過驗(yàn)證就通過脯颜。例如在鑰匙串中可以看到某券商公司申請的證書沒有在系統(tǒng)可信任的證書列表中伐脖。
下面以某浪sdk里的證書為代表簡單說明一下數(shù)字證書的內(nèi)容:
證書字段 | 證書說明 |
---|---|
簽發(fā)者名稱 | 發(fā)布并簽署該證書的實(shí)體信息 |
序列號 | 數(shù)字證書機(jī)構(gòu)(Certificate Authority,CA)給證書的唯一整數(shù),一個(gè)數(shù)字證書,一個(gè)序列號 |
主題名稱 | 用于識別該數(shù)字證書的信息 |
公共密鑰信息 | 可公開的密鑰信息 |
簽名 | 通過簽名算法計(jì)算證書內(nèi)容后得到的數(shù)據(jù),用于驗(yàn)證證書是否被篡改 |
三個(gè)隨機(jī)數(shù)
這三個(gè)隨機(jī)數(shù)構(gòu)成了后續(xù)通信中用來對數(shù)據(jù)進(jìn)行對稱加密解密的"對話密鑰"。首先客戶端先發(fā)第一個(gè)隨機(jī)數(shù)n1,然后服務(wù)器返回第二個(gè)隨機(jī)數(shù)n2(這個(gè)過程同時(shí)把數(shù)字證書發(fā)給客戶端),這兩個(gè)隨機(jī)數(shù)都是用明文;而第三個(gè)隨機(jī)數(shù)n3,客戶端用數(shù)字證書的公鑰進(jìn)行非對稱性加密,發(fā)給服務(wù)器;而服務(wù)器用只有自己知道的私鑰來解密,獲取第三個(gè)隨機(jī)數(shù)蠕啄。服務(wù)端和客戶端都有了三個(gè)隨機(jī)數(shù)n1+n2+n3后,兩端就使用這三個(gè)隨機(jī)數(shù)來生成"對話密鑰",在此之后的通信就使用這個(gè)"對話密鑰"來進(jìn)行對稱加解密歼跟。
加密通信協(xié)議
雙方商量具體使用哪一種加密方式,假如兩者支持的加密方式不匹配,則無法進(jìn)行通信格遭。因?yàn)檫@個(gè)過程中,服務(wù)端的私鑰只用來解密第三個(gè)隨機(jī)數(shù),從來沒有在網(wǎng)絡(luò)中傳輸過,只要私鑰沒有被泄露,那么數(shù)據(jù)就是安全的拒迅。
下面主要介紹一下NSURLConnection支持HTTPS的實(shí)現(xiàn)設(shè)置證書作為可信的錨點(diǎn)代碼應(yīng)用。
// SDWebImageDownloaderOperation
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
//通過 challenge.protectionSpace.authenticationMethod 取得保護(hù)空間要求我們認(rèn)證的方式
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates) &&
[challenge.sender respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)]) {
[challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
} else {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
} else {
if ([challenge previousFailureCount] == 0) {
if (self.credential) {
[[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
}
StockPromotionViewController在webview中使用
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString* scheme = [[request URL] scheme];
//判斷是不是https
if ([scheme isEqualToString:@"https"])
{
if (self.authed)
{
return YES;
}
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:request.URL] delegate:self];
[conn start];
[webView stopLoading];
return NO;
}
return YES;
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount]== 0)
{
self.authed = YES;
//NSURLCredential 這個(gè)類是表示身份驗(yàn)證憑據(jù)不可變對象。憑證的實(shí)際類型聲明的類的構(gòu)造函數(shù)來確定胞得〗捉#回調(diào)中會收到一個(gè)challenge
NSURLCredential* cre = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:cre forAuthenticationChallenge:challenge];
}
}
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{
return request;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.authed = YES;
//webview 重新加載請求嗤详。
[self.webView loadRequest:[NSURLRequest requestWithURL:connection.currentRequest.URL]];
[connection cancel];
}