如何使用未知CA自签名的证书使Android Volley执行HTTPS请求? [英] How can I make Android Volley perform HTTPS request, using a certificate self-signed by an Unknown CA?
问题描述
在提出问题之前,我找到了一些链接,并逐个检查了这些链接,但没有一个为我提供了解决方案:
Before making the question, I found some links, which I checked, one by one, and none of them, gives me a solution:
- 著名的CA 使用截击的HTTPS请求
- 接受所有SSL证书 无对等证书异常-Volley和具有自我的Android签名证书
- Node.js(Socket.io) Socket.io + SSL +自签名CA证书在连接时给出错误
- 已导入手动"自签名证书: 使用自签名证书和CA的Android SSL HTTP请求
- 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
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证书的示例(相当糟糕的主意...)
我也找到了这个博客,其中有很多解释,但是最后,我意识到这些示例使用的是"apache.org"中不推荐使用的库,而且博客本身没有适用于Android Volley的内容.
https://nelenkov.blogspot.mx/2011/12/using-custom-certificate-trust-store-on.html
I also have found this blog, which has a plenty of explanations, but at the end, I realized that the examples are using deprecated libraries from "apache.org" and also, the blog itself doesn't have a content for 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
There is also this link from Android and the code of "Unknown certificate authority" section, which gives a good idea about the solution, but the code itself, lacks something in its structure (Android Studio complaining...): 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,该示例将初始化一个新的SSLContext,该SSLContext提供一个SSLSocketFactory,您可以使用该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."
现在,这是我的问题:我有一台使用自签名证书的Web服务器,并根据其证书创建了一个"BKS信任库".我已将de BKS信任库导入到我的Android应用程序中,现在,我的应用程序中包含以下代码(我想在这里发布MainActivity,这是到目前为止,与该主题相关的唯一类):/p>
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());
}
此后,在另一个类方法中,我使用了在类COnstructor上配置的HttpClientStack的RequestQueue:
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监视器中看到以下消息:
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证书?
原谅我,但是我既是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.
更新
我改进了类的构造函数,对语句进行了更好的分组,还使用了 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());
}
}
...
...
}
反正我还是有问题.
响应: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.
推荐答案
我通过以下代码在我的凌空类中创建了新的requestQueue,从而实现了https
i have implemented https by creating new requestQueue in my volley class by the following code
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new HurlStack(null, newSslSocketFactory()));
}
return mRequestQueue;
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = getApplicationContext().getResources().openRawResource(R.raw.keystore);
try {
// Initialize the keystore with the provided trusted certificates
// Provide the password of the keystore
trusted.load(in, KEYSTORE_PASSWORD);
} finally {
in.close();
}
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(trusted);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sf = context.getSocketFactory();
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
这篇关于如何使用未知CA自签名的证书使Android Volley执行HTTPS请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!