【问题标题】:Making a HTTPS request using Android Volley使用 Android Volley 发出 HTTPS 请求
【发布时间】:2023-12-09 13:46:01
【问题描述】:

我正在尝试使用此代码发出 https 请求:

RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
request = new Request<String>(Request.Method.GET,"https://devblahblahblah.com/service/etc",errListener);

但我收到此错误:

com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException:信任锚 找不到证书路径。

需要注意的两点:

  1. HTTPS 证书有效。轻松打开,不会在浏览器上发出任何警告。
  2. 以上代码适用于 HTTP 链接。

我实际上需要知道 Android Volley 框架中是否有任何开关/选项,我可以通过这些开关/选项成功访问 HTTPS URL?

【问题讨论】:

  • 我猜你需要像直接使用HttpUrlConnection一样配置它。见commonsware.com/blog/2013/03/04/ssl-android-basics.htmlnelenkov.blogspot.ie/2011/12/…
  • 我找到了一个名为HurlStack 的类,它扩展了HttpStack,这是Volley.newRequestQueue 的可选参数。 HurlStack 的构造函数接受 SSLSocketFactory 类型的参数,它写在它的 javadoc 中:“用于 HTTPS 连接的 SSL 工厂”,但还没有尝试过。

标签: android https android-volley


【解决方案1】:

警告:以下代码不应在生产中使用,因为它容易受到 SSL 攻击

下面的这些代码可能会对您有所帮助:

1.创建一个实现X509TrustManagerHttpsTrustManager类:

public class HttpsTrustManager implements X509TrustManager {

    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};

    @Override
    public void checkClientTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {

    }

    @Override
    public void checkServerTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {

    }

    public boolean isClientTrusted(X509Certificate[] chain) {
        return true;
    }

    public boolean isServerTrusted(X509Certificate[] chain) {
        return true;
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return _AcceptedIssuers;
    }

    public static void allowAllSSL() {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }

        });

        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[]{new HttpsTrustManager()};
        }

        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        HttpsURLConnection.setDefaultSSLSocketFactory(context
                .getSocketFactory());
    }

}

2.在发出https请求之前添加HttpsTrustManager.allowAllSSL()

HttpsTrustManager.allowAllSSL();
String  tag_string_req = "string_req";
StringRequest strReq = new StringRequest(Request.Method.POST,
        your_https_url, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        Log.d(TAG, "response :"+response);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.d(TAG, "Error: " + error.getMessage());
    }
}){
    @Override
    protected Map<String, String> getParams() {
        Map<String, String> params = new HashMap<String, String>();
        params.put("username", "max");
        params.put("password", "123456");
        return params;
    }
};
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);

【讨论】:

  • 这一切都很好,只是您实际上并没有验证服务器证书。请确保不要将其部署在生产环境中,除非您想让您的用户遭受各种 SSL 攻击。
  • 这不是用于生产的。
  • 这看起来像是在 localhost 上进行自签名证书的简单解决方案
  • 如何像海报所问的那样与 Volley 一起工作?
  • 如果您使用此应用,您的应用将在 Google Play 中被拒绝。
【解决方案2】:

你可以add this class 并通过onCreate 方法执行它

new NukeSSLCerts().nuke();

它将使凌空信任所有 SSL 证书。

【讨论】:

  • 另一种方式是certbot,提供免费的签名证书,但只有90天,我们必须每90天更新一次
  • 完美开发
  • 从 2017 年 3 月 1 日起,Google 可以阻止您的应用:support.google.com/faqs/answer/7188426“从 2017 年 3 月 1 日开始,Google Play 将阻止发布任何使用不安全的 HostnameVerifier 实施的新应用或更新。您发布的APK 版本将不受影响,但除非您解决此漏洞,否则应用程序的任何更新都将被阻止。”
  • @MaxF 在我给出的第二条评论中,另一种方法。总而言之,只有在你处于开发模式时才使用 nukeSSl 类
【解决方案3】:

如果你正在使用 volley 并且想要 HTTPS 请求或 SSL 认证服务,那么你可以选择这个最简单的方法:-->

步骤 --> 1. 将 .cer 文件保存到 res/raw/ 文件夹中。

步骤 --> 2. 使用此方法并将 .cer 文件名替换为您的 .cer 文件,并同时替换您的主机名。

private SSLSocketFactory getSocketFactory() {

    CertificateFactory cf = null;
    try {

        cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = getResources().openRawResource(R.raw.cert_name);
        Certificate ca;
        try {

            ca = cf.generateCertificate(caInput);
            Log.e("CERT", "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);


        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {

                Log.e("CipherUsed", session.getCipherSuite());
                return hostname.compareTo("10.199.89.68")==0; //The Hostname of your server.

            }
        };


        HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
        SSLContext context = null;
        context = SSLContext.getInstance("TLS");

        context.init(null, tmf.getTrustManagers(), null);
        HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

        SSLSocketFactory sf = context.getSocketFactory();


        return sf;

    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }

    return  null;
}

Step --> 3. 替换这一行“RequestQueue queue = Volley.newRequestQueue(this);”使用“RequestQueue queue = Volley.newRequestQueue(this, new HurlStack(null, getSocketFactory()));”应截击的要求。

【讨论】:

    【解决方案4】:

    到目前为止,唯一的答案是关于添加不受信任的证书作为解决方案,但由于您的浏览器没有抱怨,这通常意味着 Volley 找不到完成完整受信任链的中间证书。

    LetsEncrypt 证书发生在我身上。大多数浏览器已经拥有中间证书,所以在浏览器上一切看起来都很好,但 Volley 显然缺少了一些东西。

    解决方案
    将中间证书添加到您的网络服务器配置中。对于 Apache,您可以参考以下参考:
    https://access.redhat.com/solutions/43575

    对于 LetsEncrypt,具体来说就是这个文件:/etc/letsencrypt/live/your.website.com/chain.pem 因此,除了您的 CertificateFile 和 KeyFile 之外,您应该已经开始工作了,您现在有了第三行:

    SSLCertificateChainFile /etc/letsencrypt/live/your.website.com/chain.pem
    

    只需添加该行,重新启动 apache 和 Volley 就不会再抱怨了,您也没有引入任何安全漏洞!

    【讨论】:

    • thanx,这有帮助,目前还不是很清楚我需要编辑什么文件,但我终于想通了。虽然对我来说是不同的,因为我必须在 nodejs 中进行编辑。不过谢谢。
    • 另外,要真正追踪可能的 SSL 设置错误,您可以使用此工具测试您的网站:ssllabs.com/ssltest/index.html 如果在使用该工具后您突然沉迷于得分 A+,Mozilla 有一个有用的 SSL配置生成器:mozilla.github.io/server-side-tls/ssl-config-generator 现代风格通常让你毫无问题地达到 A+。
    【解决方案5】:

    我无法打开@Ogre_BGR 提供的链接,但是在浏览网络时,我发现实际实现在以下smanikandan14 Github 中完成。查看他的SSl-connection explanation 以了解更多信息。

    【讨论】:

      【解决方案6】:

      这可能有多种原因,包括:

      • 颁发服务器证书的 CA 未知
      • 服务器证书不是由 CA 签名的,而是自签名的
      • 服务器配置缺少中间 CA

      Official doc from android

      解决方案: 您可以在请求中提供证书文件

      【讨论】:

        【解决方案7】:

        对于遇到此类问题并且您使用 Letsencrypt 作为 SSL 和 node.js 作为网络服务器的任何人,试试这个。假设你有这样的东西。我通过添加行来解决此问题 const chain = fs... 希望这会有所帮助

        ...
        const app = express();
        const privateKey  = fs.readFileSync('ssl/privkey.pem', 'utf8');
        const certificate = fs.readFileSync('ssl/cert.pem', 'utf8');
        const chain = fs.readFileSync('ssl/chain.pem', 'utf8');
        const credentials = {key: privateKey, cert: certificate, ca: chain};
        ...
        var httpsServer = https.createServer(credentials, app);
        

        【讨论】:

          【解决方案8】:

          当我将 ssl 添加到域时,我遇到了同样的问题,经过 2 天后,我找到了 URL 错误的解决方案。我使用的是https://example.com,但是当我将 ssl 添加到域中时,url 将会改变

          https://www.example.com

          POST 工作正常

          【讨论】:

            【解决方案9】:

            当我从 cloudflare 关闭代理时出现此错误 check image here 此问题的最佳解决方案是您可以打开代理返回并在 ssl 证书上添加完全安全访问。

            【讨论】:

              【解决方案10】:

              如果有人使用nginx 和来自letsencrypt 的SSL 证书,解决方案是简单地使用来自文件fullchain.pem 的证书而不是cert.pem

              ssl_certificate     /.../fullchain.pem;
              

              此文件包含您的证书和 CA 的串联。

              【讨论】: