react-native-fs 安卓端访问https服务器报错java.security.cert.CertPathValidatorException

耿志义
2023-12-01
完整报错信息为:java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
经过排查发现http服务器下载没什么问题,但是配置了CA证书的https安全连接服务器,iOS端没问题,安卓端就直接报这个异常了。
废话不多说,附上解决方法。
需要在安卓项目中react-native-fs目录下修改插件的请求方式。
进入Android/react-native-fs/src/main/java/com.rnfs/Downloader
找到这个方法
private void download

里面就是fs的网络请求设置了

private void download(DownloadParams param, DownloadResult res) throws Exception {
        InputStream input = null;
        OutputStream output = null;
        HttpURLConnection connection = null;

        try {
            connection = (HttpURLConnection)param.src.openConnection();

            ReadableMapKeySetIterator iterator = param.headers.keySetIterator();

            while (iterator.hasNextKey()) {
                String key = iterator.nextKey();
                String value = param.headers.getString(key);
                connection.setRequestProperty(key, value);
            }

            connection.setConnectTimeout(param.connectionTimeout);
            connection.setReadTimeout(param.readTimeout);
            connection.connect();

            int statusCode = connection.getResponseCode();
            int lengthOfFile = connection.getContentLength();

            boolean isRedirect = (
                    statusCode != HttpURLConnection.HTTP_OK &&
                            (
                                    statusCode == HttpURLConnection.HTTP_MOVED_PERM ||
                                            statusCode == HttpURLConnection.HTTP_MOVED_TEMP ||
                                            statusCode == 307 ||
                                            statusCode == 308
                            )
            );

            if (isRedirect) {
                String redirectURL = connection.getHeaderField("Location");
                connection.disconnect();

                connection = (HttpURLConnection) new URL(redirectURL).openConnection();
                connection.setConnectTimeout(5000);
                connection.connect();

                statusCode = connection.getResponseCode();
                lengthOfFile = connection.getContentLength();
            }

可以看到作者直接使用了无安全验证的网络请求方式

HttpURLConnection

那么当我们需要请求带有证书验证的https请求的时候,我们只需要改成信任自己的证书,或者信任所有证书的https请求方式即可。

以下为修改后的方法

private void download(DownloadParams param, DownloadResult res) throws Exception {
    InputStream input = null;
    OutputStream output = null;
    HttpsURLConnection connection = null;

    try {

      connection = (HttpsURLConnection)param.src.openConnection();
      trustAllHosts(connection);
      connection.getHostnameVerifier();
      connection.setHostnameVerifier(DO_NOT_VERIFY);

      ReadableMapKeySetIterator iterator = param.headers.keySetIterator();

      while (iterator.hasNextKey()) {
        String key = iterator.nextKey();
        String value = param.headers.getString(key);
        connection.setRequestProperty(key, value);
      }

      connection.setConnectTimeout(param.connectionTimeout);
      connection.setReadTimeout(param.readTimeout);
      connection.connect();

      int statusCode = connection.getResponseCode();
      int lengthOfFile = connection.getContentLength();

      boolean isRedirect = (
        statusCode != HttpsURLConnection.HTTP_OK &&
        (
          statusCode == HttpsURLConnection.HTTP_MOVED_PERM ||
          statusCode == HttpsURLConnection.HTTP_MOVED_TEMP ||
          statusCode == 307 ||
          statusCode == 308
        )
      );

      if (isRedirect) {
        String redirectURL = connection.getHeaderField("Location");
        connection.disconnect();

        connection = (HttpsURLConnection) new URL(redirectURL).openConnection();
        connection.setConnectTimeout(5000);
        connection.connect();

        statusCode = connection.getResponseCode();
        lengthOfFile = connection.getContentLength();
      }
除了网络请求类替换成安全验证的https请求类以外,还需要调用忽略所有验证的方法
HttpsURLConnection
      trustAllHosts(connection);
      connection.getHostnameVerifier();
      connection.setHostnameVerifier(DO_NOT_VERIFY);

方法实现直接复制进去即可

//====================== 信任所有证书
private static final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
  public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    return new java.security.cert.X509Certificate[]{};
  }

  public void checkClientTrusted(X509Certificate[] chain, String authType)
          throws CertificateException {
  }

  public void checkServerTrusted(X509Certificate[] chain, String authType)
          throws CertificateException {
  }
}};

  /**
   * 设置不验证主机
   */
  private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
      return true;
    }
  };

  /**
   * 信任所有
   * @param connection
   * @return
   */
  private static SSLSocketFactory trustAllHosts(HttpsURLConnection connection) {
    SSLSocketFactory oldFactory = connection.getSSLSocketFactory();
    try {
      SSLContext sc = SSLContext.getInstance("TLS");
      sc.init(null, trustAllCerts, new java.security.SecureRandom());
      SSLSocketFactory newFactory = sc.getSocketFactory();
      connection.setSSLSocketFactory(newFactory);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return oldFactory;
  }

//  =========

到此设置结束。https服务器的文件下载也没有问题了。

ps:我用模拟器还是会报错,用真机一切正常。具体为什么暂时没有深究。





 类似资料: