Android安全加密:HTTPS程式設計

tony0087發表於2021-09-09

Android安全加密專題文章索引

  1. Android安全加密:對稱加密
  2. Android安全加密:非對稱加密
  3. Android安全加密:訊息摘要Message Digest
  4. Android安全加密:數字簽名和數字證照
  5. Android安全加密:Https程式設計

以上學習所有內容,對稱加密、非對稱加密、訊息摘要、數字簽名等知識都是為了理解數字證照工作原理而作為一個預備知識。數字證照是密碼學裡的終極武器,是人類幾千年歷史總結的智慧的結晶,只有在明白了數字證照工作原理後,才能理解Https 協議的安全通訊機制。最終才能在SSL 開發過程中得心應手。

另外,對稱加密和訊息摘要這兩個知識點是可以單獨拿來使用的。

知識點串聯:

數字證照使用到了以上學習的所有知識

  • 對稱加密與非對稱加密結合使用實現了祕鑰交換,之後通訊雙方使用該祕鑰進行對稱加密通訊。
  • 訊息摘要與非對稱加密實現了數字簽名,根證照機構對目標證照進行簽名,在校驗的時候,根證照用公鑰對其進行校驗。若校驗成功,則說明該證照是受信任的。
  • Keytool 工具可以建立證照,之後交給根證照機構認證後直接使用自簽名證照,還可以輸出證照的RFC格式資訊等。
  • 數字簽名技術實現了身份認證與資料完整性保證。
  • 加密技術保證了資料的保密性,訊息摘要演算法保證了資料的完整性,對稱加密的高效保證了資料處理的可靠性,數字簽名技術保證了操作的不可否認性。

通過以上內容的學習,我們要能掌握以下知識點:

  • 基礎知識:bit 位、位元組、字元、字元編碼、進位制轉換、io
  • 知道怎樣在實際開發裡怎樣使用對稱加密解決問題
  • 知道對稱加密、非對稱加密、訊息摘要、數字簽名、數字證照是為了解決什麼問題而出現的
  • 瞭解SSL 通訊流程
  • 實際開發裡怎樣請求Https 的介面

概述

SSL(Secure Sockets Layer 安全套接層),為網景公司(Netscape)所研發,用以保障在Internet 上資料傳輸之安全,利用資料加密(Encryption)技術,可確保資料在網路上之傳輸過程中不會被擷取及竊聽。一般通用之規格為40 bit 之安全標準,美國則已推出128 bit 之更高安全標準,但限制出境。只要3.0 版本以上之I.E.或Netscape 瀏覽器即可支援SSL。

TLS(Transport Layer Security 傳輸層安全),用於在兩個通訊應用程式之間提供保密性和資料完整性。TLS 是SSL 的標準化後的產物,有1.0 ,1.1 ,1.2 三個版本,預設使用1.0。TLS1.0 和SSL3.0 幾乎沒
有區別,事實上我們現在用的都是TLS,但因為歷史上習慣了SSL 這個稱呼。

SSL 通訊簡單圖示:

這裡寫圖片描述

SSL 通訊詳細圖示:

這裡寫圖片描述

當請求使用自簽名證照的網站資料時,例如請求12306 的客運服務頁面:https://kyfw.12306.cn/otn/,則會報下面的錯誤,原因是客戶端的根認證機構不能識別該證照錯誤資訊:unable to find valid certification path to requested target

解決方案1

一個證照可不可信,是由TrustManager 決定的,所以我們只需要自定義一個什麼都不做的TrustManager即可,伺服器出示的所有證照都不做校驗,一律放行。

public static void main(String[] args) throws Exception {
//協議傳輸層安全TLS(transport layer secure)
SSLContext sslContext = SSLContext.getInstance("TLS");
//建立信任管理器(TrustManager 負責校驗證照是否可信)
TrustManager[] tm = new TrustManager[]{new EmptyX509TrustManager()};
//使用自定義的信任管理器初始化SSL 上下文物件
sslContext.init(null, tm, null);
//設定全域性的SSLSocketFactory 工廠(對所有ssl 連結都產生影響)
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));
 }

 /**
 * 自定義一個什麼都不做的信任管理器,所有證照都不做校驗,一律放行
 */
 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 伺服器出示的證照是中鐵集團SRCA 給他頒發的,所以SRCA 的證照是能夠識別12306 的證照的,所以只需要把SRCA 證照匯入系統的KeyStore 裡,之後交給TrustManagerFactory 進行初始化,則可把SRCA 新增至根證照認證機構,之後校驗的時候,SRCA 對12306 證照校驗時就能通過認證。

這種解決方案有兩種使用方式:一是直接使用SRCA.cer 檔案,二是使用改檔案的RFC 格式資料,將其寫在程式碼裡。

//12306 證照的RFC 格式(注意要記得手動新增兩個換行符)
 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 {
 // 使用傳輸層安全協議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,用來匯入證照
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
//引數null 表示使用系統預設keystore,也可使用其他keystore(需事先將srca.cer 證照匯入
keystore 裡)
keyStore.load(null);
//通過流建立一個證照
Certificate certificate = CertificateFactory.getInstance("X.509")
.generateCertificate(certInputStream);
// 把srca.cer 這個證照匯入到KeyStore 裡,別名叫做srca
keyStore.setCertificateEntry("srca", certificate);
// 設定使用keyStore 去進行證照校驗
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//用我們設定好的TrustManager 去做ssl 通訊協議校驗,即證照校驗
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 請求:

把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);

雙向證照驗證

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

網路流量安全測試工具,Google的開源專案:https://github.com/google/nogotofail

相關文章