You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Android 4.0 WebView中配置SSL证书实现信任?

解决Android 4.0信任Let's Encrypt证书的问题

首先得帮你理清手里的.pem证书和常见的.crt文件的区别——其实它们本质都是X.509证书格式,只是扩展名不同而已:.crt可以是二进制或Base64编码,.pem一般是带-----BEGIN CERTIFICATE-----标记的Base64编码文件。你手里的几个文件作用分别是:

  • cert.pem:你的域名专属的服务器证书,只包含站点公钥
  • chain.pem:Let's Encrypt的中间证书链,用来连接你的服务器证书和根证书
  • fullchain.pem:是cert+chain的合并版,包含完整的证书验证链,旧Android系统必须要完整链才能通过验证
  • key.pem:服务器的私钥,这个是服务器端用的,客户端完全不需要碰它

为什么Android 4.0连不上你的站点?

核心原因是Android 4.0(API 14-15)的系统信任列表里没有收录Let's Encrypt的根证书(也就是ISRG Root X1),而且旧版本的SSL/TLS栈对证书链的校验更严格,缺少中间链直接就判定证书不可信,这也是新版本Android能正常访问的原因——新版本系统已经把这些根证书加入信任列表了。

下面给你两个可行的解决方案,根据你的需求选就行:

方案1:用Portecle给Android 4.0系统手动导入证书(适合测试/定制设备)

你已经有Portecle了,操作很简单:

  1. 打开Portecle,导入fullchain.pem文件(一定要用fullchain,它包含完整的证书链)
  2. 将证书导出为DER格式(导出时可以把扩展名改成.crt,Android认这个格式)
  3. 把导出的证书放到Android 4.0设备的SD卡根目录
  4. 打开设备「设置」→「安全」→「从SD卡安装证书」,选择刚才的证书,用途选「VPN和应用程序」,跟着提示完成导入
  5. 重启设备后,你的站点就能被系统信任了

方案2:在App代码中自定义信任管理器(推荐给正式发布的应用)

如果是你开发的App,不想让用户手动操作,可以在代码里让App主动信任Let's Encrypt的证书链:

  1. fullchain.pem复制到App的res/raw目录下(重命名时别加空格或特殊字符)
  2. 编写一个自定义SSL工具类,加载证书链:
import android.content.Context;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

public class LetsEncryptSSLHelper {
    public static SSLContext getTrustedSSLContext(Context context) throws Exception {
        // 初始化证书工厂,加载fullchain.pem
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        InputStream certStream = context.getResources().openRawResource(R.raw.fullchain);
        
        // 构建自定义信任密钥库
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);
        keyStore.setCertificateEntry("lets_encrypt_cert", certFactory.generateCertificate(certStream));
        
        // 初始化信任管理器
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        
        // 创建并返回自定义SSLContext
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
        return sslContext;
    }
}
  1. 在你的WebView或网络请求库中使用这个自定义SSLContext:
    比如WebView的配置:
WebView myWebView = findViewById(R.id.my_webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

try {
    SSLContext sslContext = LetsEncryptSSLHelper.getTrustedSSLContext(this);
    myWebView.setWebViewClient(new WebViewClient() {
        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            // 正常配置下不会走到这里,除非证书有问题,这里不要直接忽略错误
            handler.proceed();
        }
    });
    // 如果用OkHttp发起网络请求,也可以配置客户端使用这个SSLContext
    // OkHttpClient client = new OkHttpClient.Builder()
    //         .sslSocketFactory(sslContext.getSocketFactory())
    //         .build();
} catch (Exception e) {
    e.printStackTrace();
}
myWebView.loadUrl("你的站点URL");

额外要注意的点

  • Android 4.0的SSL/TLS栈不支持TLS 1.2及以上版本,所以你的服务器必须开启TLS 1.0支持——检查你的Nginx/Apache配置,确保ssl_protocols里包含TLSv1
  • 绝对不要把key.pem放到客户端App里,这是服务器的私钥,泄露会导致严重的安全问题

内容的提问来源于stack exchange,提问作者why

火山引擎 最新活动