如何使用Unkown CA自签名的证书让Android Volley执行HTTPS请求? [英] How can I make Android Volley perform HTTPS request, using a certificate self-signed by an Unkown CA?

查看:265
本文介绍了如何使用Unkown CA自签名的证书让Android Volley执行HTTPS请求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在提出问题之前,我找到了一些链接,我一个接一个地查看,但没有一个给我一个解决方案:

Before making the question, I found some links, which I checked, one by one, and none of them, gives me a solution:

  • well-kown CA HTTPS request using volley
  • accept all SSL certificates No peer certificate Exception - Volley and Android with self signed certificate
  • Node.js (Socket.io) Socket.io + SSL + self-signed CA certificate gives error when connecting
  • Self-Signed Certificate "MANUALLY" imported: Android SSL HTTP Request using self signed cert and CA

唯一到目前为止我找到的链接是这个,它提供了两种方法:制作使用Android Volley的HTTPS请求

The only link which I have found until now, is this one, which gives two approaches: Making a HTTPS request using Android Volley


  • 1º指示将某些类导入到您的应用中,确实有另外一个类必须导入,并且类使用来自apache.org的弃用库。

  • 2ºNUKE所有SSL ceriticates的一个例子(非常糟糕的想法......)



我也找到了这个博客,其中有很多解释,但最后,我意识到这些例子正在使用来自apache.org的已弃用的库以及博客本身没有Android Volley的内容。
https:// nelenkov。 blogspot.mx/2011/12/using-custom-certificate-trust-store-on.html



还有来自Android的这个链接和未知证书颁发机构部分的代码,它提供了一个关于解决方案的好主意,但代码本身在其结构中缺少一些东西(Android Studio抱怨...): https://developer.android.com/training/articles/security-ssl.html

但链接中的这句话似乎是解决问题的核心概念。

But this quote from the link, seems the core concept for solving the problem.

TrustManager是系统用于验证来自服务器的证书的方法,并且通过从具有一个或多个CA的KeyStore创建一个证书 - 那些将是该TrustManager信任的唯一CA.
给定新的TrustManager,考试ple初始化一个新的SSLContext,它提供了一个SSLSocketFactory,可用于覆盖HttpsURLConnection的默认SSLSocketFactory。通过这种方式,连接将使用您的CA进行证书验证。

"A TrustManager is what the system uses to validate certificates from the server and—by creating one from a KeyStore with one or more CAs—those will be the only CAs trusted by that TrustManager. Given the new TrustManager, the example initializes a new SSLContext which provides an SSLSocketFactory you can use to override the default SSLSocketFactory from HttpsURLConnection. This way the connection will use your CAs for certificate validation."




和现在,这是我的问题:我有一个使用自签名证书的网络服务器,我根据其证书创建了一个BKS信任库。我已将de BKS信任库导入我的Android APP,现在,我在我的应用程序上有以下代码(我刚刚在这里发布了MainActivity,这是迄今为止唯一与此主题相关的类,我想):



And now, here is my problem: I have a webserver that is using a self-signed certificate and I have created a "BKS truststore" based on its certificate. I have imported de BKS truststore to my Android APP and now, I have the following code on my App (I'm just posting here the MainActivity, which is the only class that has relevance to this subject until now, I suppose):

package com.domain.myapp;


import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import android.widget.EditText;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

import java.io.InputStream;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;


public class LoginScreen extends AppCompatActivity {

Context ctx          = null;
InputStream inStream = null;
HurlStack hurlStack  = null;

EditText username    = null;
EditText password    = null;
String loginStatus   = null;

public LoginScreen() {

    try {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        KeyStore ks = KeyStore.getInstance("BKS");
        inStream = ctx.getApplicationContext().getResources().openRawResource(R.raw.mytruststore);
        ks.load(inStream, null);
        inStream.close();
        tmf.init(ks);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        hurlStack = new HurlStack(null, sslSocketFactory);
    } catch (Exception e){
        Log.d("Exception:",e.toString());
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login_screen);
    username = (EditText) findViewById(R.id.user);
    password = (EditText) findViewById(R.id.passwd);
}

public void login(View view) {

    RequestQueue queue = Volley.newRequestQueue(this, hurlStack);
    final String url = "https://myserver.domain.com/app/login";

    StringRequest postRequest = new StringRequest(Request.Method.POST, url,
            new Response.Listener<String>()
            {
                @Override
                public void onResponse(String response) {
                    Log.d("Response", response);
                    loginStatus = "OK";
                }
            },
            new Response.ErrorListener()
            {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.d("Error.Response", String.valueOf(error));
                    loginStatus = "NOK";
                }
            }
    ) {
        @Override
        protected Map<String, String> getParams()
        {
            Map<String, String>  params = new HashMap<String, String>();
            params.put("username", String.valueOf(user));
            params.put("domain", String.valueOf(passwd));

            return params;
        }
    };
    queue.add(postRequest);

    if (loginStatus == "OK") {
        Intent intent = new Intent(LoginScreen.this, OptionScreen.class);
        startActivity(intent);
    } else {
        Toast.makeText(getApplicationContext(), "Login failed",Toast.LENGTH_SHORT).show();
    }
}

}

关于构造函数类,我冒昧地复制代码,对每个部分的内容进行了一些评论:

Regarding the constructor class, I took the liberty of copying the code, putting some comments about what do I understand from each part of it:

try {
// I have a TrustManagerFactory object
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// I have a KeyStore considering BKS (BOUNCY CASTLE) KeyStore object
KeyStore ks = KeyStore.getInstance("BKS");
// I have configured a inputStream using my TrustStore file as a Raw Resource
inStream = ctx.getApplicationContext().getResources().openRawResource(R.raw.mytruststore);
// I have loaded my Raw Resource into the KeyStore object
ks.load(inStream, null);
inStream.close();
// I have initialiazed my Trust Manager Factory, using my Key Store Object
tmf.init(ks);
// I have created a new SSL Context object
SSLContext sslContext = SSLContext.getInstance("TLS");
// I have initialized my new SSL Context, with the configured Trust Managers found on my Trust Store
sslContext.init(null, tmf.getTrustManagers(), null);
// I have configured a HttpClientStack, using my brand new Socket Context
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
hurlStack = new HurlStack(null, sslSocketFactory);
} catch (Exception e){
Log.d("Exception:",e.toString());
}

在此之后,在另一个Class Method中,我有了RequestQueue,使用了HttpClientStack我已在类COnstructor上配置:

After this, in another Class Method, I have the RequestQueue, using the HttpClientStack which I have configured on the Class COnstructor:

RequestQueue queue = Volley.newRequestQueue(this, hurlStack);
final String url = "https://myserver.domain.com/app/login";
StringRequest postRequest = new StringRequest(Request.Method.POST, url,new Response.Listener<String>()
    {
    ...
    ...
    }

当我运行我的应用程序时,提供我的WebServer预期的用户和密码,我可以从Android Studio的Android Monitor中看到以下消息:

When I run my app, giving the user and password which is expected by my WebServer, I can see in the Android Monitor from Android Studio the following messages:


09-17 21:57:13.842 20617-20617 / com.domain。 myapp D / Error.Response:
com.android.volley.NoConnectionError:
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException:找不到证书路径的信任锚。

09-17 21:57:13.842 20617-20617/com.domain.myapp D/Error.Response: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

完成所有这些解释之后,我有以下问题:

After all this explanation, I have the following question:


  • 为了让Android从我在类的构造函数中配置的自定义TrustManager的CA接受SSL证书,还必须配置什么?

  • What else must be configured in order to make Android accept the SSL certificate from the CA of the custom TrustManager which I have configured at the constructor of the class?

原谅我,但我是Android初学者,也是Java的初学者,所以也许,我犯了一个可怕的错误...

Forgive me, but I'm beginner on Android programming, as well on Java, so maybe, I'm making a terrible mistake...

任何帮助,都会非常感激。

Any help, would be much appreciated.

UPDATE

我改进了班级的构造函数,做得更好对语句进行分组,并使用 KeyManagerFactory ,这在此过程中似乎非常重要。这里是:

I have improved the constructor of the class, doing a better grouping of the statements, and also using the KeyManagerFactory, which seems to be pretty important on this process. Here goes:

public class LoginScreen extends AppCompatActivity {

...
...

  public LoginScreen() {

    try {
        inStream = this.getApplicationContext().getResources().openRawResource(R.raw.mytruststore);

        KeyStore ks = KeyStore.getInstance("BKS");
        ks.load(inStream, "bks*password".toCharArray());
        inStream.close();

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
        kmf.init(ks, "bks*password".toCharArray());

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
        tmf.init(ks);

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(),tmf.getTrustManagers(), null);
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        hurlStack = new HurlStack(null, sslSocketFactory);
    } catch (Exception e){
        Log.d("Exception:",e.toString());
    }

  }

...
...

}

无论如何,我仍然遇到问题..

Anyway, I'm still having problems..


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

Response: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

再次,任何帮助,将不胜感激。

Again, any help, would be much appreciated.

推荐答案

我遇到过类似的问题在过去,有效的解决方案是在服务器端安装中间证书颁发机构

I have faced the similar problem in the past and the solution for the same that worked was to install the intermediate certificate authority on the server side.

这里有趣的是,在大多数桌面浏览器中访问此服务器不会导致像完全未知的CA或自签名服务器证书那样的错误。这是因为大多数桌面浏览器会随着时间的推移缓存可信中间CA.一旦浏览器访问并从一个站点了解到中间CA,下次就不需要在证书链中包含中间CA.

What is interesting to note here is that visiting this server in most desktop browsers does not cause an error like a completely unknown CA or self-signed server certificate would cause. This is because most desktop browsers cache trusted intermediate CAs over time. Once a browser has visited and learned about an intermediate CA from one site, it won't need to have the intermediate CA included in the certificate chain the next time.

一些网站有意为用于提供资源的辅助Web服务器执行此操作。例如,他们的主HTML页面可能由具有完整证书链的服务器提供服务,但是服务器用于资源(如图像,CSS或JavaScript)不包括CA,可能是为了节省带宽。不幸的是,有时这些服务器可能会提供您尝试从Android应用程序调用的Web服务,这不是原谅。

Some sites do this intentionally for secondary web servers used to serve resources. For example, they might have their main HTML page served by a server with a full certificate chain, but have servers for resources such as images, CSS, or JavaScript not include the CA, presumably to save bandwidth. Unfortunately, sometimes these servers might be providing a web service you are trying to call from your Android app, which is not as forgiving.

配置服务器以包含中间CA在服务器链中。大多数CA提供有关如何为所有常见Web服务器执行此操作的文档。如果您需要网站至少通过Android 4.2使用默认Android浏览器,这是唯一的方法。

Configure the server to include the intermediate CA in the server chain. Most CAs provide documentation on how to do this for all common web servers. This is the only approach if you need the site to work with default Android browsers at least through Android 4.2.

您可以按照步骤操作如此处所述缺少中间证书授权书

You can follow the steps as mentioned here Missing intermediate certificate authority

另一个例子什么是中间证书?

FYI trust-anchor-not-found-for-android-ssl-connection

浏览器可能会接受根证书颁发机构,但Android SDK可能不会这样做,因为浏览器会缓存相同的内容。浏览器将缓存中间证书,并在不同站点之间使用它们。因此,如果您缺少中间证书,随机用户将收到信任错误,而其他用户则不会。 中间证书是否在Firefox中缓存?

Browsers may accept root certificate authority but Android SDK may not do the same because browsers cache the same. Browsers will cache intermediate certificates, and use them between different sites. Because of that, if you are missing the intermediate certificate, random users will receive a trust error, while others won't. Do intermediate certificates get cached in Firefox?

这篇关于如何使用Unkown CA自签名的证书让Android Volley执行HTTPS请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆