SSLHandshakeException при отправке запроса POST через https

Я попытался отправить запрос POST на удаленный безопасный сервер (вызов https). Сначала я использовал библиотеку OkHTTP для выполнения вызова, так как эта библиотека работала для выполнения вызовов http в предыдущем приложении, но с https она больше не работает, см. Мой предыдущий пост: Невозможно выполнять вызовы HTTPS с помощью библиотеки OKHTTP

Я решил попробовать HttpURLConnection, чтобы сделать вызов, как описано здесь: https://developer.android.com/reference/java/net/HttpURLConnection.html Код, который я использую, работает только на определенных устройствах.

Он работает с Wiko на Android 6.0 с SIM-картой и Wi-Fi и не работает на Samsung на Android 4.2.2, подключенном только к Интернету через Wi-Fi.

Вот logcat проблемы, с которой я сталкиваюсь:

02-23 12:29:59.893 11026-11951 W/System.err: javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
02-23 12:29:59.923 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:382)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:231)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:81)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:197)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:281)
02-23 12:29:59.923 11026-11951 W/System.err:     at com.gulplug.gulplugtoolbox.DiagnosisOnline$NetworkAsyncTask.doInBackground(DiagnosisOnline.java:109)
02-23 12:29:59.923 11026-11951 W/System.err:     at com.gulplug.gulplugtoolbox.DiagnosisOnline$NetworkAsyncTask.doInBackground(DiagnosisOnline.java:82)
02-23 12:29:59.923 11026-11951 W/System.err:     at android.os.AsyncTask$2.call(AsyncTask.java:287)
02-23 12:29:59.923 11026-11951 W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:234)
02-23 12:29:59.923 11026-11951 W/System.err:     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
02-23 12:29:59.923 11026-11951 W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
02-23 12:29:59.923 11026-11951 W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
02-23 12:29:59.933 11026-11951 W/System.err:     at java.lang.Thread.run(Thread.java:856)
02-23 12:29:59.933 11026-11951 W/System.err: Caused by: java.security.cert.CertificateException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:296)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:197)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:598)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:379)
02-23 12:29:59.933 11026-11951 W/System.err:    ... 16 more
02-23 12:29:59.933 11026-11951 W/System.err: Caused by: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
02-23 12:29:59.943 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(RFC3280CertPathUtilities.java:1475)
02-23 12:29:59.943 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:305)
02-23 12:29:59.943 11026-11951 W/System.err:     at com.sec.android.security.pkix.SecCertPathValidatorSpi.engineValidate(SecCertPathValidatorSpi.java:99)
02-23 12:29:59.953 11026-11951 W/System.err:     at java.security.cert.CertPathValidator.validate(CertPathValidator.java:190)
02-23 12:29:59.953 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:283)
02-23 12:29:59.953 11026-11951 W/System.err:    ... 20 more
02-23 12:29:59.953 11026-11951 W/System.err: Caused by: java.security.SignatureException: Signature was not verified
02-23 12:29:59.953 11026-11951 W/System.err:     at org.apache.harmony.security.provider.cert.X509CertImpl.verify(X509CertImpl.java:384)
02-23 12:29:59.953 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.CertPathValidatorUtilities.verifyX509Certificate(CertPathValidatorUtilities.java:1428)
02-23 12:29:59.953 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(RFC3280CertPathUtilities.java:1470)
02-23 12:29:59.953 11026-11951 W/System.err:    ... 24 more

и код, прикрепленный к этому журналу:

class NetworkAsyncTask extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... voids) {
        Authentification authentification = new Authentification();
        authentification.setApplicationToken(applicationtoken);
        Gson gson = new Gson();
        //String json = gson.toJson(authentification);
        String json = "";
        Log.d(TAG, json);
        JSONObject auth = new JSONObject();
        try {
            auth.put("application_token",mytoken);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        String url = myurl;

        try {
            URL object = new URL(url);
            HttpsURLConnection con = (HttpsURLConnection) object.openConnection();

            con.setDoOutput(true);
            con.setDoInput(true);
            con.setRequestProperty("Content-Type", "application/json");
            con.setRequestProperty("Accept", "application/json");
            con.setRequestProperty("Content-Lenght", String.valueOf(json.getBytes("UTF-8").length));
            con.setRequestMethod("POST");
            con.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());

            OutputStreamWriter wr = null;
            wr = new OutputStreamWriter(con.getOutputStream());
            wr.write(auth.toString());
            wr.flush();

            StringBuilder sb = new StringBuilder();
            int HttpResult = 0;

            HttpResult = con.getResponseCode();

            if (HttpResult == HttpURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(
                        new InputStreamReader(con.getInputStream(), "utf-8"));
                String line = null;
                while ((line = br.readLine()) != null) {
                    sb.append(line + "\n");
                }
                br.close();
                Log.d(TAG, "First log");
                Log.d(TAG, "" + sb.toString());
            } else {
                Log.d(TAG, "response code : " + HttpResult);
                Log.d(TAG, "Http not ok");
                Log.d(TAG, con.getResponseMessage());
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Я не понимаю, почему мое первое устройство может проверить подпись, а другое нет. Я попытался синхронизировать дату и время, как описано здесь: Не удалось проверить подпись сертификата?

Возможное объяснение моей проблемы здесь, но я не понимаю https://groups.google.com/forum/

Кто-то сталкивался с такой же проблемой и исправил ее?

UPDT: оба метода, представленные Алексом и Антоном, не работают с моим устройством. Использование сервиса Google Play говорит о том, что поставщик обновлен. И я все еще не получаю проверку подписи при включении TLS 1.1 и 1.2 с помощью метода, описанного здесь: https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/

UPDT 2: по этой ссылке сервер должен использовать TLS 1.1: https://www.ssllabs.com/ TLS включен

4 ответа

Решение

Android 4.2.2 на старом устройстве просто не поддерживает новую версию TLS, используемую на сервере. Вам нужно разрешить использовать хотя бы TLS 1.1 на вашем сервере, ничего не делать на стороне клиента.

UPD: поддержка TLS 1.2 по умолчанию не включена на Android <5, но вы можете включить ее использование с помощью пользовательских SSLSocketFactory включая следующий метод:

private Socket enableTLSOnSocket(Socket socket) {
    if(socket != null && (socket instanceof SSLSocket)) {
        ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
    }
    return socket;
}

см ссылку для справки: https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/

Как сказал Антон Малышев, TLS v1.2 не поддерживается в Android 4.2.2, фактически на устройствах ниже Android 5.0, и даже если он поддерживается на некоторых устройствах, у вас возникнут проблемы с устаревшими сертификатами.

Что вы можете сделать, это попытаться установить google ssl провайдера, если на устройстве есть сервисы воспроизведения.

Вы можете прочитать больше о https://developer.android.com/training/articles/security-gms-provider.html

   private void updateAndroidSecurityProvider() {
    try {
        ProviderInstaller.installIfNeeded(this);
    } catch (Exception e) {
        e.getMessage();
    }
    }

вызовите вышеуказанный метод перед вызовом любого HTTP .

а также

добавлена ​​библиотека ниже в gradle:

compile 'com.google.android.gms:play-services-auth:11.8.0'

Я смог добиться этого, используя OkHttp с Retrofit2. Это написано на Kotlin.

Вы можете добавить это при запуске вашего приложения, возможно в вашей активности запуска:

    try {
           SSLContext.getInstance("TLSv1.2")
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        }

        try {
            ProviderInstaller.installIfNeeded(context)
        } catch (e: Exception) {
            e.printStackTrace()
        }

Также добавьте это в ваш OkHttp Builder:

fun provideSecureClient(): OkHttpClient {

    try {
        val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
        trustManagerFactory.init(null as KeyStore?)
        val trustManagers = trustManagerFactory.trustManagers
        if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) {
            throw IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers))
        }

        val trustManager = trustManagers[0] as X509TrustManager
        val sslContext = SSLContext.getInstance("SSL")
        sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
        val sslSocketFactory = sslContext.socketFactory

        return OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory, trustManager)
                .build()

    } catch (e: Exception) {
        throw RuntimeException(e)
    }

}
Другие вопросы по тегам