首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Android HTTPS 通訊完整實作:從原理到三種憑證驗證策略

     編輯:Java 相關知識

HTTPS 是移動端網路安全的基石。本文系統解析 SSL/TLS 握手原理,並針對 Android 客戶端實作三種不同安全等級的 HTTPS 連線策略:**預設信任系統 CA、信任自簽憑證(忽略驗證)、攜帶自訂 KeyStore 驗證特定憑證**,附完整可編譯程式碼與 Nginx 憑證生成指引。

一、HTTPS / SSL/TLS 核心原理速覽

HTTPS = HTTP + SSL/TLS。核心目標解決三大風險:

  • 竊聽: 加密傳輸,第三方無法讀取明文。
  • 竄改: 完整性校驗,資料被修改即可偵測。
  • 冒充: 數位憑證驗證伺服器身分。

握手四步驟(簡化):

  1. ClientHello:客戶端送出支援的協定版本、隨機數、加密套件。
  2. ServerHello:伺服器回應確認版本、隨機數、憑證鏈(含公鑰)。
  3. Client 驗證憑證 → 產生 Pre-Master Secret → 用伺服器公鑰加密送出。
  4. 雙方獨立推導 Session Key,後續通訊採對稱加密(AES/ChaCha20)。

二、伺服器端:Nginx 自簽憑證生成與配置

# 1. 生成私鑰
openssl genrsa -out server.key 2048
# 2. 生成自簽憑證 (有效期 10 年)
openssl req -new -x509 -key server.key -out server.crt -days 3650 -subj "/CN=myapp.local"
# 3. Nginx 配置片段
server {
    listen 443 ssl;
    server_name myapp.local;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
}

三、Android 客戶端三種策略實作

基礎依賴: Android 6.0+ 建議改用 HttpURLConnection 或 OkHttp;本文為相容舊專案示範 Apache HttpClient(已棄用),新專案請直接使用 OkHttp。

策略 1:預設信任系統 CA(適用正規憑證)

public class HttpsClientDefault {
    public static HttpClient create() {
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
        return new DefaultHttpClient(new ThreadSafeClientConnManager(new BasicHttpParams(), registry), new BasicHttpParams());
    }
}

策略 2:信任所有憑證(僅限測試環境,生產嚴禁)

public class TrustAllSSLSocketFactory extends SSLSocketFactory {
    private final SSLContext sslContext = SSLContext.getInstance("TLS");

    public TrustAllSSLSocketFactory(KeyStore truststore) throws Exception {
        super(truststore);
        TrustManager tm = new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() { return null; }
            public void checkServerTrusted(X509Certificate[] chain, String authType) {}
            public void checkClientTrusted(X509Certificate[] chain, String authType) {}
        };
        sslContext.init(null, new TrustManager[]{tm}, null);
    }

    @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); }
    @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    public static SSLSocketFactory getSocketFactory() {
        try {
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null, null);
            return new TrustAllSSLSocketFactory(ks);
        } catch (Exception e) { throw new RuntimeException(e); }
    }
}

// 使用方式:registry.register(new Scheme("https", TrustAllSSLSocketFactory.getSocketFactory(), 443));

⚠️ 風險: 完全失去身分驗證,易遭中間人攻擊(MITM)。

策略 3:攜帶自訂 KeyStore 驗證特定自簽憑證(推薦生產環境)

步驟 1:將 server.crt 轉為 BKS KeyStore(需 BouncyCastle Provider)

keytool -importcert -v -trustcacerts -alias myapp -file server.crt \
    -keystore myapp.bks -storetype BKS \
    -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
    -providerpath bcprov-jdk16-146.jar \
    -storepass your_keystore_password

將生成的 myapp.bks 放入 Android 專案 res/raw/ 目錄。

步驟 2:自訂 SocketFactory 載入 KeyStore

public class PinnedKeyStoreSocketFactory extends SSLSocketFactory {
    private final SSLContext sslContext;

    public PinnedKeyStoreSocketFactory(KeyStore truststore) throws Exception {
        super(truststore);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(truststore);
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);
    }

    @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); }
    @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    public static SSLSocketFactory getSocketFactory(Context context) {
        try (InputStream in = context.getResources().openRawResource(R.raw.myapp)) {
            KeyStore ks = KeyStore.getInstance("BKS");
            ks.load(in, "your_keystore_password".toCharArray());
            return new PinnedKeyStoreSocketFactory(ks);
        } catch (Exception e) { throw new RuntimeException(e); }
    }
}

// 使用方式:registry.register(new Scheme("https", PinnedKeyStoreSocketFactory.getSocketFactory(context), 443));

四、現代化替代方案:OkHttp + CertificatePinner

新專案強烈建議使用 OkHttp,程式碼極簡且安全:

OkHttpClient client = new OkHttpClient.Builder()
    .certificatePinner(new CertificatePinner.Builder()
        .add("myapp.local", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
        .build())
    .build();

Request request = new Request.Builder().url("https://myapp.local/api").build();
client.newCall(request).enqueue(new Callback() { ... });

sha256/... 為伺服器憑證公鑰的 SHA-256 指紋,可透過 openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64 取得。

五、安全檢核清單

  • ✅ 生產環境**必須**使用策略 3 或 OkHttp Certificate Pinning。
  • ✅ 憑證到期前輪換機制(KeyStore 更新需發版或動態下發)。
  • ✅ 啟用 TLS 1.2+,禁用 SSLv3、TLS 1.0/1.1。
  • ✅ 伺服器端配置 HSTS、HPKP(可選)強化防禦。

正確實作 HTTPS 驗證策略,是保障用戶資料傳輸安全的底線。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved