cod*_*rmx 14 ssl https android android-volley trustmanager
在提出问题之前,我找到了一些链接,我逐一检查了这些链接,但没有一个链接给我一个解决方案:
我到目前为止唯一找到的链接是这一个,它提供了两种方法:使用Android Volley发出HTTPS请求
我也找到了这个博客,其中有很多解释,但最后,我意识到这些例子都使用了来自"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,该示例初始化一个新的SSLContext它提供了一个SSLSocketFactory,您可以使用它来覆盖HttpsURLConnection的默认SSLSocketFactory.这样连接将使用您的CA进行证书验证."
现在,这是我的问题:我有一个使用自签名证书的网络服务器,我根据其证书创建了一个"BKS信任库".我已将de BKS信任库导入我的Android APP,现在,我的应用程序上有以下代码(我刚刚在这里发布了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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
关于构造函数类,我冒昧地复制代码,对我从每个部分理解的内容做了一些评论:
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());
}
Run Code Online (Sandbox Code Playgroud)
在此之后,在另一个Class方法中,我有RequestQueue,使用我在Class COnstructor上配置的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>()
{
...
...
}
Run Code Online (Sandbox Code Playgroud)
当我运行我的应用程序,提供我的WebServer预期的用户和密码时,我可以在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未找到证书路径.
经过所有这些解释,我有以下问题:
原谅我,但我是Android编程的初学者,也是Java的初学者,所以也许,我犯了一个可怕的错误......
任何帮助将非常感激.
UPDATE
我已经改进了类的构造函数,对语句进行了更好的分组,并且还使用了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());
}
}
...
...
}
Run Code Online (Sandbox Code Playgroud)
无论如何,我还有问题..
响应:com.android.volley.NoConnectionError:javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:未找到证书路径的信任锚.
我已经通过以下代码在我的凌空类中创建新的requestQueue来实现https
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我过去遇到过类似的问题,有效的解决方案是在服务器端安装中间证书颁发机构。
这里值得注意的是,在大多数桌面浏览器中访问此服务器不会导致像完全未知的 CA 或自签名服务器证书那样的错误。这是因为大多数桌面浏览器都会随着时间的推移缓存受信任的中间 CA。一旦浏览器访问并了解了某个站点的中间 CA,下次就不需要将该中间 CA 包含在证书链中。
有些站点故意为用于提供资源的辅助 Web 服务器执行此操作。例如,他们的主要 HTML 页面可能由具有完整证书链的服务器提供服务,但图像、CSS 或 JavaScript 等资源的服务器不包括 CA,大概是为了节省带宽。不幸的是,有时这些服务器可能会提供您尝试从 Android 应用程序调用的 Web 服务,这并不那么宽容。
配置服务器以将中间 CA 包含在服务器链中。大多数 CA 提供有关如何为所有常见 Web 服务器执行此操作的文档。如果您需要网站至少在 Android 4.2 及以上版本中使用默认 Android 浏览器,这是唯一的方法。
您可以按照此处提到的步骤操作缺少中间证书颁发机构
另一个例子什么是中级证书?
浏览器可能接受根证书颁发机构,但 Android SDK 可能不会这样做,因为浏览器会缓存相同的内容。浏览器将缓存中间证书,并在不同站点之间使用它们。因此,如果您缺少中间证书,随机用户将收到信任错误,而其他用户则不会。 中间证书会在 Firefox 中缓存吗?
| 归档时间: |
|
| 查看次数: |
10503 次 |
| 最近记录: |