編輯:關於Android編程
最近公司項目用到https的接口形式,對於一般的網絡請求 我們用的是http://****** 使用的是
代碼用來打開一個http 連接.
URLurlConnection=newURL("http://www.codeproject.com/"); HttpURLConnectionurlConnection=(HttpURLConnection)url.openConnection();
不要在 http連接中打開:登陸頁面, 或是傳遞用戶名, 密碼, 銀行卡之類的重要個人資料. 這些重要個人數據應該通過 HTTPS (英文的介紹)傳輸.
HTTPS 其實就是個安全版的http.HTTPS能保證電子商務的交易安全. 如:網上銀行.像 IE 或者火狐浏覽器, 如果出現下面的掛鎖圖標.
同時, 在浏覽器的地址欄中以 https:// 開頭, 這表示, 你的浏覽器跟這個網站的數據往來都是安全的.https 跟 http 的最大區別在於 https 多加了一個保障通訊安全的層.
像下列代碼這樣打開一個 https 連接, 可以保障這個連接的數據通訊安全.
URLurlConnection=newURL("https://www.codeproject.com/"); HttpsURLConnectionurlConnection=(HttpsURLConnection)url.openConnection(); InputStreamin=newBufferedInputStream(urlConnection.getInputStream());//然而對於有的鏈接(需要證書的)是連接不上的後面會介紹
HTTPS通過 SSL/TLS傳遞數據.
SSL (Secure Sockets Layer) 是一種在客戶端跟服務器端建立一個加密連接的安全標准. 一般用來加密網絡服務器跟浏覽器,或者是郵件服務器跟郵件客戶端(如: Outlook)之間傳輸的數據.
SSL 能保障敏感信息(如:銀行卡號, 社保卡號, 登陸憑證等)的傳輸安全. 一般情況下, 數據在浏覽器跟服務器之間的傳輸使用的是明文格式, 這種方式存在資料被竊取的風險.如果黑客能攔截浏覽器跟服務器之間的通訊數據,就能看到通訊的內容.
你要是對 SSL或 X.509 證書一無所知, 那我大概解釋下. 對於那些打算用自簽名證書(self-signed certificate)的人來說, 需要了解自簽名證書跟花錢購買機構頒發的證書有什麼區別.
首先我們需要了解下 SSL 證書究竟是個什麼東東? 其實它就包含倆部分: 1) 一個身份標識,一個用來識別身份的東西, 有點類似警察叔叔通過護照或駕照查你的身份;2) 一個公共密鑰, 這個用來給數據加密, 而且只有證書的持有者才能解密.簡而言之,SSL 證書就倆個功能, 身份驗證跟保障通訊過程中的數據安全.
另外還有一點很重要. 那就是一個證書可以給另外一個證書“簽字”. 舉個栗子說:就是 Bob 用他自己的證書在別的證書上蓋上 “同意” 兩個紅紅的大字. 如果你信任 Bob (當然還有他的證書), 那麼你也可以信任由他簽發的證書. 在這個例子中, Bob 搖身一變, 成了證書頒發機構(Certificate Authority). 現在主流的浏覽器都自帶一大堆受信任證書頒發機構(trusted Certificate Authorities)(比如:Thawte,Verisign等).
最後我們講一講浏覽器是怎麼使用證書的.籠統的講,當你打開下列連接的時候 “https://www.yoursite.com” :
服務器會給浏覽器發一個證書.
浏覽器會對比證書中的“common name”(有時也叫 “subject”) 跟服務器的域名是否一樣.例如, 一個從“www.yoursite.com”網站發過來的證書就應該有一個內容是 “www.yoursite.com” 的 common name, 否則浏覽器就會提示該證書有問題.
浏覽器驗證證書真偽, 有點像門衛通過證件上的全息圖辨別你的證件是不是真的. 既然在現實生活中有人偽造別人的身份. 那麼在網絡世界也就有人造假, 比如用你的域名“www.yoursite.com” 來偽造一個安全證書.浏覽器在驗證的時候, 會檢查這個證書是否是它信任機構頒發的,如果不是, 那麼浏覽器就會提示這個證書可能有問題.當然用戶也可以無視警告,繼續使用;(相信大家一定會遇到的,比如12306網頁訪問的時候)
一旦證書通過驗證(或是用戶無視警告, 繼續使用有問題的證書),浏覽器就開始利用證書中的公開密鑰加密數據並傳給服務器.
一旦服務器發過來的證書通過驗證, 浏覽器就會利用證書中包含的公共密鑰加密某個指定的共享密鑰, 然後發給服務器. 這個加密過的共享密鑰只能用服務器的私有密鑰才能解密(非對稱加密), 別人無法解密出其中的內容. 服務器把解密出來的共享密鑰保存起來, 供本次連接會話專用. 從現在開始, 服務器跟浏覽器之間的所有通訊信息都用這個共享密鑰加密解密(對稱加密).
理論部分就這麼多, 下面我們來看幾個例子.
單擊這個綠色圖標, 然後點證書信息連接, 就能看到下列內容.
這是個 SSL 證書, 該證書是 Verisign 給 mail.live.cm 頒發的.
Verisign是一個證書頒發機構,它提示你的浏覽器正在連接的網站是: mail.live.com, 需要跟這個網站的服務器建立一條安全連接進行通訊, 避免他人攔截或篡改浏覽器跟服務器之間傳遞的數據.
MITM 攻擊(MITMA)是指: 黑客攔截篡改網絡中的通訊數據
被動MITMA 是指黑客只能竊取通訊數據, 而在主動 MITMA中,黑客除了竊取數據,還能篡改通訊數據. 黑客利用 MITMA 方式攻擊手機要比攻擊台式電腦容易的多. 這主要是因為使用手機的環境在不固定,有些地方用手機連接上網並不安全, 尤其是那些對公眾免費開放的無線網絡熱點.(這個時候也就需要我們有證書的保證了)
證書頒發機構(CA)
·Symantec(which boughtVeriSign's SSL interests and owns Thawte and Geotrust) 38.1% 市場份額
·Comodo SSL29.1%
·Go Daddy13.4%
·GlobalSign10%
Jelly bean 版本的安卓系統中, 你可以在下列路徑中找到證書頒發機構: 設置->安全 ->受信任的憑證.
說回來Https 連接
URLurl=newURL("https://www.example.com/"); HttpsURLConnectionurlConnection=(HttpsURLConnection)url.openConnection(); InputStreamin=urlConnection.getInputStream();
如果你連接的服務器(www.example.com)傳過來的證書是由機構頒發的, 這段代碼就能正常運行.
但是如果你連的服務器用的是自己頒發的證書(self-singed certificate), 那就會出現錯誤.
自簽名證書就是沒有通過受信任的證書頒發機構, 自己給自己頒發的證書.
SSL證書大致分三類:
由安卓認可的證書頒發機構(如: VeriSign), 或這些機構的下屬機構頒發的證書.
沒有得到安卓認可的證書頒發機構頒發的證書.
自己頒發的證書, 分臨時性的(在開發階段使用)或在發布的產品中永久性使用的兩種.
只有第一種, 也就是那些被安卓系統認可的機構頒發的證書, 在使用過程中不會出現安全提示.
免費. 購買受信任機構頒發的證書每年要交 100 到 500 美元不等的費用. 自簽名證書不花一分錢.
自簽名證書在手機應用中的普及率較高(跟用電腦浏覽網頁不同, 手機的應用一般就固定連一台服務器.).
在開發階段寫的代碼,測試跟發布的時候也可以用.
最近一項調查表明, 810萬個證書中, 只有 320萬個是由受信任機構頒發的. 剩余490萬證書中, 自簽名的占48%, 未知機構頒發的占33%, 而不被信任的機構頒發的證書占19%.
無獨有偶, 我的分析結果也表明,起碼有 60% 安卓應用使用自簽證書.
個人以為, 在手機應用中使用自簽名證書沒什麼不好, 既不需要花錢, 也不需要修改代碼.(注:如果你用的是機構頒發的證書, 在產品發布階段, 需要修改代碼).
但是下面的戲法的一般性的https代碼
URLurl=newURL("https://www.example.com/"); HttpsURLConnectionurlConnection=(HttpsURLConnection) url.openConnection(); InputStreamin=urlConnection.getInputStream();
如果你使用上述的代代碼去驗證你的自己簽署的證書,由於在android操作系統中自己簽署的不能通過驗證的,所以安卓應用軟件將會拋出錯誤。因此你需要書寫你自己的代碼來檢查你的自己簽署的證書。但是在這個時候,安卓開發者犯了一個很大的錯誤,我發現開發者僅僅是簡單的復制、粘貼Stack Overflow和其他博客中允許你的應用默認信任所以證書的答案。即使大多說的答案表述僅僅在測試模式下可以使用,但是開發者簡單地復制代碼,將會導致應用軟件在遭到中間件攻擊和session黑客攻擊是,表現的非常的脆弱!
例子:http://stackoverflow.com/questions/2703161/how-to-ignore-ssl-certificate-errors-in-apache-httpclient-4-0
http://stackoverflow.com/questions/2012497/accepting-a-certificate-for-https-on-android?lq=1
http://www.caphal.com/android/using-self-signed-certificates-in-android/
http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https
在使用自己簽署的證書時一般性的錯誤
TrustManager的主要責任是去決定提出的認證證書應該是可信任的。如果證書是不可信任的,那麼連接將會被終止。去認證遠程的安全套接字識別,你需要用一個或者多個TrustManager(s)初始化SSLContext對象。
importorg.apache.http.conn.ssl.SSLSocketFactory; publicclassMySSLSocketFactoryextendsSSLSocketFactory{ SSLContextsslContext=SSLContext.getInstance("TLS"); publicMySSLSocketFactory(KeyStoretruststore)throwsNoSuchAlgorithmException,KeyManagementException,KeyStoreException,UnrecoverableKeyException{ super(truststore); TrustManagertm=newX509TrustManager(){ publicvoidcheckClientTrusted(X509Certificate[]chain,StringauthType)throwsCertificateException{ } publicvoidcheckServerTrusted(X509Certificate[]chain,StringauthType)throwsCertificateException{ } publicX509Certificate[]getAcceptedIssuers(){ returnnull; } }; sslContext.init(null,newTrustManager[]{tm},null); } @Override publicSocketcreateSocket(Socketsocket,Stringhost,intport,booleanautoClose)throwsIOException,UnknownHostException{ returnsslContext.getSocketFactory().createSocket(socket,host,port,autoClose); } @Override publicSocketcreateSocket()throwsIOException{ returnsslContext.getSocketFactory().createSocket(); } }
我發現,80到100個應用軟件中,在20到25個應用中實現了上述的代碼。(在asyncHttp的網絡封裝中就用到了這段代碼,不過你只要不使用AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort)構造方法,將fixNoHttpResponseException設置為true是不會出問題的,當時如果你要用到https時的證書該如何,可看http://blog.csdn.net/u013424496/article/details/51133544文章)
在上述的TrustManager接口中,可以實現信任所以的證書,不論是誰簽署的或者即使他們發布的任何主題。這個接口將會允許接受ANY證書。接受任何的證書將會危害數據的完整性、安全性等等。
在上述的例子中,檢查客戶端可信任性,獲得接受事件,檢查服務器端可信任性是三點重要的功能。每一位開發者都應該留意這三點功能的實現上。但是卻很少有開發者從不同的網站中搜索、負責上述的功能。
忘記檢查證書是否在這個地址發布是有可能的。當證書接受了example.com的服務器,那麼另外的一個域名也將被接受。
HostnameVerifierhostnameVerifier=org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; DefaultHttpClientclient=newDefaultHttpClient(); SchemeRegistryregistry=newSchemeRegistry(); SSLSocketFactorysocketFactory=SSLSocketFactory.getSocketFactory(); socketFactory.setHostnameVerifier((X509HostnameVerifier)hostnameVerifier); registry.register(newScheme("https",socketFactory,443)); SingleClientConnManagermgr=newSingleClientConnManager(client.getParams(),registry); DefaultHttpClienthttpClient=newDefaultHttpClient(mgr,client.getParams()); //Setverifier HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); //Examplesendhttprequest finalStringurl="https://www.paypal.com” HttpPosthttpPost=newHttpPost(url); HttpResponseresponse=httpClient.execute(httpPost); HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
HostnameVerifierhostnameVerifier=org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
上述的代碼中,即使是錯誤的實現,也將會接受對任何域名的任何CA證書聲明。
應用軟件開發者在相同的應用中使用最大安全、不安全的連接,或者不使用SSL將是免費的。這不是直接的SSL聲明,但是和提到的沒有向外的簽署個有關,同時對於一般軟件的使用者,檢查是否使用一個安全的連接是不可能的。這將會為例如SSL剝離,或者像FireSheep這樣的攻擊開後門。
SSL剝離是另外的一種可以使MITMA登陸並抵制SSL連接方式。利用使用最大HTTP和HTTPS應用。SSL剝離依賴大量建立在點擊鏈接、或者來自於沒有SSL重定向保護的網站SSL連接。在SSL剝離中,Mallory用HTTPS://取代了沒有保護的網站中http:// 鏈接中。因此,除非使用者注意到了鏈接被篡改了,Mallory可以完全地規避SSL保護。這樣的攻擊主要與浏覽器應用、或者原生使用安卓WebView應用有關。
更多關於SSL剝離的信息:
http://security.stackexchange.com/questions/41988/how-does-sslstrip-work
http://www.thoughtcrime.org/software/sslstrip/
直接在代碼中固定寫死使用某個服務器的證書.然後在應用中使用自己定義的信任存儲(trust store)代替手機系統自帶的那個, 去連接指定的服務器.
這樣做的好處是, 我們既能使用自簽名證書, 又不需要額外安裝其他證書.
安全性提升- 采用這種方式, 應用不再依賴系統自帶的信任存儲(trust store). 使得破解這種應用變得復雜:首先你要反編譯,修改完後,還要重新編譯.關鍵是你不可能使用應用作者原先用的那個 keystore 文件重新頒發證書.
成本降低- 證書鎖定方式讓我們可以在自己的服務器上使用免費的自簽名證書, 調用自己寫的 API. 雖說復雜了點, 可是像這種既不花錢, 還能提高應用安全的好事上哪找去?
適應性較差- 一旦 SSL 證書出現變動, 應用也要跟著升級.再發布到 Google Play. 然後祈禱用戶能都升級到最新版本.
安卓的 SSLContext 自帶的TrustManager無法讓本文示例中提到的自簽名證書通過驗證.解決的辦法是自己定義一個TrustManager 類. 然後用這個類去驗證自簽名證書.
先把證書加載到KeyStore,然後用 KeyStore生成一個TrustManager 數組, 最後再用這個 TrustManager 數組創建SSLContext.
本文的應用把服務器的證書直接存進應用的資源.(畢竟這個文件是所有用戶都共用的, 而且也不會經常改動), 當然你可以把它存到別的地方.
1)
創建BKS或者 keystore,需要用到下面這個文件,bcprov-jdk15on-146.jar, 版本很多, 我用的是這個:http://download.csdn.net/detail/johnson106/3870999, 下載後, 把文件存到C:\codeproject.
然後用Keytool 生成 keystore 文件.(keytool是 Java SDK 自帶的文件, 跟javac 放在同一個目錄下)在命令提示符窗口中輸入 keytool 就能看到這個工具的各種選項說明. 或者輸入下列路徑運行.
"C:\Program Files (x86)\Java\jre7\bin>keytool".
2)
下面是用keytool 生成 keysotre 文件的命令.要是這個文件已經存在, 這一步可以忽略.
keytool -genkey -alias codeproject -keystore C:\codeproject\codeprojectssl.keystore -validity 365
這行命令創建一個別名為code project 的密鑰(key),生成的文件名是 codeprojectssl.keystore. 執行文件生成過程中會要求輸入密鑰(key)跟keystore的密碼諸如此類的東東. 這裡需要注意下,當要求你錄入 Common name的時候, 要填你的主機名(我的電腦-右鍵-屬性就可以看到). 本文例子用的是: codeproject.com
3)
keytool -export -alias codeproject -keystore C:\codeproject\codeprojectssl.keystore -file C:\codeproject\codeprojectsslcert.cer
這行命令將密鑰(key)從 .keystore文件導入 .cer 文件.
4)
keytool -import -alias codeproject -file C:\codeproject\codeprojectsslcert.cer -keystore C:\codeproject\codeprojectssl.bks -storetype BKS -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath C:\codeproject\bcprov-jdk15on-146.jar
搞定! 現在, 全部.bks文件都生成了.稍後將這些文件復制到安卓應用中. 連接那些使用自簽名證書的服務器的時候會用到.
把.keystore文件復制到/androidappdir/res/raw/
創建一個新類: MyHttpClient, 繼承DefaultHttpClient 類. 這個新類在驗證SSL 證書的時候, 會自動加載我們自己創建的 keystore 文件, 而不是安卓自帶的那個.只要證書跟服務器匹配上了就沒問題. 代碼如下:
importjava.io.InputStream; importjava.security.KeyStore; importandroid.content.Context; publicclassMyHttpClientextendsDefaultHttpClient{ privatestaticContextcontext; publicstaticvoidsetContext(Contextcontext){ MyHttpClient.context=context; } publicMyHttpClient(HttpParamsparams){ super(params); } publicMyHttpClient(ClientConnectionManagerhttpConnectionManager,HttpParamsparams){ super(httpConnectionManager,params); } @Override protectedClientConnectionManagercreateClientConnectionManager(){ SchemeRegistryregistry=newSchemeRegistry(); registry.register(newScheme("http",PlainSocketFactory.getSocketFactory(),80)); //用我們自己定義的 SSLSocketFactory 在ConnectionManager 中注冊一個 443 端口 registry.register(newScheme("https",newSslSocketFactory(),443)); returnnewSingleClientConnManager(getParams(),registry); } privateSSLSocketFactorynewSslSocketFactory(){ try{ //GetaninstanceoftheBouncyCastleKeyStoreformat KeyStoretrusted=KeyStore.getInstance("BKS"); //從資源文件中讀取你自己創建的那個包含證書的 keystore 文件 InputStreamin=MyHttpClient.context.getResources().openRawResource(R.raw.codeprojectssl);//這個參數改成你的keystore文件名 try{ //用 keystore 的密碼跟證書初始化 trusted trusted.load(in,"這裡是你的 keystore 密碼".toCharArray()); }finally{ in.close(); } //PassthekeystoretotheSSLSocketFactory.Thefactoryisresponsible //fortheverificationoftheservercertificate. SSLSocketFactorysf=newSSLSocketFactory(trusted); //Hostnameverificationfromcertificate //http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506 sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);//這個參數可以根據需要調整, 如果對主機名的驗證不需要那麼嚴謹, 可以將這個嚴謹程度調低些. returnsf; }catch(Exceptione){ thrownewAssertionError(e); } } }
MyHttpClient 類的調用代碼如下:
//InstantiatethecustomHttpClient DefaultHttpClientclient=newMyHttpClient(getApplicationContext()); HttpGetget=newHttpGet("https://www.google.com"); //以 GET 方式讀取服務器返回的數據 HttpResponsegetResponse=client.execute(get); HttpEntityresponseEntity=getResponse.getEntity();
Android界面的坐標是以左上角為起始點,平行的為X軸,垂直的為Y軸,數值都是遞增的。如下圖所示: Android的Rect類是形成一個矩形的區域,區域
從今天開始,把看書時候的知識點整理成博客, 這個比較簡單,估計有經驗的都用過,weight屬性 在做Android布局的時候,經常遇到需要幾個控件按比例分配空間的情況
Android中的TextView是整個framework中最復雜的控件之一,負責Android中顯示文本的大部分工作,framwork中的許多控件也直接或者間接的繼承於
Fragment必須是依存於Activity而存在的,因此Activity的生命周期會直接影響到Fragment的生命周期。官網這張圖很好的說明了倆者的關系:可以看到Fr