HTTPS-соединение с сертификатом клиента в приложении для Android

Я пытаюсь заменить текущее работающее HTTP-соединение на HTTPS-соединение в приложении для Android, которое я пишу. Необходима дополнительная безопасность HTTPS-соединения, поэтому я не могу игнорировать этот шаг.

У меня есть следующее:

  1. Сервер, настроенный для установки HTTPS-соединения и требующий сертификата клиента
    • У этого сервера есть сертификат, выданный стандартным большим -масштаб CA. Короче говоря, если я получаю доступ к этому подключению через браузер в Android, оно работает нормально, потому что хранилище доверенных сертификатов устройств распознает CA. (Значит, это не самоподписанный)
  2. Сертификат клиента, который по сути самоподписанный. (Выдается внутренним центром сертификации)
  3. Приложение Android, которое загружает этот сертификат клиента и пытается подключиться к вышеупомянутому серверу, но имеет следующие проблемы / свойства:
    • Клиент может подключиться к серверу, если сервер настроен на не требовать сертификат клиента. В принципе, если я использую SSLSocketFactory.getSocketFactory () , соединение работает нормально, но сертификат клиента является обязательной частью этих спецификаций приложения, поэтому:
    • Клиент создает javax.net. ssl.SSLPeerUnverifiedException: нет исключения однорангового сертификата , когда я пытаюсь подключиться с помощью моего пользовательского 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)
    }
}

Я проверил это, да, действительно, хранилище ключей загружает сертификат, и все это устраивает.

У меня есть две теории относительно того, чего мне не хватает в чтении о HTTPS / SSL-соединениях, но поскольку это действительно мой первый набег, я немного озадачен тем, что мне действительно нужно для решения этой проблемы.

Первая возможность, насколько я могу судить, состоит в том, что мне нужно настроить этот SSLSocketFactory с хранилищем доверенных сертификатов устройств, которое включает все стандартные промежуточные центры сертификации и центры сертификации конечных точек. То есть устройство по умолчанию SSLSocketFactory.getSocketFactory () загружает некоторый набор центров сертификации в хранилище доверенных сертификатов фабрики, которое используется для доверия серверу, когда он отправляет свой сертификат, и это то, что дает сбой в моем коде, потому что у меня неправильно загружено хранилище доверенных сертификатов. Если это правда, как мне лучше всего загрузить эти данные?

Вторая возможность связана с тем, что сертификат клиента самоподписан (или выпущен внутренним центром сертификации - поправьте меня, если я неправильно, но на самом деле это одно и то же, для всех намерений и целей здесь). На самом деле мне не хватает этого хранилища доверенных сертификатов, и в основном мне нужно предоставить серверу возможность проверить сертификат с помощью внутреннего ЦС, а также подтвердить, что этот внутренний ЦС на самом деле является "надежный" . Если это правда, то что именно я ищу? Я видел некоторые ссылки на это, которые заставляют меня думать, что это может быть моя проблема, как в здесь , но я действительно не уверен.Если это действительно моя проблема, что бы я попросил у человека, обслуживающего внутренний центр сертификации, и как мне добавить это в свой код, чтобы мое HTTPS-соединение работало?

Третье и, надеюсь, менее возможное решение , заключается в том, что я совершенно не прав в каком-то моменте здесь и пропустил важный шаг или полностью игнорирую часть HTTPS / SSL, о которой я просто в настоящее время ничего не знаю. Если это так, не могли бы вы дать мне небольшое руководство, чтобы я мог пойти и узнать, что мне нужно выучить?

Спасибо за чтение!

31
задан Community 23 May 2017 в 12:18
поделиться