Ошибка SSL-рукопожатия Websocket

у меня есть сервер Tomcat spring-boot для безопасных соединений websocket. Сервер принимает клиентов Android 4.4, iOS, Firefox и Chrome без сбоев с сертификатом, подписанным органом. Android 5.0, однако, не удается SSL рукопожатие.

Caused by: javax.net.ssl.SSLHandshakeException: Handshake failed
        at com.android.org.conscrypt.OpenSSLEngineImpl.unwrap(OpenSSLEngineImpl.java:436)
        at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:1006)
        at org.glassfish.grizzly.ssl.SSLConnectionContext.unwrap(SSLConnectionContext.java:172)
        at org.glassfish.grizzly.ssl.SSLUtils.handshakeUnwrap(SSLUtils.java:263)
        at org.glassfish.grizzly.ssl.SSLBaseFilter.doHandshakeStep(SSLBaseFilter.java:603)
        at org.glassfish.grizzly.ssl.SSLFilter.doHandshakeStep(SSLFilter.java:312)
        at org.glassfish.grizzly.ssl.SSLBaseFilter.doHandshakeStep(SSLBaseFilter.java:552)
        at org.glassfish.grizzly.ssl.SSLBaseFilter.handleRead(SSLBaseFilter.java:273)
        at org.glassfish.grizzly.filterchain.ExecutorResolver.execute(ExecutorResolver.java:119)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
        at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
        at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
        at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access0(WorkerThreadIOStrategy.java:56)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
at java.lang.Thread.run(Thread.java:818)
 Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0xa1f34200: Failure in SSL library, usually a protocol error
error:1408E0F4:SSL routines:SSL3_GET_MESSAGE:unexpected message (external/openssl/ssl/s3_both.c:498 0xac526e61:0x00000000)
        at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake_bio(Native Method)
        at com.android.org.conscrypt.OpenSSLEngineImpl.unwrap(OpenSSLEngineImpl.java:423)

Я думаю, что проблема заключается в TLS или наборах шифров из-за изменения в Android 5.0 Lollipop, а не с сертификатами, потому что другие клиенты подключаются, но я не могу придумать, как сказать, что происходит на стороне клиента соединения, потому что отладка SSL не поддерживается на Android. проблема, вероятно, очень похожа на этот, который также еще не разрешен, но предполагает, что проблема связана с наборами шифров. Ошибки Android 88313 81603 разработчик-предварительный просмотр-1989 похоже, что реализация Android верна, но конфигурация сервера или реализация наборов шифров не могут быть.

Я установил следующие наборы шифров сервера

server.ssl.ciphers = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA

в частности, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA находится на список поддерживаемых протоколов для Android для API 11+.

Я проверил, что сервер поддерживает это

openssl s_client -connect server:port

возвращает

SSL-Session:
Protocol  : TLSv1.2
Cipher    : ECDHE-RSA-AES128-SHA

существует небольшое несоответствие в именах между openssl и java, но документация openssl говорит Это же шифровальный набор.

мой сервер поддерживает и согласовывает сначала набор шифров с клиентом openssl, совместимым с Android 5.0. Я ожидаю, что Android 5.0 подключится без проблем, но это не удается.

кто-нибудь успешно подключил Android 5.0 безопасные подключения websocket к Tomcat? Не существует шифров, которые, как известно, работают? Есть ли способ отладить реализацию SSL на стороне клиента Android?


обновление

сеть результаты трассировки:

SYN -->
<-- SYN, ACK
ACK -->
<-- Data
ACK -->
<-- certificates, SSL/TLS params? 1
<-- 2
<-- 3
<-- 4
ACK --> 
ACK --> 
ACK --> 
FIN(!), ACK --> 

когда устройство Android 5.0 (Nexus 5) получает информацию о сертификате сервера, отправленную в 4-5 пакетах, оно отвечает переменным числом (2-4) ACKs, затем FIN, ACK. В успешной трассировке клиент не отправляет FIN. Клиенту Android 5 не нравится то, что он получает от сервера.

для сбоя информация об отладке SSL сервера говорит:

http-nio-8080-exec-10, called closeOutbound()
http-nio-8080-exec-10, closeOutboundInternal()
http-nio-8080-exec-10, SEND TLSv1.2 ALERT:  warning, description = close_notify
http-nio-8080-exec-10, WRITE: TLSv1.2 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 03 00 02 01 00 

обновление 2

вот голые кости Tyrus Android приложение для использования

package edu.umd.mindlab.androidssldebug;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import org.glassfish.tyrus.client.ClientManager;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.net.URI;

import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;

@ClientEndpoint
public class MainActivity extends ActionBarActivity {
    public static final String TAG = "edu.umd.mindlab.androidssldebug";
    final Object annotatedClientEndpoint = this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart(){
        super.onStart();
        final Object annotatedClientEndpoint = this;
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    URI connectionURI = new URI("wss://mind7.cs.umd.edu:8080/test");
                    ClientManager client = ClientManager.createClient();
                    Object clientEndpoint = annotatedClientEndpoint;
                    client.connectToServer(clientEndpoint, connectionURI);
                }
                catch(Exception e){
                    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
                    PrintStream printStream = new PrintStream(byteStream);
                    e.printStackTrace(printStream);
                    final String message = byteStream.toString();
                    Log.e(TAG, message);
                    e.printStackTrace();
                    runOnUiThread(new Runnable() {
                        public void run() {
                            TextView outputTextView = (TextView) findViewById(R.id.outputTextView);
                            outputTextView.setText(message);
                        }
                    });
                }
            }
        }).start();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @OnOpen
    public void onOpen(Session session) {
        Log.i(TAG, "opened");
        runOnUiThread(new Runnable() {
            public void run() {
                TextView outputTextView = (TextView) findViewById(R.id.outputTextView);
                outputTextView.setText("opened");
            }
        });

    }

    @OnMessage
    public void onMessage(String message, Session session) {
        Log.i(TAG, "message: " + message);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        Log.i(TAG, "close: " + closeReason.toString() );
    }

    @OnError
    public void onError(Session session, Throwable t) {
        final String message = "error: " + t.toString();
        Log.e(TAG, message);
        runOnUiThread(new Runnable() {
            public void run() {
                TextView outputTextView = (TextView) findViewById(R.id.outputTextView);
                outputTextView.setText(message);
            }
        });
    }

}

3 ответов


предлагаемое исправление в Тирус-402 устраняющее эту. Я открыл соответствующую ошибку Grizzly GRIZZLY-1827, которая имеет соответствующий патч.

обновление: ошибка гризли-1827 исправлено.


error:1408E0F4:SSL routines:SSL3_GET_MESSAGE:unexpected message (external/openssl/ssl/s3_both.c:498 0xac526e61:0x00000000)
        at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake_bio(Native Method)
        at com.android.org.conscrypt.OpenSSLEngineImpl.unwrap(OpenSSLEngineImpl.java:423)

0x1408E0F4 это:

$ openssl errstr 0x1408E0F4
error:1408E0F4:SSL routines:SSL3_GET_MESSAGE:unexpected message

он появляется в источниках OpenSSL в нескольких местах:

$ cd openssl-1.0.1l
$ grep -R SSL3_GET_MESSAGE *
ssl/s3_both.c:          SSLerr(SSL_F_SSL3_GET_MESSAGE,SSL_R_UNEXPECTED_MESSAGE);
ssl/s3_both.c:          SSLerr(SSL_F_SSL3_GET_MESSAGE,SSL_R_UNEXPECTED_MESSAGE);
ssl/s3_both.c:          SSLerr(SSL_F_SSL3_GET_MESSAGE,SSL_R_EXCESSIVE_MESSAGE_SIZE);
ssl/s3_both.c:          SSLerr(SSL_F_SSL3_GET_MESSAGE,SSL_R_EXCESSIVE_MESSAGE_SIZE);
ssl/s3_both.c:          SSLerr(SSL_F_SSL3_GET_MESSAGE,ERR_R_BUF_LIB);

вот код, который, я считаю, вызывает проблемы (номера строк изменились, и SSLerr составляет 491):

/* Obtain handshake message of message type 'mt' (any if mt == -1),
 * maximum acceptable body length 'max'.
 * The first four bytes (msg_type and length) are read in state 'st1',
 * the body is read in state 'stn'.
 */
long ssl3_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
    {
    ...

    /* s->init_num == 4 */
    if ((mt >= 0) && (*p != mt))
        {
        al=SSL_AD_UNEXPECTED_MESSAGE;
        SSLerr(SSL_F_SSL3_GET_MESSAGE,SSL_R_UNEXPECTED_MESSAGE);
        goto f_err;
        }
    ...

но я не уверен, что причины этой проблемы. См. этот вопрос в списке пользователей OpenSSL по адресу SSL_F_SSL3_GET_MESSAGE и SSL_R_UNEXPECTED_MESSAGE.

редактировать: согласно источнику Android для s3_both.c, это код, который вызывает проблему.

-----

OK, глядя на файл successful.pcap и unsuccessful.pcap, хороший клиент использует TLS 1.0, в то время как плохой клиент использует TLS 1.2. Но я не вижу ничего оскорбительного, что заставило бы клиента закрыть соединение при обработке четырех сообщений (сервер Привет, Сертификат, обмен ключами сервера, сервер Hello Done) в записи.

-----

на основе ServerKeyExchange сообщение:

enter image description here

сервер выбрал предложение клиента secp521r1. Вы можете использовать secp256. Это большинство interoperable прямо сейчас. Также смотрите ограниченная поддержка эллиптической кривой в rhel / centos / redhat openssl робастная достаточно?.

-----

OpenSSL 1.0.1 e FIPS, используемый сервером, столкнулся с несколькими проблемами. См., например:

если возможно, вы можете обновить его до чего-то нового.

-----

есть ли способ отладка реализации SSL на стороне клиента Android?

Я думаю, что это простой вопрос. Используйте пользовательский SSLSocketFactory как SSLSocketFactoryEx. Это позволит вам попробовать различные протоколы, комплекты шифров и настройки. Но это метод проб и ошибок.

в противном случае вам нужно будет захватить копию исходного кода OpenSSL, используемого Android 5.0 (включая патчи). Я не знаю, как получить это и убедиться, что он строится как mainline OpenSSL (эффективно, вам нужно build s_client использование источников Android С отладочной информации).

это может быть полезно: OpenSSL на Android. Судя по внешнему виду различий, Android использует OpenSSL 1.0.0. (Некоторые из патчей в patch/ каталог специально вызвать 1.0.0 b).


подтверждено, что это вызвано Android 5.0 ошибка. В настоящее время мне неясно, есть ли проблема в Tyrus websocket или Grizzly.

Читайте также: 93740 и предварительный просмотр 328.