Подтвердить что ты не робот

SSLSocket через другой SSLSocket

Я пытаюсь создать SSLSocket поверх другого SSLSocket в приложении для Android. Более низкое соединение - это SSL-защищенная связь с Secure Web Proxy (прокси HTTP через SSL), верхнее соединение для HTTP через SSL ( HTTPS).

Для этого я использую функцию SSLSocketFactory createSocket(), которая позволяет передать существующий Socket, для которого можно запустить SSL-соединение, например:

private Socket doSSLHandshake(Socket socket, String host, int port) throws IOException {
    TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager(){
                public X509Certificate[] getAcceptedIssuers(){ return null; }
                public void checkClientTrusted(X509Certificate[] certs, String authType) {}
                public void checkServerTrusted(X509Certificate[] certs, String authType) {}
            }
    };

    try {
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new SecureRandom());
        SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, true);
        sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());
        sslSocket.setEnableSessionCreation(true);
        sslSocket.startHandshake();
        return sslSocket;
    } catch (KeyManagementException | NoSuchAlgorithmException e) {
        throw new IOException("Could not do handshake: " + e);
    }
}

Этот код работает нормально, когда базовый сокет является обычным Tcp Socket, но когда я использую в качестве базового сокета SSLSocket, который был создан с использованием вышеприведенного кода раньше, рукопожатие выходит из строя со следующим исключением:

javax.net.ssl.SSLHandshakeException: Handshake failed
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429)
    at com.myapp.MyThreadClass.doSSLHandshake(MyThreadClass.java:148)
    at com.myapp.MyThreadClass.run(MyThreadClass.java:254)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x7374d56e80: Failure in SSL library, usually a protocol error
    error:100000e3:SSL routines:OPENSSL_internal:UNKNOWN_ALERT_TYPE (external/boringssl/src/ssl/s3_pkt.c:618 0x738418ce7e:0x00000000)
    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
    ... 2 more

Я тестирую на Android 7.1.1. Приложение настроено на уровень 23 SDK.

  • Что я могу делать неправильно?
  • Как я могу продолжить отладку проблемы?
  • Есть ли у кого-нибудь рабочий пример SSLSocket, который перебирает другой SSLSocket на недавней версии Android?

Любая помощь очень ценится!


Обновить. Тот же самый код работает в JRE 1.8 на Mac, но не на Android.


Обновление 2. Концептуально это шаги, через которые проходит соединение:

  • В приложении Android сделайте подключение к серверу Secure Proxy (Socket)
  • Выполнение SSL/TLS-связи с прокси-сервером (SSLSocket через Socket)
  • Через SSLSocket отправьте сообщение CONNECT на прокси-сервер
  • Прокси подключается к серверу назначения (https) и только копирует байты с этого момента
  • Сопоставление SSL/TLS с сервером назначения (https) (SSLSocket через SSLSocket через Socket)
  • Отправьте сообщение GET на целевой сервер и прочитайте ответ

Проблема возникает на шаге 5 при выполнении рукопожатия на SSLSocket который проходит через SSLSocket (который проходит через Socket).


Обновление 3: теперь я открыл репозиторий GitHub с образцом проекта и tcpdumps: https://github.com/FD-/SSLviaSSL


Примечание: я нашел и прочитал вопрос с очень похожим заголовком, но он не содержал много полезной помощи, к сожалению.

4b9b3361

Ответ 1

Я не думаю, что вы делаете что-то неправильно. Похоже, есть ошибка в согласовании протокола во время вашего второго рукопожатия. Хороший кандидат будет терпеть неудачу в расширении квитирования TLS NPN.

Взгляните на свои протоколы в этом вызове: sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());

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

Ответ 2

Итак, я попытался выяснить, что пойдет не так в случае с Android, но до сих пор я не нашел ничего плохого в вашем коде. Кроме того, поскольку код работает для JRE, он утверждает гипотезу.

Из предоставленного вами tcpdump имеется существенная информация, чтобы заключить, как Android ведет себя с тем же набором API, что и JRE.

Посмотрим на JRE tcpdump:

введите описание изображения здесь

  • См. начальные сообщения подтверждения (Client Hello, Server Hello, change cipher spec). Это показывает рукопожатие между клиентом JRE и прокси-сервером. Это успешно.
  • Теперь мы не видим второго рукопожатия между клиентом JRE и www.google.com(конечным сервером), как это зашифровано, поскольку мы делаем SSL через SSL. Прокси-сервер копирует их по битам на конечный сервер. Так что это правильное поведение.

Теперь посмотрим на android tcpdump:

введите описание изображения здесь

  • См. начальные сообщения подтверждения (Client Hello, Server Hello, change cipher spec). Это показывает рукопожатие между клиентом android и прокси-сервером. Это успешно.
  • Теперь в идеале мы не должны видеть второе рукопожатие, поскольку оно должно быть зашифровано. Но здесь мы видим, что клиент Android отправляет "приветствие клиента" и отправляет его на "www.google.com", даже если пакет отправляется на прокси-сервер. введите описание изображения здесь
  • Вышеуказанное значение связано с отказом, поскольку пакет должен был быть написан через SSL-сокет, а не через начальный простой сокет. Я просмотрел ваш код, и я вижу, что вы делаете второе рукопожатие над SSLSocket, а не над обычным сокетом.

Анализ прокси /stunnel wireshark:

Случай JRE:

В случае JRE клиент выполняет начальное SSL-соединение с сервером stunnel/proxy. То же самое можно увидеть ниже: введите описание изображения здесь

Рукопожатие выполнено успешно и соединение выполнено

Затем клиент пытается подключиться к удаленному серверу (www.google.com) и начинает рукопожатие. Таким образом, приветствие клиента, отправленное клиентом, рассматривается как зашифрованное сообщение в пакете № 34, и когда stunnel расшифровывает его, он отображается на "Client hello", который перенаправляется на прокси-сервер с помощью stunnel введите описание изображения здесь

Теперь посмотрим на дело клиента Android. введите описание изображения здесь

Исходное SSL-обращение от клиента к stunnel/proxy успешно, как видно выше.

Затем, когда клиент android начинает рукопожатие с удаленным (www.google.com), в идеале он должен использовать SSL-сокет для этого же. Если это так, мы должны увидеть зашифрованный трафик от android до stunnel (аналогично пакету № 34 из случая JRE), stunnel должен расшифровать и отправить "client hello" в прокси. однако, как вы можете видеть ниже, клиент Android отправляет "приветствие клиента" через обычный сокет.

введите описание изображения здесь

Если вы сравните пакет # 24 с пакетом № 34 от JRE, мы можем определить эту разницу.

Вывод:

Это ошибка с реализацией SSL-протокола android SSL (factory.createsocket() с SSL-сокетом), и я чувствую, что не может быть волшебного обходного пути для того же, используя тот же набор API. На самом деле я нашел эту проблему в списке ошибок Android. См. Ниже ссылку: https://code.google.com/p/android/issues/detail?id=204159

Эта проблема по-прежнему не решена, и вы, возможно, можете продолжить работу с командой разработчиков Android, чтобы исправить ее.

Возможные решения:

Если мы пришли к выводу, что один и тот же набор API-интерфейсов не может работать, вы остаетесь только с одним вариантом:

  • Создайте собственную SSL-оболочку через SSL-сокет. Вы можете вручную сделать рукопожатие или использование сторонней реализации. Это может привести к но выглядит как единственный способ.

Ответ 3

Так как HTTPS гарантирует, что средний человек не прерывает связь между ними. вот почему вы не можете этого сделать. так что второе рукопожатие терпит неудачу.

Вот ссылка может помочь.

HTTPS-соединения через прокси-серверы

Ответ 4

Я не знаю, поможет ли это: Я построил тестовую среду репо, и моя ошибка несколько отличается:

I/SurfaceTextureClient(20733): [0x52851b98] frames:2, duration:1.005000, fps:1.989805
I/System.out(20733): [socket][2] connection /192.168.1.100:10443;LocalPort=35380(0)
I/System.out(20733): [CDS]connect[/192.168.1.100:10443] tm:90
I/System.out(20733): [socket][/192.168.1.123:35380] connected
I/System.out(20733): Doing SSL handshake with 192.168.1.100:10443
I/System.out(20733): Supported protocols are: [SSLv3, TLSv1, TLSv1.1, TLSv1.2]
E/NativeCrypto(20733): ssl=0x53c96268 cert_verify_callback x509_store_ctx=0x542e0a80 arg=0x0
E/NativeCrypto(20733): ssl=0x53c96268 cert_verify_callback calling verifyCertificateChain authMethod=RSA
I/System.out(20733): Doing SSL handshake with 192.168.1.100:443
I/System.out(20733): Supported protocols are: [SSLv3, TLSv1, TLSv1.1, TLSv1.2]
E/NativeCrypto(20733): Unknown error during handshake
I/System.out(20733): Shutdown rx/tx
I/System.out(20733): [CDS]close[35380]
I/System.out(20733): close [socket][/0.0.0.0:35380]
W/System.err(20733): javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException:
                        SSL handshake aborted: ssl=0x53c9c1d8:
                        Failure in SSL library, usually a protocol error
W/System.err(20733): error:140770FC:SSL routines:
                        SSL23_GET_SERVER_HELLO:
                        unknown protocol (external/openssl/ssl/s23_clnt.c:766 0x4e7cb3ad:0x00000000)
W/System.err(20733):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:413)
W/System.err(20733):    at com.bugreport.sslviassl.SecureWebProxyThread.doSSLHandshake(SecureWebProxyThread.java:147)
W/System.err(20733):    at com.bugreport.sslviassl.SecureWebProxyThread.run(SecureWebProxyThread.java:216)
W/System.err(20733): Caused by: javax.net.ssl.SSLProtocolException:
                        SSL handshake aborted: ssl=0x53c9c1d8:
                        Failure in SSL library, usually a protocol error
W/System.err(20733): error:140770FC:SSL routines:
    SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:766 0x4e7cb3ad:0x00000000)
W/System.err(20733):    at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
W/System.err(20733):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:372)
W/System.err(20733):    ... 2 more
I/SurfaceTextureClient(20733): [0x52851b98] frames:5, duration:1.010000, fps:4.946089

Одна мысль о потоках: какой поток вы используете для своего метода doSSLHandshake()?

public void policy()
{
    int SDK_INT = android.os.Build.VERSION.SDK_INT;
    if (SDK_INT > 8)
    {
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
    }
}