先貼一個Google官方對https和ssl的說明: https://developer.android.com/training/articles/security-ssl.html 首先說為什么使用https,簡單點說就是為了防止數據傳輸過程中信息被竊取或偷換。防止中間人攻擊。 分兩種情況來說:
從 Android 4.2 (Jelly Bean) 開始,Android 目前包含在每個版本中更新的 100 多個 知名CA。CA 具有一個證書和一個私鑰,這點與服務器相似。為服務器發放證書時,CA 使用其私鑰簽署服務器證書。然后,客戶端可以驗證該服務器是否具有平臺已知的 CA 發放的證書。 假設有一個由知名 CA 發放證書的網絡服務器,那么,可以使用直接使用retrofit即可發起安全的請求。對于驗證證書和主機名的細節,OKHttp 框架在 API 中已經考慮了這些細節。
在這種情況下,由于您具有系統不信任的 CA,將發生 SSLHandshakeException。原因可能是您有一個來自 Android 還未信任的新 CA 的證書,或您的應用在沒有 CA 的較舊版本上運行。CA 未知的原因通常是因為它不是公共 CA,而是政府、公司或教育機構等組織發放的僅供自己使用的私有 CA。 此過程可能有點復雜,下面的示例展示了這個過程,從 InputStream 獲取一個特定的 CA,用該 CA 創建 KeyStore,然后用后者創建和初始化 TrustManager。TrustManager 是系統用于從服務器驗證證書的工具,可以使用一個或多個 CA 從 KeyStore 創建,而創建的 TrustManager 將僅信任這些 CA。 如果是新的 TrustManager,此示例將初始化一個新的 SSLContext,后者可以提供一個 SSLSocketFactory,您可以通過 OkHttpClient添加socketFactory(SSLSocketFactory)。這樣一來,連接將使用您的 CA 驗證證書。 具體步驟如下: 1. 使用命令行獲得服務器的數字證書。 echo -n | openssl s_client -connect api.abc.com:443 | sed -ne ‘/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p’ > server.cert 注意粗體內容要分別替換成你的服務器地址和要保存到本地的數字證書文件名稱。 2. 轉化數字證書為BKS格式 我們知道java本身支持的證書格式jks,但是遺憾的是在android當中并不支持jks格式正式,而是需要bks格式的證書。因此我們需要將jks證書轉換成bks格式證書。 首先要下載bouncycastle 的JAR,這是目前的最新版: http://repo2.maven.org/maven2/org/bouncycastle/bcPRov-jdk16/1.46/bcprov-jdk16-1.46.jar 執行以下命令進行轉換: keytool -importcert -v -trustcacerts -file “server.cert” -alias server_alias -keystore “server.bks” -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath “bcprov-jdk16-146.jar” -storetype BKS -storepass bkspassWord
同樣注意黑體字部分是你需要修改的。 將生成好的server.bks拷貝到android工程的res/raw目錄 開始代碼:
public static SSLSocketFactory getSSLSocketFactory(Context context){ try { KeyStore ksTrust = KeyStore.getInstance("BKS"); InputStream instream = context.getResources() .openRawResource(R.raw.server); ksTrust.load(instream, "bkspassword".toCharArray()); //TrustManager decides which certificate authorities to use. TrustManagerFactory tmf = TrustManagerFactory .getInstance("X509");//TrustManagerFactory.getDefaultAlgorithm() tmf.init(ksTrust); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); return sslContext.getSocketFactory(); } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) { e.printStackTrace(); } return null; }然后在初始化retrofit的時候把SSLSocketFactory加進去
okHttpClient = new OkHttpClient.Builder() .addInterceptor(httpLoggingInterceptor) .socketFactory(sslSocketFactory) .build();新聞熱點
疑難解答