Я пытаюсь заменить действующее HTTP-соединение с HTTPS-соединением в приложении для Android, которое я пишу. Необходима дополнительная безопасность соединения HTTPS, поэтому я не могу игнорировать этот шаг.
У меня есть следующее:
- Сервер, настроенный для установления HTTPS-соединения и требующий сертификата клиента
- Этот сервер имеет сертификат, выдаваемый стандартным крупномасштабным ЦС. Короче говоря, если я получаю доступ к этому соединению через браузер в Android, он отлично работает, потому что доверительный магазин устройств распознает ЦС. (Так что это не самозапись)
- Клиентский сертификат, который по сути является самоподписанным. (Выдается внутренним ЦС)
- Приложение Android, которое загружает этот сертификат клиента и пытается подключиться к вышеупомянутому серверу, но имеет следующие проблемы/свойства:
- Клиент может подключиться к серверу, когда сервер настроен на не, требующий сертификата клиента. В принципе, если я использую
SSLSocketFactory.getSocketFactory()
, соединение работает нормально, но клиентский сертификат является обязательной частью этих спецификаций приложений, поэтому: - Клиент создает исключение
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
, когда я пытаюсь подключиться к своему пользовательскомуSSLSocketFactory
, но я не совсем уверен, почему. Это исключение кажется немного неоднозначным после поиска в Интернете различных решений.
- Клиент может подключиться к серверу, когда сервер настроен на не, требующий сертификата клиента. В принципе, если я использую
Вот код для клиента:
SSLSocketFactory socketFactory = null;
public void onCreate(Bundle savedInstanceState) {
loadCertificateData();
}
private void loadCertificateData() {
try {
File[] pfxFiles = Environment.getExternalStorageDirectory().listFiles(new FileFilter() {
public boolean accept(File file) {
if (file.getName().toLowerCase().endsWith("pfx")) {
return true;
}
return false;
}
});
InputStream certificateStream = null;
if (pfxFiles.length==1) {
certificateStream = new FileInputStream(pfxFiles[0]);
}
KeyStore keyStore = KeyStore.getInstance("PKCS12");
char[] password = "somePassword".toCharArray();
keyStore.load(certificateStream, password);
System.out.println("I have loaded [" + keyStore.size() + "] certificates");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
socketFactory = new SSLSocketFactory(keyStore);
} catch (Exceptions e) {
// Actually a bunch of catch blocks here, but shortened!
}
}
private void someMethodInvokedToEstablishAHttpsConnection() {
try {
HttpParams standardParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(standardParams, 5000);
HttpConnectionParams.setSoTimeout(standardParams, 30000);
SchemeRegistry schRegistry = new SchemeRegistry();
schRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schRegistry.register(new Scheme("https", socketFactory, 443));
ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(standardParams, schRegistry);
HttpClient client = new DefaultHttpClient(connectionManager, standardParams);
HttpPost request = new HttpPost();
request.setURI(new URI("https://TheUrlOfTheServerIWantToConnectTo));
request.setEntity("Some set of data used by the server serialized into string format");
HttpResponse response = client.execute(request);
resultData = EntityUtils.toString(response.getEntity());
} catch (Exception e) {
// Catch some exceptions (Actually multiple catch blocks, shortened)
}
}
Я подтвердил, что да, действительно, keyStore загружает сертификат и все это доволен.
У меня есть две теории относительно того, что мне не хватает в чтении о соединениях HTTPS/SSL, но поскольку это действительно мой первый набег, я немного озадачен тем, что мне действительно нужно для решения этой проблемы.
Первая возможность, насколько я могу судить, заключается в том, что мне нужно настроить этот SSLSocketFactory с помощью доверительного магазина устройств, который включает в себя все стандартные средние сертификаты промежуточных и конечных точек. То есть, по умолчанию устройство SSLSocketFactory.getSocketFactory()
загружает некоторый набор ЦС в доверительный магазин factory, который используется, чтобы доверять серверу, когда он отправляет свой сертификат, и это то, что не работает в моем коде, потому что я неправильно загружен хранилище доверия. Если это так, как мне лучше всего загружать эти данные?
Вторая возможность связана с тем, что сертификат клиента самоподписан (или выпущен внутренним центром сертификации - исправьте меня, если я ошибаюсь, но они действительно равны одному и тому же, для всех целей и цели здесь). На самом деле это доверенное хранилище, которое мне не хватает, и в основном мне нужно предоставить сервер для проверки сертификата с внутренним ЦС, а также проверить, что этот внутренний ЦС фактически "заслуживает доверия". Если это правда, именно то, что я ищу? Я видел некоторые ссылки на это, что заставляет меня поверить, что это может быть моей проблемой, как в здесь, но я действительно не уверен. Если это действительно моя проблема, что бы я попросил у человека, который поддерживает внутренний ЦС, и как я могу добавить это в свой код, чтобы мое HTTPS-соединение работало?
Третье и, надеюсь, менее возможное решение состоит в том, что я совершенно ошибаюсь в некоторых вопросах здесь и пропустил важный шаг или полностью игнорирую часть HTTPS/SSL, которую я просто не знаю в настоящее время, Если это так, не могли бы вы предоставить мне немного направления, чтобы я мог пойти и узнать, что мне нужно узнать?
Спасибо за чтение!