Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Tomcat單向Https驗證搭建,並親自實現與主流浏覽器、Android/iOS客戶端安全通信,androidios

Tomcat單向Https驗證搭建,並親自實現與主流浏覽器、Android/iOS客戶端安全通信,androidios

編輯:關於android開發

Tomcat單向Https驗證搭建,並親自實現與主流浏覽器、Android/iOS客戶端安全通信,androidios


眾所周知,iOS9已經開始在聯網方面默認強制使用Https替換原來的Http請求了,雖然Http和Https各有各的優勢,但是總得來說,到了現在這個安全的信息時代,開發者已經離不開Https了。

網上有很多搭建Https的教程,但是比較零散,Web浏覽器端和移動端具體部署也不是特別明確,如果真的用於項目中,還需要折騰一番,本人直接來個項目級別的Demo。

 

在開始之前,我總結一下keytool這個證書工具需要處理的幾種常見後綴格式的意義:

jsk/keystore, 表示一個密鑰庫,裡面可以包含多個密鑰條目(證書),密鑰條目(證書)還可以分私有的和信任的等,私有的一般包括私鑰、公鑰和密鑰條目信息,信任的一般包括公鑰和密鑰條目信息(公鑰證書)。打開密鑰庫需要一個密碼,同時打開每個私有密鑰條目也需要一個密碼(但一般建議將打開私有密鑰條目的密碼設置跟打開密鑰庫密碼相同,省的弄亂了,以下我的Demo演示是設置相同的),做過給安卓apk簽名打包的一定能體會到這個。

csr/certreq,證書請求文件,你把這個提交給CA,CA會給你頒發cer格式的含有公鑰和密鑰條目信息的證書(公鑰證書)給你。

cer,用於存儲某個密鑰條目(證書)的公鑰文件,一般你提交了csr給CA後,CA會頒發給你,你也可以通過自簽名的CA頒發,如果你已經有密鑰條目(證書)在密鑰庫裡,也可以從jsk/keystore中的某個密鑰條目(證書)導出其公鑰和密鑰條目內容的證書(公鑰證書)。

綜上,其實最簡單的理解就是密鑰庫就相當於SQL數據庫,各種密鑰條目(證書)就相當於SQL數據庫表 ,一個SQL數據庫表其實跟其它的表又有父子(外鍵)關系的,這種關系叫做密鑰條目(證書)的密鑰鏈。為了描述更加方便,以下將《密鑰庫》描述詞叫做《證書庫》,《密鑰條目》描述詞叫做《證書》,cer格式的公鑰和密鑰條目內容的證書叫做《公鑰證書》。

 

接下來開始演示Demo示例:

1、生成服務器端證書庫和證書:(生成服務器端證書庫和證書可以有多種方式,推薦通過走第三方CA方式,這樣生成的證書以後更具有保障性和安全性(尤其是對web客戶端,可以啟動“綠色地址欄/安全鎖 地址欄顯示單位名稱 EV國際認證標識”等等))

1-1-1、方式一、使用keytool,生成自簽名的CA證書和自簽名的server證書(下面生成的CA是自簽名的,當然下面生成的server也是自簽名的,這些證書在浏覽器上使用絕對不會出現綠條):

1.生成自簽名CA:keytool -genkey -v -alias ca -keyalg RSA -keystore D:\ca_cert_lib.jks -validity 3650
2.生成服務器證書:keytool -genkey -v -alias server -keyalg RSA -keystore D:\server_cert_lib.jks -validity 365

注意證書名叫ca定義為自簽名的CA證書,證書名叫server定義為服務器證書,它們分別保存在證書庫路徑為 D:\ca_cert_lib.jksD:\server_cert_lib.jks 中
之所以要分自簽名的CA證書server服務器證書,是因為正常情況下我們的server服務器證書是需要向第三方CA申請的,第三方CA會用它的根證書給你生成一份公鑰證書(這個過程叫做第三方CA給你簽名),而此處就是要自導自演展示自簽名的CA給server證書簽名這個過程

    

1-1-2、用自簽名的CA給server簽上CA的簽名(server本身也是自簽名的,下面要做的相當於將server的自簽名換成CA的簽名,也許你會問CA的簽名是誰的,CA也可以是別人的,比如如果沃通願意給你的CA簽名的話,那麼CA的頒發者就是沃通,我這裡的Demo演示沒有權威機構給它簽名,所以我這個CA就是自己給自己簽名的,這個CA其實就是ROOT證書,只不過不會被任何客戶端信任(如:浏覽器等)而已,即用我這個CA簽發的所有server服務器證書在任何浏覽器上絕對不會出現綠條):

在給server簽名之前,查看一下當前證書庫情況,它們的確都是各自給自己簽名的:
keytool -list -v -keystore D:\ca_cert_lib.jks
keytool -list -v -keystore D:\server_cert_lib.jks

現在使用自簽名CA給server簽名(如果你要沃通CA給你server簽名,就把下面的csr交給沃通):
1.生成server的證書請求文件:keytool -certreq -alias server -keystore D:\server_cert_lib.jks > D:\server.csr (linux上:keytool -certreq -alias server -keystore <路徑>/server_cert_lib.jks | tee <路徑>/server.csr)
2.使用自簽名的CA對server的證書請求文件進行簽名頒發服務器server.cer公鑰證書:keytool -gencert -alias ca -keystore D:\ca_cert_lib.jks -infile D:\server.csr -outfile D:\server.cer
3.生成自簽名CA的公鑰文件:keytool -export -alias ca -keystore D:\ca_cert_lib.jks -rfc -file D:\ca.cer

此時可以先查看以下ca.cer和server.cer公鑰證書具體內容(注意ca.cer是自簽名CA的公鑰文件,其頒發者還是它自己,而server.cer是server服務器的公鑰文件,其頒發者是自簽名的CA,兩者是有本質區別的,下面安裝回復後可以看到這個區別),不過其實他們都是個Base64過的字符串:
keytool -printcert -rfc -file D:\ca.cer
keytool -printcert -rfc -file D:\server.cer

安裝證書回復(回復這個翻譯也許不太好,反正這個意思就是:將CA頒發的cer公鑰證書安裝到server服務器端證書庫,前提條件是CA的cer公鑰證書也需要先被安裝):
1.先安裝CA的公鑰證書(這步不可以少,否則下面的證書回復沒安裝):keytool -importcert -alias ca -keystore D:\server_cert_lib.jks -file D:\ca.cer
2.安裝server的公鑰證書(安裝證書回復(被CA簽名過的)):keytool -importcert -alias server -keystore D:\server_cert_lib.jks -file D:\server.cer

此時再查看下服務器server證書:keytool -list -v -keystore D:\server_cert_lib.jks -alias server

這時發現這個server證書變化挺大的,一是證書連變長了,變成2了,這個server證書附帶了上一級證書SELF CA ROOT CERT的信息,其次是server的發布者變成了SELF CA ROOT CERT,這也就是說明成功的使用自簽名的CA給server簽名成功了

         

 

1-2-1、通過權威CA(第三方SSL證書機構)生成,如通過沃通生成免費/收費服務器端密鑰庫和證書,CA生成的證書更具有保障性,最直觀的表現是客戶端用Web浏覽器訪問該Https網站時會有綠色標識(當然要顯示越華麗就得給權威CA交更多的錢)如github:,以下演示使用沃通申請免費的DV SSL證書。

1-2-1、登陸沃通,申請一個免費的DV SSL證書。

1-2-2、申請需要先綁定域名

1-2-3、申請完後需要驗證域名,驗證域名這個事就自己去搞定吧

1-2-4、上面用自簽名的CA給server證書簽名已經提到了如何生成csr文件,此處通過提交證書申請文件csr申請的步驟略。以下演示在線生成,即本次講的通過沃通CA自動生成公鑰證書,順便把server服務器證書庫也一並生成好並將公鑰證書導入到這個證書庫,此處輸入的密碼實際上既是server服務器證書庫密碼也是server服務器證書(此種方式生成的證書名字叫做1,這個1對應上面自簽名CA導入的server證書)的密碼

輸入密碼生成證書之後就可以下載沃通CA頒布給你server服務器端用的證書庫和證書了,然後部署到對應的服務器程序中,本案例部署到tomcat,為了保持統一性和直觀性此處將沃通CA頒發的證書庫名andy5.me.jks改名為server_cert_lib.jks

  

一般通過此時生成的證書名字(alias)叫做:1,對應自簽名CA方式中的server證書

 因此你拿到了上面的沃通頒發的證書後,你還可以繼續頒發給別人,這些你頒發的證書都是可信任的,因為沃通上面的根證書一定是可信任的,不然沃通本身就是不可以信任的。

 

2、通過上面的步驟,已經得到了含有CA頒發的證書的證書庫server_cert_lib.jks了,接下來,給服務器程序tomcat配置證書庫(可以理解為給服務器端安裝server密鑰庫)

2-1、在tomcat的安裝目錄/conf/server.xml中配置和啟用以下port為8443的Connector

    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
               keystoreFile="D:\\server_cert_lib.jks" keystorePass="s123456"
               />

 

3、接下來,要操作客戶端了(這裡的客戶端包括多種:1是主流浏覽器、2是Android、3是iOS)

3-1、先從jks格式的服務器端證書庫server_cert_lib.jks導出cer格式的服務器端公鑰證書server.cer。

keytool -keystore D:\server_cert_lib.jks -export -alias server -file D:\server.cer

3-2、將server.cer服務器端公鑰證書導入到客戶端信任證書庫(這個實現不能用keytool導入了,而是要根據具體的各個客戶端平台進行實現,此步驟可以理解為給客戶端安裝服務器端公鑰證書

3-2-1、Web浏覽器實現:使用浏覽器安裝服務器端server.cer公鑰證書

3-2-1-1、IE浏覽器,直接雙擊server.cer,然後在證書存儲——將所有證書放入下列存儲,選擇受信任的根證書頒發機構

   

3-2-1-2、火狐浏覽器,火狐沒辦法直接將來自自簽名CA頒發的server.cer公鑰證書導入到證書機構中,因為火狐會檢查你的服務器公鑰證書server.cer的最高一級頒發者是不是權威機構(權威第三方CA),不是的話,不會通過的,比如你在火狐選項——高級——證書——查看證書——服務器/證書機構:導入server.cer,會提示無法導入,因此解決方法是在不用導入server.cer,而是在服務器選項卡中添加列外地址,如果來自權威CA頒發的server.cer,則可以直接導入。

      

 

3-2-2、Android客戶端實現

3-2-2-1、將服務器端公鑰證書server.cer放入安卓assert目錄,然後使用HttpsUtil.getSslSocketFactory進行初始化(該工具類的方法具體實現見後面的Demo代碼)

    private void initHttpsEngine(boolean isSelfCa) {
        try {
            // 初始化服務器端公鑰證書,得到SSLSocketFactory
            SSLSocketFactory sslSocketFactory = HttpsUtil.getSslSocketFactory(new InputStream[]{getAssets().open
                    ("server.cer")});
            OkHttpClient.Builder builder = new OkHttpClient.Builder();

            if (isSelfCa) {
                /**
                 * 注意;如果你的server.cer是來自自簽名CA頒發的,那麼就要設置下面的customVerifier,主要是為了解決報以下異常,
                 * 即跳過Hostname www.andy5.me在CA上的驗證,如果你的server.cer是來自第三方SSL權威機構頒發的,不用設置這個customVerifier
                 *
                 * javax.net.ssl.SSLPeerUnverifiedException: Hostname www.andy5.me not verified:
                 * certificate: sha1/EnrjjhNxjvuDkO/rJqPmJ9XaIMs=
                 * DN: CN=Andy Wu(www.andy5.me),OU=Andy5 Server,O=www.andy5.me,L=Guangzhou,ST=Guangdong,C=CN
                 * subjectAltNames: []
                 */
                HostnameVerifier customVerifier = new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        // 指定SERVER_URL一定可以通過
                        if (SERVER_URL.equalsIgnoreCase(hostname)) {
                            return true;
                        } else {
                            // 使用默認的OkHostnameVerifier進行驗證
                            return OkHostnameVerifier.INSTANCE.verify(hostname, session);
                        }
                    }
                };
                mOkHttpClient = builder.sslSocketFactory(sslSocketFactory).connectTimeout(30, TimeUnit.SECONDS)
                        .hostnameVerifier(customVerifier).build();
            } else {
                mOkHttpClient = builder.sslSocketFactory(sslSocketFactory).connectTimeout(30, TimeUnit.SECONDS).build();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3-2-2-2、可以在任何地方對該服務器發起Https請求了,如果是自簽名CA簽發的服務器端server證書,需要忽略域名驗證才能正常通信(具體看Demo代碼),顯然也是不安全的。

    public void testHttps(View v) {

        if (mOkHttpClient == null) {
            Toast.makeText(getApplicationContext(), "請先初始化客戶端密鑰庫和服務器端公鑰!", Toast.LENGTH_SHORT).show();
            return;
        }

        mTvResult.setText("正在從 " + HTTPS_SERVER_URL + " 獲取數據....");
        mWvResult.loadData("", "text/html", "UTF-8");

        Request request = new Request.Builder().url(HTTPS_SERVER_URL).build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                e.printStackTrace();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mTvResult.setText("從 " + HTTPS_SERVER_URL + " 獲取數據失敗!\n" + e);
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String html = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mTvResult.setText("從 " + HTTPS_SERVER_URL + " 獲取數據成功!");
                        mWvResult.loadData(html, "text/html", "UTF-8");
                    }
                });
            }
        });
    }

3-2-2-3、成功請求界面如下,軟件環境:MIUI 6(Android 4.4.2) + AS 1.5.1

 

3-2-3、iOS客戶端實現

3-2-3-1、將服務器端公鑰證書server.cer放入根目錄,然後使用HttpsUtil.configSecurityPolicy配置AFNetworking安全選項(該工具類的方法具體實現見後面的Demo代碼)

- (IBAction)testHttps:(UIButton *)sender {
    
    AFHTTPSessionManager *manager =[AFHTTPSessionManager manager];
    
    NSArray *serverCersNames = [[NSArray alloc] initWithObjects:@"server.cer", nil];
    [HttpsUtil configSecurityPolicy:manager.securityPolicy serverCers:serverCersNames];
    manager.requestSerializer.timeoutInterval = 30.0f;
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    
    [_tvResult setText:[NSString stringWithFormat:@"正在從%@獲取數據....",HTTPS_SERVER_URL]];
    [_wvResult loadHTMLString:@"" baseURL:nil];
    
    [manager GET:HTTPS_SERVER_URL parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {
        //
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSString *result = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
        NSLog(@"獲取數據成功\n%@",result);
        [_tvResult setText:[NSString stringWithFormat:@"從%@獲取數據成功!",HTTPS_SERVER_URL]];
        [_wvResult loadHTMLString:result baseURL:nil];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"獲取數據失敗\n%@",error);
        [_tvResult setText:[NSString stringWithFormat:@"從%@獲取數據失敗!\n%@",HTTPS_SERVER_URL,error]];
    }];
}

3-2-3-2、成功請求界面如下,軟件環境:iOS 9.2.1 + Xcode 7.2.1

 

 

4、此時,客戶端(web浏覽器、Android、iOS)就可以向服務器端發起Https請求了,詳細效果見Demo(為了兼容演示自簽名CA,Demo使用的是自簽名服務器證書)。

 

5、額外補充說明:

1、關於自己內網測試問題,我所有使用的www.andy5.me這個域名是可以改成IP的,一樣可以測試成功,當然我測試的時候是用nat123做了映射,主要是為了更接近真實環境。

 

參考:

http://blog.csdn.net/lmj623565791/article/details/48129405

http://callistaenterprise.se/blogg/teknik/2011/11/24/creating-self-signed-certificates-for-use-on-android

 

單向Https驗證Demo相關文件.7z

 

Https雙向認證隨筆稍後發布

 

原創隨筆,轉載注明出處。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved