SSL詳解(個人理解)

howsslwork_clip_image002.gif

一次完整的https請求大概是這樣的。
1.客戶端服務器發出https,請求。
2.服務器發送自己信息到客戶端,包括服務器端公鑰(.car證書文件) 等信息。
3.客戶端根據服務器發回來的 公鑰 去自己鑰匙串里面匹配本地證書(公鑰),來判斷證書是否過期,可用等。如果本地證書沒有的話。就會彈出警告改證書不被信任,詢問用戶是否要安裝這個證書。一旦安裝,下次訪問的時候再去自己的鑰匙串里面去找的時候就能找到了。
4.客戶單服務器的證書加密隨機值發給服務器(這個隨機值在TCP協議里面會自動幫我加。OSX內核部分,我們都無法接觸到。這個值我們改不了)。
5.服務器用自己的 私鑰 解密發回來,客戶端校驗這個隨機值是否正確。雙方信任對方。

以上就是一次https握手過程。完成握手以后,就用公鑰加密傳輸內容了。
舉個例子:

1EA0B000-2937-4201-ABEE-D97CBE362D6A.jpg

查看百度的https證書。蘋果一般會自動更新自己 鑰匙串 里面的權威機構的證書。

QQ20170208-114019@2x.png

以上是蘋果鑰匙串里已經安裝的證書。

QQ20170208-114134@2x.png
QQ20170208-114222@2x.png

以上是百度的買的證書服務商。查了一下是比利時的。

這里說一下,我們開發中用到https。(ios9開始默認開始https)
一般只要你買的證書大部分都是直接過。客戶端什么都不用做,只要把http改https就行了。你買的權威機構證書,手機端默認在鑰匙串里面安裝了默認信任機構的證書。(特別權威的https在訪問的網址那里會有綠色符號)。所以網上說了一堆什么什么的,都不用管只要你用權威機構的,客戶端什么都不用做。服務器端我就不知道了。阿里云貌似的就不用。客戶端直接改https就行了。

另外,費事的就是自制的證書了。蘋果鑰匙串里沒有你的證書,不信任你。這時候可能要做一些功夫了。

NSURLConnection的代理方法里面默認的https大概代碼是這樣的:

// Now start the connection
NSURL * httpsURL = [NSURL URLWithString:@"https://www.baidu.com"];
self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];
     
//回調
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //1)獲取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
     
    //2)SecTrustEvaluate對trust進行驗證
    OSStatus status = SecTrustEvaluate(trust, &result);
    if (status == errSecSuccess &&
        (result == kSecTrustResultProceed ||
        result == kSecTrustResultUnspecified)) {
         
        //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續連接
        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
         
    } else {
     
        //5)驗證失敗,取消這次驗證流程
        [challenge.sender cancelAuthenticationChallenge:challenge];
         
  }
}

在一般購買的證書。這個代理根本不用實現。啥都不用做。

自制的證書代碼是這樣的:

//先導入證書
NSString * cerPath = ...; //證書的路徑
NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
self.trustedCertificates = @[CFBridgingRelease(certificate)];
//回調
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //1)獲取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    //注意:這里將之前導入的證書設置成下面驗證的Trust Object的anchor certificate
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);
    //2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設置的證書或者系統默認提供的證書,對trust進行驗證
    OSStatus status = SecTrustEvaluate(trust, &result);
    if (status == errSecSuccess &&
        (result == kSecTrustResultProceed ||
        result == kSecTrustResultUnspecified)) {
         
        //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續連接
        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
         
    } else {
     
        //5)驗證失敗,取消這次驗證流程
        [challenge.sender cancelAuthenticationChallenge:challenge];
         
  }
}

在老版本的ios系統里 可能 不實現這個方法沒問題。在新版本的ios系統里,恐怕不行。

為了做一次實驗。用百度寫了一個小demo.

QQ20170208-114134@2x.png
QQ20170208-122708@2x.png
QQ20170208-122726@2x.png

找到剛剛百度用的證書,把他導出來然后添加項目工程了。用的時候,別用代理里面返回的證書信息。別別去鑰匙串里面查找了,直接用我導的這個證書。

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"11111" ofType:@"cer"]; //證書的路徑
    NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
    SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
    //    self.trustedCertificates = @[CFBridgingRelease(certificate)];
    
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    //注意:這里將之前導入的證書設置成下面驗證的Trust Object的anchor certificate
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)@[CFBridgingRelease(certificate)]);
    //2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設置的證書或者系統默認提供的證書,對trust進行驗證
    NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
    [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
    
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"%@",response);
    
}

最后我們發現我們也能收到response.

QQ20170208-123327@2x.png

所以。本地證書和服務器證書是可以互相校驗的。放哪無所謂。只不過默認去鑰匙串里面找。

QQ20170208-123619@2x.png

Charles這個工具用過的都知道。這里直接叫青花瓷吧。
他是怎么工作的呢。
他就像客戶端服務器之間的一個中間人。我們叫他代理服務器吧。
文章的開始就說了https請求的過程。
青花瓷在第二步開始的時候就把青花瓷自己的 公鑰 返回給客戶端了。
客戶端請求信息作為自己的信息 請求去訪問服務器
重新屢一下:

1.客戶端發送請求到青花瓷
2青花瓷發送客戶端請求到服務器
3.服務器返回 公鑰信息到青花瓷
4.青花瓷把自己的 公鑰 發送給客戶端
5.客戶端認證青花瓷的證書。
6.客戶端青花瓷公鑰 加密隨機值發給青花瓷
7.青花瓷服務器公鑰加密隨機值發給 服務器
8.服務器用自己的私鑰解密隨機值,然后發給青花瓷
9.青花瓷用自己的私鑰加密隨機值,然后發送給客戶端
10.客戶端對比。完成握手。
11.青花瓷對比。完整握手。
以后。客戶端就用青花瓷公鑰加密數據發給青花瓷青花瓷用自己私鑰解密,展現給我們看。然后用服務器公鑰加密已經解密的數據發給服務器
就這樣,幫我們攔截所有請求。

這也是為什么青花瓷攔截https時客戶端要安裝一份青花瓷的公鑰證書,這樣在第5步時候,客戶端鑰匙串找證書的時候正好能找到。

目前大部分app的https都能抓到數據。
但是某些apps 用自己的一套加密加密,然后用https加密傳傳輸。所以有時候,我們在青花瓷看到也是加密數據。

有圖有證據:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    //1)獲取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    
    SecKeyRef  xxx =SecTrustCopyPublicKey(trust);
    
    SecCertificateRef  pppp = SecTrustGetCertificateAtIndex(trust,0);
    
    //2)SecTrustEvaluate對trust進行驗證
}

未用青花瓷時:證書信息

QQ20170208-130351@2x.png

青花瓷時:證書信息

QQ20170208-130513@2x.png

兩個證書明顯不一樣了。

木有木發現appstore有部分請求,用青花瓷攔截不到。
因為在第5步的時候,蘋果沒有根據服務器返回的證書來加密數據。而是直接用自己已知公鑰證書加密返回,這樣即使你鑰匙串里面安裝的別人的證書的時候也沒有用了。

代碼類似上面“用百度寫了一個小demo”那里的代碼:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"11111" ofType:@"cer"]; //證書的路徑
    NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
    SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
    //    self.trustedCertificates = @[CFBridgingRelease(certificate)];
    
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    //注意:這里將之前導入的證書設置成下面驗證的Trust Object的anchor certificate
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)@[CFBridgingRelease(certificate)]);
    //2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設置的證書或者系統默認提供的證書,對trust進行驗證
    NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
    [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
 
}

不去判斷公鑰證書是否安裝、是否過期,也不去查找。直接用我知道的公鑰證書去加密。這樣就可以防止青花瓷做中間人攔截數據。(不查找,直接用已知的公鑰證書和發過來的公鑰證書對比信息,用服務器發過來的公鑰信息查找證書直接判斷過期、可用。還是有風險的)
這是青花瓷攔截不到了

QQ20170208-132115@2x.png

猜測蘋果可能在這里做了判斷。

保險的做法用了https之外,吧數據用自己的一套加密機制在加密一遍。

幾個測試例子:
https://pan.baidu.com/s/1i5cB6Yh
用到某個測試就有個注釋去掉就行了

參考文章:
http://www.cocoachina.com/ios/20150811/12947.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容