Проблемы с подключением Android SSL SNI

У меня есть приложение, которое служит для получения и обновления данных на веб-сервере, и недавно владелец приложения решил переключиться на безопасное соединение из-за сохраненной личной информации.

Сервер уже настроен как SNI, и я проверил его с помощью digicert, сервер работает нормально и Кажется, настроен правильно, но не включает путь *.host.com в свои альтернативные имена (я не уверен, нормально это или нет для SNI).

iOS работала как часы, однако на Android я получаю эту ошибку:

java.security.cert.CertPathValidatorException: якорь доверия для пути сертификации не найден.

Мой текущий метод подключения выглядит так:

    URL url = new URL(postURL);
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    SSLContext sc;
    sc = SSLContext.getInstance("TLS");

    sc.init(null, null, new java.security.SecureRandom());
    conn.setSSLSocketFactory(sc.getSocketFactory());

    String userpass = "bob" + ":" + "12345678";
    String basicAuth = "Basic " + Base64.encodeToString(userpass.getBytes(), Base64.DEFAULT);
    conn.setRequestProperty("Authorization", basicAuth);

    conn.setReadTimeout(7000);
    conn.setConnectTimeout(7000);
    conn.setRequestMethod("POST");
    conn.setDoInput(true);

    conn.connect();

    InputStream instream = conn.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(instream));

    StringBuilder everything = new StringBuilder();
    String line;

    while ((line = reader.readLine()) != null) {
        everything.append(line);
    }

    JSONObject jsonObject = new JSONObject(everything.toString());

    return jsonObject;

Я не совсем понимаю, в чем здесь проблема, но пытаюсь подключиться к https://sni.velox.ch/ дает мне длинный ответ, который кажется успешным.

Кроме того, у меня есть ключ pem для сертификата, но я не знаю, как добавить его в этом контексте.


person Larpus    schedule 17.11.2015    source источник


Ответы (1)


Обычно вы получаете эту ошибку при использовании самозаверяющего сертификата, и в этом случае вам придется использовать сертификат при выполнении запроса.

Кроме того, вы можете получить эту ошибку из-за отсутствия пути *.host.com.

Вы можете попробовать приведенный ниже код, чтобы передать свой сертификат при создании файла HttpsURLConnection. Не забудьте скопировать файл ca.pem в папку с ресурсами.

private HttpsURLConnection buildSslServerConnection() {
    HttpsURLConnection urlConnection = null;
    try {
        // Load CAs from an InputStream
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = new BufferedInputStream(context.getAssets().open("ca.pem"));
        Certificate ca;
        try {
            ca = cf.generateCertificate(caInput);
        } finally {
            caInput.close();
        }

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        // Create an SSLContext that uses our TrustManager
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        // Tell the URLConnection to use a SocketFactory from our SSLContext
        urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setSSLSocketFactory(context.getSocketFactory());
        urlConnection.setRequestMethod("POST");
        urlConnection.setRequestProperty("Authorization", "Basic" + Base64.encodeToString(userpass.getBytes(), Base64.DEFAULT));
        urlConnection.setConnectTimeout(7000);
        urlConnection.setReadTimeout(7000);
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.setUseCaches(false);
        urlConnection.setAllowUserInteraction(false);
        urlConnection.setDoOutput(false);
    } catch (KeyManagementException e) {
        LOG.error("Error while checking server connectivity: ", e);
    } catch (CertificateException e) {
        LOG.error("Error while checking server connectivity: ", e);
    } catch (FileNotFoundException e) {
        LOG.error("Error while checking server connectivity: ", e);
    } catch (KeyStoreException e) {
        LOG.error("Error while checking server connectivity: ", e);
    } catch (NoSuchAlgorithmException e) {
        LOG.error("Error while checking server connectivity: ", e);
    } catch (MalformedURLException e) {
        LOG.error("Error while checking server connectivity: ", e);
    } catch (IOException e) {
        LOG.error("Error while checking server connectivity: ", e);
    }
    return urlConnection;
}

Надеюсь это поможет.

person Suhas    schedule 17.11.2015
comment
Спасибо за код, но, к сожалению, он не сработал. Просто для ясности: ca.pem должен находиться в папке с ресурсами, расположенной в том же каталоге, что и папки java и res, верно? Я также пытался перевести его в исходное состояние (и внести необходимые коррективы) безрезультатно. - person Larpus; 18.11.2015
comment
Если вы используете maven, он должен быть внутри src/main/assets/ca.pem. В противном случае, если вы используете стандартную структуру проекта Android, папка с ресурсами будет находиться в том же каталоге, что и res, src, libs, AndroidManifest.xml... - person Suhas; 18.11.2015
comment
Да, он был в правильном месте src/main/assets/ca.pem, если я поставлю его в любое другое место, он выдаст мне ошибку о невозможности найти сертификат. Но я все еще получаю Trust anchor for certification path not found. - person Larpus; 18.11.2015
comment
Странно, что код у вас не работает. Просто чтобы уточнить, у вас есть правильный файл pem, верно? - person Suhas; 20.11.2015
comment
Да, так мне сказал администратор сервера. Это не настоящая копия сертификата, а файл pem, сгенерированный с его уникальным ключом/отпечатком пальца (не знаю, как он называется в данном контексте), которого должно быть достаточно, не так ли? - person Larpus; 23.11.2015
comment
Просто чтобы закрыть все это испытание, да, как оказалось, это была проблема с сертификатом на стороне сервера, но это был ДРУГОЙ сертификат, который я никогда не пытался использовать или получить доступ, что заставило Android отказаться установить соединение. - person Larpus; 07.12.2015
comment
Почти забыл об этом. Спасибо за закрытие :) - person Suhas; 07.12.2015