IOS Https適配摸索


https封面

在WWDC 2016開發者大會上,蘋果宣佈了一個最後期限:到2017年1月1日 App Store中的所有應用都必須啓用 App Transport Security安全功能。也就是說,自2017年起,網絡請求必須由http改成https。正因爲這個原因,我也打算嘗試下適配Https網絡請求。由於先前沒有配置服務器經驗,網上說的也很不詳細,因此踩了不少坑,足足花了一天的時間。現在我把我配置的流程寫下來,希望後來人能少走點彎路。(網上教程有,但是寫得很亂,難理解,這裏我打算用最通俗的語言來描述。)

概念誤區:HTTPS和語言無關


一開始,我以爲https應該像處理http報頭一樣,要寫什麼PHP代碼(服務器語言是PHP),把客戶端傳來的證書經過處理驗證什麼的,然後再返回處理結果(類似於Token驗證)。因此,一開始我就搜PHP怎麼處理HTTPS請求,結果都是介紹怎麼用PHP發送HTTPS請求的。經過一段時間糾結,我才意識到HTTPS的處理並不需要PHP做什麼,你的服務器(比如apache)就已經幫你做好驗證了,你只需要像接收http請求一樣處理數據就可以了,也就是說,服務器增加HTTPS並不需要在代碼中做什麼,只要服務器配置下就好

關於HTTPS握手流程我覺得還是應該瞭解下,可以參考這份資料:https握手流程

簡單得說就是客戶端向服務器發起需求,服務器把證書發給客戶端,客戶端驗證下證書是否合法,然後用證書的數據加密傳輸數據給服務器,服務器解密

生成證書文件


看了上面的原理就知道,要HTTPS傳輸首先得有證書。在生成證書這方面我也遇到了很大的坑,幾乎所有的網站都要生成2個證書,server.pemclient.pem。一開始我把server.pem配置到服務器上,把client.pem給AFNetwoking,結果怎麼都通過不了驗證!後來我發現只要AFNetworing使用server.pem驗證就可以了,也就是說只要一份證書就行了。。。(真的不知道爲什麼要2份證書,如有大神歡迎指出)。

下面我將命令行代碼貼出來,主要參考這篇文章:參考文章

//第一步,爲服務器端和客戶端準備公鑰、私鑰
# 生成服務器端私鑰
openssl genrsa -out server.key 1024
# 生成服務器端公鑰
openssl rsa -in server.key -pubout -out server.pem

//第二步,生成 CA 證書
# 生成 CA 私鑰
openssl genrsa -out ca.key 1024
# X.509 Certificate Signing Request (CSR) Management.
openssl req -new -key ca.key -out ca.csr
# X.509 Certificate Data Management.
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt

在第二步時會出來一個填寫資料的界面(我已經填好大家可以參考,有些地方可以空着)

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Zhejiang
Locality Name (eg, city) []:Hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My CA
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

這裏有點要注意, Common Name (e.g. server FQDN or YOUR name) []: 這一項,是最後可以訪問的域名,我這裏爲了方便測試,寫成 localhost ,如果是爲了給網站生成證書,需要寫成 xxxx.com 。

//第三步,生成服務器端證書
# 服務器端需要向 CA 機構申請簽名證書,在申請簽名證書之前依然是創建自己的 CSR 文件
openssl req -new -key server.key -out server.csr
# 向自己的 CA 機構申請證書,簽名過程需要 CA 的證書和私鑰參與,最終頒發一個帶有 CA 簽名的證書
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.cr

同樣會有信息填寫,照舊寫就好了。

第四步,生成cer文件
使用openssl 進行轉換
openssl x509 -in server.crt -out server.cer -outform der

如果完成,就會得到這麼多文件


證書文件

配置服務器


爲了方便,我是以mac本地電腦做服務器,使用的是XAMPP搭建的服務器使用的是apache。在其他服務器上應該就是文件路徑位置不一樣,其他應該是一樣的。如果有些服務器沒開啓ssl,可以網上搜索怎麼開啓。

修改httpd-ssl.conf文件 把server.crt和server.key的路徑修改對就好了
SSLCertificateFile /apache/conf/server.crt  
SSLCertificateKeyFile /apache/conf/server.key

由於我的服務器默認開啓ssl,因此我就修改下證書路徑就好了。
我們來瀏覽器訪問下


瀏覽器訪問https

按我標出的框來訪問證書,就可以看見我們剛纔自己填的數據


服務器返回的證書


因爲不同服務器配置不同,不能一概而論,所以大家還是根據自己服務器的情況再配置。

配置AFNetworking


這裏直接上代碼

+ (AFSecurityPolicy*)customSecurityPolicy
{
    // /先導入證書
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];//證書的路徑
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

    // AFSSLPinningModeCertificate 使用證書驗證模式
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

    // allowInvalidCertificates 是否允許無效證書(也就是自建的證書),默認爲NO
    // 如果是需要驗證自建證書,需要設置爲YES
    securityPolicy.allowInvalidCertificates = YES;

    //validatesDomainName 是否需要驗證域名,默認爲YES;
    //假如證書的域名與你請求的域名不一致,需把該項設置爲NO;如設成NO的話,即服務器使用其他可信任機構頒發的證書,也可以建立連接,這個非常危險,建議打開。
    //置爲NO,主要用於這種情況:客戶端請求的是子域名,而證書上的是另外一個域名。因爲SSL證書上的域名是獨立的,假如證書上註冊的域名是www.google.com,那麼mail.google.com是無法驗證通過的;當然,有錢可以註冊通配符的域名*.google.com,但這個還是比較貴的。
   //如置爲NO,建議自己添加對應域名的校驗邏輯。
    securityPolicy.validatesDomainName = NO;

    securityPolicy.pinnedCertificates = @[certData];

    return securityPolicy;
}

+ (void)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id))success failure:(void (^)(NSError *))failure
{
    // 1.獲得請求管理者
    AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
    // 2.申明返回的結果是text/html類型
    mgr.responseSerializer = [AFHTTPResponseSerializer serializer];

    // 加上這行代碼,https ssl 驗證。
    [mgr setSecurityPolicy:[NetworkHelpManager customSecurityPolicy]];

    // 3.發送POST請求
    [mgr POST:url parameters:params
  success:^(NSURLSessionDataTask *operation, id responseObj) {
          if (success) {
            success(responseObj);
         }
      } failure:^(NSURLSessionDataTask *operation, NSError *error) {
          if (failure) {
              failure(error);
          }
      }];
}

還要記得把證書添加到項目中來哦


項目中的證書

下面要介紹下證書的驗證模式 AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

  • AFSSLPinningModeNone

不做任何驗證,只要服務器返回了證書就通過

  • AFSSLPinningModePublicKey

只驗證公鑰部分,只要公鑰部分一致就驗證通過,如圖所示,紅色框起來的部分只要一致就通過


客戶端和服務器端證書對比
  • AFSSLPinningModeCertificate

除了公鑰外,其他能容也要一致才能通過驗證。

配置結果


下面我們用Charles抓包看看是否成功加密了。


抓包結果

返回的數據依稀能看出我們證書裏的內容,但是數據已經加密了。

總結


配置https其實並不難,既然遲早要更新還不如早一點配置好。其實很多時候都是我們不願意做,而不是不能做。多一點耐心,多一點實踐,就能多一點突破。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章