在提出问题之前,我找到了一些链接,逐个检查了它们,但没有一个给我提供解决方案:
- 知名CA 使用 volley 进行 HTTPS 请求
- 接受所有 SSL 证书 自签名证书 Volley 和 Android 中的 no peer certificate 异常
- Node.js(Socket.io) Socket.io + SSL + 自签名 CA 证书连接时报错
- 手动导入的自签名证书: 使用自签名证书和 CA 的 Android SSL HTTP 请求
迄今为止,我唯一找到的一个链接是这个,其中提供了两种方法:使用 Android Volley 进行 HTTPS 请求
- 1º 指示将某些类导入到应用程序中,实际上有其他必须导入的类,并且这些类使用了“apache.org”的不推荐使用的库
- 2º 一个 NUKE 所有 SSL 证书的示例(非常糟糕的想法...)
我还发现了这篇博客,其中有很多解释,但最终我意识到示例使用了来自“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
但是,来自该链接的以下引用似乎是解决问题的核心概念。
“TrustManager 是系统用于验证服务器证书的组件,并通过使用一个或多个 CA 创建的 KeyStore 创建 TrustManager,这些 CA 将是该 TrustManager 所信任的唯一 CA。 给定新的 TrustManager,示例初始化了一个新的 SSLContext,提供了一个 SSLSocketFactory,您可以使用它来覆盖 HttpsURLConnection 的默认 SSLSocketFactory。这样,连接将使用您的 CA 进行证书验证。”
现在,这是我的问题:我有一个正在使用自签名证书的 Web 服务器,并根据其证书创建了“BKS truststore”。我已将 BKS truststore 导入到我的 Android 应用程序中,现在,我在应用程序中有以下代码(我只是在此处发布了 MainActivity,这是目前涉及此主题的唯一类,我想):
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();
}
}
}
关于构造函数类,我已经复制了代码并添加了一些注释,说明我对每个部分的理解:
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());
}
接着,在另一个类方法中,我有一个请求队列(RequestQueue),使用的是我已经在类构造函数上配置好的HttpClientStack:
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>()
{
...
...
}
当我在我的应用程序中输入Web服务器期望的用户名和密码并运行应用程序时,我可以在Android Studio的Android Monitor中看到以下消息:
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.
经过所有这些解释,我有以下问题:
- 除了在类的构造函数中配置自定义TrustManager的CA之外,还需要配置什么才能使Android接受SSL证书?
很抱歉,我是一个初学者,不论是在Android编程上还是在Java上,所以也许我犯了一个可怕的错误...
任何帮助都将不胜感激。
更新
我已经改进了类的构造函数,更好地分组语句,并使用了KeyManagerFactory,这似乎在这个过程中非常重要。以下是代码:
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: 未找到用于认证路径的信任锚点。