Прежде чем задать вопрос, я нашел несколько ссылок, которые я проверил один за другим, и ни один из них не дает мне решения:
- well-kown CA Запрос HTTPS с использованием залпа
- принять все сертификаты SSL Отсутствие экспертного сертификата Exception - Volley и Android с самоподписанным сертификатом
- Node.js(Socket.io) Socket.io + SSL + самоподписанный сертификат CA дает ошибку при подключении
- Самоподписанный сертификат "MANUALLY" импортирован: Андроид HTTP HTTP-запрос с использованием самозаверяющего сертификата и центра сертификации
Единственная ссылка, которую я нашел до сих пор, - это та, которая дает два подхода: Выполнение запроса HTTPS с использованием 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
Существует также эта ссылка от Android и код раздела "Неизвестный центр сертификации", который дает хорошее представление об этом решении, но сам код не имеет чего-то в своей структуре (Android Studio жалуется...): https://developer.android.com/training/articles/security-ssl.html
Но эта цитата из ссылки кажется основной концепцией решения проблемы.
"TrustManager - это то, что система использует для проверки сертификатов с сервера и путем создания одного из KeyStore с одним или несколькими ЦС - это будут единственные CA, которым доверяет этот TrustManager. Учитывая новый TrustManager, в примере инициализируется новый SSLContext, который предоставляет SSLSocketFactory, который вы можете использовать для переопределения SSLSocketFactory по умолчанию из HttpsURLConnection. Таким образом, соединение будет использовать ваши CA для проверки сертификата.
И теперь, вот моя проблема: У меня есть веб-сервер, который использует самозаверяющий сертификат, и я создал "BKS truststore" на основе его сертификата. Я импортировал доверительный магазин BKS в свой 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, который я сконфигурировал в 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 Monitor от Android Studio следующие сообщения:
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: привязка доверия для пути сертификации не найдена.
После всего этого объяснения у меня возникает следующий вопрос:
- Что еще нужно настроить, чтобы заставить Android принять SSL-сертификат из CA настраиваемого TrustManager, который я настроил в конструкторе класса?
Простите меня, но я начинаю программировать на 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());
}
}
...
...
}
Во всяком случае, у меня все еще возникают проблемы.
Ответ: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: привязка доверия для пути сертификации не найдена.
Опять же, любая помощь была бы очень признательна.