Android 安全加密:Https編程詳解
Android安全加密專題文章索引
以上學(xué)習(xí)所有內(nèi)容,對(duì)稱加密、非對(duì)稱加密、消息摘要、數(shù)字簽名等知識(shí)都是為了理解數(shù)字證書工作原理而作為一個(gè)預(yù)備知識(shí)。數(shù)字證書是密碼學(xué)里的終極武器,是人類幾千年歷史總結(jié)的智慧的結(jié)晶,只有在明白了數(shù)字證書工作原理后,才能理解Https 協(xié)議的安全通訊機(jī)制。最終才能在SSL 開發(fā)過程中得心應(yīng)手。
另外,對(duì)稱加密和消息摘要這兩個(gè)知識(shí)點(diǎn)是可以單獨(dú)拿來使用的。
知識(shí)點(diǎn)串聯(lián):
數(shù)字證書使用到了以上學(xué)習(xí)的所有知識(shí)
- 對(duì)稱加密與非對(duì)稱加密結(jié)合使用實(shí)現(xiàn)了秘鑰交換,之后通信雙方使用該秘鑰進(jìn)行對(duì)稱加密通信。
- 消息摘要與非對(duì)稱加密實(shí)現(xiàn)了數(shù)字簽名,根證書機(jī)構(gòu)對(duì)目標(biāo)證書進(jìn)行簽名,在校驗(yàn)的時(shí)候,根證書用公鑰對(duì)其進(jìn)行校驗(yàn)。若校驗(yàn)成功,則說明該證書是受信任的。
- Keytool 工具可以創(chuàng)建證書,之后交給根證書機(jī)構(gòu)認(rèn)證后直接使用自簽名證書,還可以輸出證書的RFC格式信息等。
- 數(shù)字簽名技術(shù)實(shí)現(xiàn)了身份認(rèn)證與數(shù)據(jù)完整性保證。
- 加密技術(shù)保證了數(shù)據(jù)的保密性,消息摘要算法保證了數(shù)據(jù)的完整性,對(duì)稱加密的高效保證了數(shù)據(jù)處理的可靠性,數(shù)字簽名技術(shù)保證了操作的不可否認(rèn)性。
通過以上內(nèi)容的學(xué)習(xí),我們要能掌握以下知識(shí)點(diǎn):
- 基礎(chǔ)知識(shí):bit 位、字節(jié)、字符、字符編碼、進(jìn)制轉(zhuǎn)換、io
- 知道怎樣在實(shí)際開發(fā)里怎樣使用對(duì)稱加密解決問題
- 知道對(duì)稱加密、非對(duì)稱加密、消息摘要、數(shù)字簽名、數(shù)字證書是為了解決什么問題而出現(xiàn)的
- 了解SSL 通訊流程
- 實(shí)際開發(fā)里怎樣請(qǐng)求Https 的接口
概述
SSL(Secure Sockets Layer 安全套接層),為網(wǎng)景公司(Netscape)所研發(fā),用以保障在Internet 上數(shù)據(jù)傳輸之安全,利用數(shù)據(jù)加密(Encryption)技術(shù),可確保數(shù)據(jù)在網(wǎng)絡(luò)上之傳輸過程中不會(huì)被截取及竊聽。一般通用之規(guī)格為40 bit 之安全標(biāo)準(zhǔn),美國(guó)則已推出128 bit 之更高安全標(biāo)準(zhǔn),但限制出境。只要3.0 版本以上之I.E.或Netscape 瀏覽器即可支持SSL。
TLS(Transport Layer Security 傳輸層安全),用于在兩個(gè)通信應(yīng)用程序之間提供保密性和數(shù)據(jù)完整性。TLS 是SSL 的標(biāo)準(zhǔn)化后的產(chǎn)物,有1.0 ,1.1 ,1.2 三個(gè)版本,默認(rèn)使用1.0。TLS1.0 和SSL3.0 幾乎沒
有區(qū)別,事實(shí)上我們現(xiàn)在用的都是TLS,但因?yàn)闅v史上習(xí)慣了SSL 這個(gè)稱呼。
SSL 通信簡(jiǎn)單圖示:
SSL 通信詳細(xì)圖示:
當(dāng)請(qǐng)求使用自簽名證書的網(wǎng)站數(shù)據(jù)時(shí),例如請(qǐng)求12306 的客運(yùn)服務(wù)頁面:https://kyfw.12306.cn/otn/,則會(huì)報(bào)下面的錯(cuò)誤,原因是客戶端的根認(rèn)證機(jī)構(gòu)不能識(shí)別該證書錯(cuò)誤信息:unable to find valid certification path to requested target
解決方案1
一個(gè)證書可不可信,是由TrustManager 決定的,所以我們只需要自定義一個(gè)什么都不做的TrustManager即可,服務(wù)器出示的所有證書都不做校驗(yàn),一律放行。
public static void main(String[] args) throws Exception { //協(xié)議傳輸層安全TLS(transport layer secure) SSLContext sslContext = SSLContext.getInstance("TLS"); //創(chuàng)建信任管理器(TrustManager 負(fù)責(zé)校驗(yàn)證書是否可信) TrustManager[] tm = new TrustManager[]{new EmptyX509TrustManager()}; //使用自定義的信任管理器初始化SSL 上下文對(duì)象 sslContext.init(null, tm, null); //設(shè)置全局的SSLSocketFactory 工廠(對(duì)所有ssl 鏈接都產(chǎn)生影響) HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); //URL url = new URL("https://www.baidu.com"); URL url = new URL("https://kyfw.12306.cn/otn/"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); InputStream in = conn.getInputStream(); System.out.println(Util.inputstream2String(in)); } /** * 自定義一個(gè)什么都不做的信任管理器,所有證書都不做校驗(yàn),一律放行 */ private static class EmptyX509TrustManager implements X509TrustManager{ @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }
解決方案2
12306 服務(wù)器出示的證書是中鐵集團(tuán)SRCA 給他頒發(fā)的,所以SRCA 的證書是能夠識(shí)別12306 的證書的,所以只需要把SRCA 證書導(dǎo)入系統(tǒng)的KeyStore 里,之后交給TrustManagerFactory 進(jìn)行初始化,則可把SRCA 添加至根證書認(rèn)證機(jī)構(gòu),之后校驗(yàn)的時(shí)候,SRCA 對(duì)12306 證書校驗(yàn)時(shí)就能通過認(rèn)證。
這種解決方案有兩種使用方式:一是直接使用SRCA.cer 文件,二是使用改文件的RFC 格式數(shù)據(jù),將其寫在代碼里。
//12306 證書的RFC 格式(注意要記得手動(dòng)添加兩個(gè)換行符) private static final String CERT_12306_RFC = "-----BEGIN CERTIFICATE-----\n"+ "MIICmjCCAgOgAwIBAgIIbyZr5/jKH6QwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ04xKTAn"+ "BgNVBAoTIFNpbm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMB4X"+ "DTA5MDUyNTA2NTYwMFoXDTI5MDUyMDA2NTYwMFowRzELMAkGA1UEBhMCQ04xKTAnBgNVBAoTIFNp"+ "bm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMIGfMA0GCSqGSIb3"+ "DQEBAQUAA4GNADCBiQKBgQDMpbNeb34p0GvLkZ6t72/OOba4mX2K/eZRWFfnuk8e5jKDH+9BgCb2"+ "9bSotqPqTbxXWPxIOz8EjyUO3bfR5pQ8ovNTOlks2rS5BdMhoi4sUjCKi5ELiqtyww/XgY5iFqv6"+ "D4Pw9QvOUcdRVSbPWo1DwMmH75It6pk/rARIFHEjWwIDAQABo4GOMIGLMB8GA1UdIwQYMBaAFHle"+ "tne34lKDQ+3HUYhMY4UsAENYMAwGA1UdEwQFMAMBAf8wLgYDVR0fBCcwJTAjoCGgH4YdaHR0cDov"+ "LzE5Mi4xNjguOS4xNDkvY3JsMS5jcmwwCwYDVR0PBAQDAgH+MB0GA1UdDgQWBBR5XrZ3t+JSg0Pt"+ "x1GITGOFLABDWDANBgkqhkiG9w0BAQUFAAOBgQDGrAm2U/of1LbOnG2bnnQtgcVaBXiVJF8LKPaV"+ "23XQ96HU8xfgSZMJS6U00WHAI7zp0q208RSUft9wDq9ee///VOhzR6Tebg9QfyPSohkBrhXQenvQ"+ "og555S+C3eJAAVeNCTeMS3N/M5hzBRJAoffn3qoYdAO1Q8bTguOi+2849A=="+ "-----END CERTIFICATE-----\n"; public static void main(String[] args) throws Exception { // 使用傳輸層安全協(xié)議TLS(transport layer secure) SSLContext sslContext = SSLContext.getInstance("TLS"); //使用SRCA.cer 文件的形式 //FileInputStream certInputStream = new FileInputStream(new File("srca.cer")); //也可以通過RFC 字符串的形式使用證書 ByteArrayInputStream certInputStream = new ByteArrayInputStream(CERT_12306_RFC.getBytes()); // 初始化keyStore,用來導(dǎo)入證書 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); //參數(shù)null 表示使用系統(tǒng)默認(rèn)keystore,也可使用其他keystore(需事先將srca.cer 證書導(dǎo)入 keystore 里) keyStore.load(null); //通過流創(chuàng)建一個(gè)證書 Certificate certificate = CertificateFactory.getInstance("X.509") .generateCertificate(certInputStream); // 把srca.cer 這個(gè)證書導(dǎo)入到KeyStore 里,別名叫做srca keyStore.setCertificateEntry("srca", certificate); // 設(shè)置使用keyStore 去進(jìn)行證書校驗(yàn) TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); //用我們?cè)O(shè)定好的TrustManager 去做ssl 通信協(xié)議校驗(yàn),即證書校驗(yàn) sslContext.init(null, trustManagerFactory.getTrustManagers(), null); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext .getSocketFactory()); URL url = new URL("https://kyfw.12306.cn/otn/"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); InputStream in = conn.getInputStream(); System.out.println(Util.inputstream2String(in)); }
Android 里的https 請(qǐng)求:
把scra.cer 文件考到assets 或raw 目錄下,或者直接使用證書的RFC 格式,接下來的做法和java工程代碼一樣
//ByteArrayInputStream in = new ByteArrayInputStream("rfc".getBytes()); CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt")); Certificate ca; try { ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); } finally { caInput.close(); } String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); URL url = new URL("https://certs.cac.washington.edu/CAtest/"); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);
雙向證書驗(yàn)證
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory. getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); //初始化keystore KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); clientKeyStore.load(getAssets().open("client.bks"), "123456".toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, "123456".toCharArray()); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
Nogotofail
網(wǎng)絡(luò)流量安全測(cè)試工具,Google的開源項(xiàng)目:https://github.com/google/nogotofail
相關(guān)文章
簡(jiǎn)單實(shí)現(xiàn)Android倒計(jì)時(shí)效果
這篇文章主要教大家如何簡(jiǎn)單的實(shí)現(xiàn)Android倒計(jì)時(shí)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10ViewPager實(shí)現(xiàn)帶引導(dǎo)小圓點(diǎn)與自動(dòng)跳轉(zhuǎn)的引導(dǎo)界面
這篇文章主要為大家詳細(xì)介紹了ViewPager實(shí)現(xiàn)帶引導(dǎo)小圓點(diǎn)與自動(dòng)跳轉(zhuǎn)的引導(dǎo)界面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android ScrollView實(shí)現(xiàn)下拉彈回動(dòng)畫效果
這篇文章主要為大家詳細(xì)介紹了Android ScrollView實(shí)現(xiàn)下拉彈回動(dòng)畫效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08深入學(xué)習(xí)Android?ANR?的原理分析及解決辦法
Android系統(tǒng)中,AMS和WMS會(huì)檢測(cè)App的響應(yīng)時(shí)間,如果App在特定時(shí)間無法相應(yīng)屏幕觸摸或鍵盤輸入時(shí)間,或者特定事件沒有處理完畢,就會(huì)出現(xiàn)ANR。本文將帶領(lǐng)大學(xué)深入學(xué)習(xí)一下ANR的原理及解決辦法,感興趣的同學(xué)可以學(xué)習(xí)一下2021-11-11Android?App跳轉(zhuǎn)微信小程序踩坑實(shí)戰(zhàn)
現(xiàn)在市面上很多的應(yīng)用都可以實(shí)現(xiàn)相互跳轉(zhuǎn),下面這篇文章主要給大家介紹了關(guān)于Android?App跳轉(zhuǎn)微信小程序踩坑的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05Android開發(fā)教程之如何屏蔽View的重復(fù)點(diǎn)擊
這篇文章主要給大家介紹了關(guān)于Android開發(fā)教程之如何屏蔽View的重復(fù)點(diǎn)擊的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09Android動(dòng)態(tài)顯示當(dāng)前年月日時(shí)分秒系統(tǒng)時(shí)間(示例代碼)
這篇文章主要介紹了Android動(dòng)態(tài)顯示當(dāng)前年月日時(shí)分秒系統(tǒng)時(shí)間的示例代碼,需要的朋友可以參考下2017-05-05Android通過Handler與AsyncTask兩種方式動(dòng)態(tài)更新ListView(附源碼)
這篇文章主要介紹了Android通過Handler與AsyncTask兩種方式動(dòng)態(tài)更新ListView的方法,結(jié)合實(shí)例形式分析了ListView動(dòng)態(tài)更新的常用技巧,并附上完整實(shí)例源碼供讀者下載,需要的朋友可以參考下2015-12-12Android開發(fā)之ListView的簡(jiǎn)單用法及定制ListView界面操作示例
這篇文章主要介紹了Android開發(fā)之ListView的簡(jiǎn)單用法及定制ListView界面操作,結(jié)合實(shí)例形式分析了Android ListView界面布局相關(guān)操作技巧,需要的朋友可以參考下2019-04-04