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

來源:互聯網
上載者:User

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、Firefox瀏覽器,Firefox沒辦法直接將來自自簽名CA頒發的server.cer密鑰憑證匯入到認證機構中,因為Firefox會檢查你的伺服器密鑰憑證server.cer的最高一級頒發者是不是權威機構(權威第三方CA),不是的話,不會通過的,比如你在Firefox選項——進階——認證——查看認證——伺服器/認證機構:匯入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雙向認證隨筆稍後發布

 

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

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.