SSLHandshakeException: получено фатальное предупреждение: handshake_failure при установке шифров на сервере tomcat 7

У меня есть веб-сервер Tomcat7, который я пытался настроить для приема безопасных соединений, добавив этот коннектор в файл server.xml:

<Connector SSLEnabled="true"
           acceptCount="100"
           connectionTimeout="20000"
           executor="tomcatThreadPool"
           keyAlias="server"
           keystoreFile="c:\opt\engine\conf\tc.keystore"
           keystorePass="o39UI12z"
           maxKeepAliveRequests="15"
           port="8443"
           protocol="HTTP/1.1"
           redirectPort="8443"
           scheme="https"
           secure="true"
           sslProtocol="TLS"/>

Я использую самозаверяющий сертификат, созданный с помощью этой команды:

%JAVA_HOME%/bin/keytool -genkeypair -keystore c:\opt\engine\conf\tc.keystore -storepass o39UI12z-keypass o39UI12z-dname "cn=Company, ou=Company, o=Com, c=US" -alias server -validity 36500

На стороне клиента у меня есть приложение Spring, которое подключается к серверу с помощью RestTemplate. При запуске контекста приложения я инициализирую экземпляр restTemplate следующим образом:

final ClientHttpRequestFactory clientHttpRequestFactory =
        new MyCustomClientHttpRequestFactory(new NullHostNameVerifier(), serverInfo);
    restTemplate.setRequestFactory(clientHttpRequestFactory);

Класс MyCustomClientHttpRequestFactory выглядит так:

public class MyCustomClientHttpRequestFactory  extends SimpleClientHttpRequestFactory {

private static final Logger LOGGER = LoggerFactory
    .getLogger(MyCustomClientHttpRequestFactory.class);

private final HostnameVerifier hostNameVerifier;
private final ServerInfo serverInfo;

public MyCustomClientHttpRequestFactory (final HostnameVerifier hostNameVerifier,
    final ServerInfo serverInfo) {
    this.hostNameVerifier = hostNameVerifier;
    this.serverInfo = serverInfo;
}

@Override
protected void prepareConnection(final HttpURLConnection connection, final String httpMethod)
    throws IOException {
    if (connection instanceof HttpsURLConnection) {
        ((HttpsURLConnection) connection).setHostnameVerifier(hostNameVerifier);
        ((HttpsURLConnection) connection).setSSLSocketFactory(initSSLContext()
            .getSocketFactory());
    }
    super.prepareConnection(connection, httpMethod);
}

private SSLContext initSSLContext() {
    try {
        System.setProperty("https.protocols", "TLSv1");

        // Set ssl trust manager. Verify against our server thumbprint
        final SSLContext ctx = SSLContext.getInstance("TLSv1");
        final SslThumbprintVerifier verifier = new SslThumbprintVerifier(serverInfo);
        final ThumbprintTrustManager thumbPrintTrustManager =
            new ThumbprintTrustManager(null, verifier);
        ctx.init(null, new TrustManager[] { thumbPrintTrustManager }, null);
        return ctx;
    } catch (final Exception ex) {
        LOGGER.error(
            "An exception was thrown while trying to initialize HTTP security manager.", ex);
        return null;
    }
}

До сих пор все работало нормально. Когда я поставил точку останова в классе SslThumbprintVerifier, код дошел до этой точки, а также до класса NullHostNameVerifier. Это было проверено в производстве и отлично работало.

Теперь я хотел повысить безопасность, ограничив наборы шифров, и я добавил это свойство к соединителю, который я представил в начале:

ciphers="TLS_KRB5_WITH_RC4_128_SHA,SSL_RSA_WITH_RC4_128_SHA,SSL_DHE_RSA_WITH_AES_256_CBC_SHA,SSL_RSA_WITH_AES_256_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_AES_128_CBC_SHA,SSL_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_RC4_128_SHA,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_KRB5_WITH_3DES_EDE_CBC_SHA"

Теперь, когда я запускаю клиентский код, я получаю это исключение:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

Это происходит в методе restTemplate.doExecute(). Код не достигает ни класса верификатора отпечатка, ни класса верификатора имени хоста, как это было до добавления шифров.

В отладке я проверил ctx.getSocketFactory().getSupportedCipherSuites(), который показал:

[SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, SSL_RSA_WITH_NULL_MD5, SSL_RSA_WITH_NULL_SHA, SSL_DH_anon_WITH_RC4_128_MD5, TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_DH_anon_WITH_AES_256_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, SSL_DH_anon_WITH_DES_CBC_SHA, SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, TLS_KRB5_WITH_RC4_128_SHA, TLS_KRB5_WITH_RC4_128_MD5, TLS_KRB5_WITH_3DES_EDE_CBC_SHA, TLS_KRB5_WITH_3DES_EDE_CBC_MD5, TLS_KRB5_WITH_DES_CBC_SHA, TLS_KRB5_WITH_DES_CBC_MD5, TLS_KRB5_EXPORT_WITH_RC4_40_SHA, TLS_KRB5_EXPORT_WITH_RC4_40_MD5, TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5]

а также проверил ctx.getSocketFactory().getDefaultCipherSuites():

[SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]

и, наконец, ctx.getSupportedSSLParameters().getCipherSuites():

[SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, SSL_RSA_WITH_NULL_MD5, SSL_RSA_WITH_NULL_SHA, SSL_DH_anon_WITH_RC4_128_MD5, TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_DH_anon_WITH_AES_256_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, SSL_DH_anon_WITH_DES_CBC_SHA, SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, TLS_KRB5_WITH_RC4_128_SHA, TLS_KRB5_WITH_RC4_128_MD5, TLS_KRB5_WITH_3DES_EDE_CBC_SHA, TLS_KRB5_WITH_3DES_EDE_CBC_MD5, TLS_KRB5_WITH_DES_CBC_SHA, TLS_KRB5_WITH_DES_CBC_MD5, TLS_KRB5_EXPORT_WITH_RC4_40_SHA, TLS_KRB5_EXPORT_WITH_RC4_40_MD5, TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5]

Насколько я понимаю, если есть пересечение между поддерживаемыми/включенными шифровальными наборами клиента и поддерживаемыми сервером шифровальными наборами, это должно работать (у нас есть такое пересечение с SSL_RSA_WITH_RC4_128_SHA, например). И все же я получаю эту ошибку.

На следующем шаге я добавил этот параметр Java:

-Djavax.net.debug=ssl,handshake,failure

Журнал показал только клиентское приветствие без ответа серверного приветствия:


[2013-03-20 15:29:51.315] [INFO ] data-service-pool-37          System.out                                                        trigger seeding of SecureRandom 
[2013-03-20 15:29:51.315] [INFO ] data-service-pool-37          System.out                                                        done seeding SecureRandom 
[2013-03-20 15:30:38.894] [INFO ] data-service-pool-37          System.out                                                        Allow unsafe renegotiation: false 
[2013-03-20 15:30:38.894] [INFO ] data-service-pool-37          System.out                                                        Allow legacy hello messages: true 
[2013-03-20 15:30:38.894] [INFO ] data-service-pool-37          System.out                                                        Is initial handshake: true 
[2013-03-20 15:30:38.894] [INFO ] data-service-pool-37          System.out                                                        Is secure renegotiation: false 
[2013-03-20 15:30:38.894] [INFO ] data-service-pool-37          System.out                                                        %% No cached client session 
[2013-03-20 15:30:38.894] [INFO ] data-service-pool-37          System.out                                                        *** ClientHello, TLSv1 
[2013-03-20 15:30:38.941] [INFO ] data-service-pool-37          System.out                                                        RandomCookie:  GMT: 1363720446 bytes = { 99, 249, 173, 214, 110, 82, 58, 52, 189, 92, 74, 169, 133, 128, 250, 109, 160, 64, 112, 253, 50, 160, 255, 196, 85, 93, 33, 172 } 
[2013-03-20 15:30:38.941] [INFO ] data-service-pool-37          System.out                                                        Session ID:  {} 
[2013-03-20 15:30:38.941] [INFO ] data-service-pool-37          System.out                                                        Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV] 
[2013-03-20 15:30:38.956] [INFO ] data-service-pool-37          System.out                                                        Compression Methods:  { 0 } 
[2013-03-20 15:30:38.956] [INFO ] data-service-pool-37          System.out                                                        *** 
[2013-03-20 15:30:38.956] [INFO ] data-service-pool-37          System.out                                                        data-service-pool-37, WRITE: TLSv1 Handshake, length = 81 
[2013-03-20 15:30:38.956] [INFO ] data-service-pool-37          System.out                                                        data-service-pool-37, READ: TLSv1 Alert, length = 2 
[2013-03-20 15:30:38.956] [INFO ] data-service-pool-37          System.out                                                        data-service-pool-37, RECV TLSv1 ALERT:  fatal, handshake_failure 
[2013-03-20 15:30:38.972] [INFO ] data-service-pool-37          System.out                                                        data-service-pool-37, called closeSocket() 
[2013-03-20 15:30:38.972] [INFO ] data-service-pool-37          System.out                                                        data-service-pool-37, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 

При удалении шифров из server.xml снова работает. Также я упомяну, что тестирую и сервер, и клиент на одном компьютере. Я попытался установить IP-адрес сервера в клиентском запросе на «localhost» и фактический IP-адрес компьютера, и это не помогло. не работает в обоих случаях. Кроме того, у меня этот сервер работал на другой машине с Linux (конечно, хранилище ключей было сгенерировано на машине с Linux с путем Linux), и все же он работает без шифров и перестает работать с шифрами.


person Avi    schedule 21.03.2013    source источник


Ответы (1)


Ну, я решил эту проблему. Похоже, что при создании самозаверяющего сертификата с помощью keytool без указания параметра -keyalg алгоритм пары ключей по умолчанию использует DSA. Ни один из моих наборов шифров не включал алгоритм DSA. В этом случае, хотя у клиента и сервера были пересечения между их наборами шифров, ни один из них не подходил для ключевого алгоритма.

Добавление -keyalg RSA при создании хранилища ключей решило проблему.

person Avi    schedule 21.03.2013