Я пытаюсь использовать спокойный ws с базовой аутентификацией. Я не импортировал сертификат в свое хранилище ключей. Когда я использую плагин Chrome Advance Rest client для его тестирования (используя базовую аутентификацию с именем пользователя в кодировке base64: pass). Я вижу ответ. Все идет нормально. Но когда я разрабатываю код Java для использования этого ws, я получаю сбой подтверждения SSL:

org.springframework.web.client.ResourceAccessException: I/O error: 
Received fatal alert: handshake_failure; nested exception is
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:453)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:401)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:377)
at test.Rest.main(Rest.java:37) Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.Alerts.getSSLException(Unknown Source)

Мой вопрос: если проблема в том, что я не импортировал сертификат в свое хранилище ключей, то код Java и плагин не должны работать вместе. Здесь плагин работает, а мой код - нет. Какова причина? Что-то не так с моим кодом?

Сильфон это мой код

RestTemplate restTemplate = new RestTemplate();         
 String plainCreds = "username:pass"; 
 byte[] plainCredsBytes = plainCreds.getBytes(Charset.forName("US-ASCII") );     
 byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); 
 String base64Creds = new String(base64CredsBytes);

 HttpHeaders headers = new HttpHeaders(); 
 headers.add("Authorization", "Basic " + base64Creds);

 ResponseEntity<String> response =  
          restTemplate.exchange("https://url",HttpMethod.GET,new  
                                  HttpEntity(headers),String.class);

Вот ссылка на файл журнала: (Я заменил свое имя сервера на XXXXXX) http://www.filedropper.com/ssllog

После запуска: openssl s_client -showcerts -tls1 -connect host: port

WARNING: can't open config file: /usr/local/ssl/openssl.cnf
CONNECTED(00000164)
8088:error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number:.\ssl\s3_pkt.c:362:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 5 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1
    Cipher    : 0000
    Session-ID:
    Session-ID-ctx:
    Master-Key:
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1452011344
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---

И это результат, когда я запускаю команду openssl s_client -connect server: port

WARNING: can't open config file: /usr/local/ssl/openssl.cnf
CONNECTED(00000164)
depth=0 C = US, ST = "XXXXXX", L = XXXXXX, O = XXXXXX, OU = xxxxx, CN = XXXXXXXXX.test.intranet, emailAddress = xxxxx@xxxxx
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = US, ST = "XXXXXXX ", L = XXXXX, O = XXXXXX, OU = xxxxxx, CN = XXXXXXXXX.test.intranet, emailAddress = xxxxx@xxxxx
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/C=US/ST=XXXXXXX /L=XXXXX/O=XXXXXX/OU=XXXXX/CN=XXXXXX.test.intranet/emailAddress=xxxxx@xxxxx
   i:/DC=intranet/DC=xxxx/CN=XXXXXX DEV Issuing CA
---
Server certificate
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXX.....
-----END CERTIFICATE-----
subject=/C=US/ST=XXXXXXX /L=XXXXX/O=XXXXXX/OU=XXXXX/CN=XXXXXX.test.intranet/emailAddress=xxxxx@xxxxx
issuer=/DC=intranet/DC=XXX/CN=XXXXX DEV Issuing CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: XXXXX, P-256, 256 bits
---
SSL handshake has read 1895 bytes and written 443 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 568BF22A5CDBF103155264BBC056B272168AE0777CBC10F055705EB2DD907E5A
    Session-ID-ctx:
    Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1452012074
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---
read:errno=0
8
David 2 Янв 2016 в 09:44

2 ответа

Лучший ответ

Из журнала javax.net.debug я вижу, что вы используете Java 7, а клиент разрешает TLSv1. Из openssl вывод, что ваш сервер не поддерживает TLSv1.

TLS вер. 1.1 и 1.2 отключены в Java 7 по умолчанию.

Хотя SunJSSE в выпуске Java SE 7 поддерживает TLS 1.1 и TLS 1.2, ни одна из версий не включена по умолчанию для клиентских подключений. Некоторые серверы неправильно реализуют прямую совместимость и отказываются взаимодействовать с клиентами TLS 1.1 или TLS 1.2. Для обеспечения совместимости SunJSSE не включает TLS 1.1 или TLS 1.2 по умолчанию для клиентских подключений.

Включите TLSv1.1 и TLSv1.2 одним из следующих способов:

  1. Аргумент JVM:

    -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
    
  2. Или установите то же свойство из кода Java:

    System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1");
    
  3. Или установите файлы политик JCE Unlimited Strength для Java 7.. Я не на 100% уверен, что этот единственный шаг решит проблему, хотя всегда стоит устанавливать JCE, поскольку он позволяет JVM использовать более сильные версии существующих алгоритмов.

ОБНОВЛЕНИЕ 29 сентября 2016 г. :

Порядок протоколов изменен с лучшего на худший (TLS версии 1.2 на 1) в вариантах 1 и 2.

11
Michal Foksa 5 Фев 2020 в 03:12

Если вы используете Java 7, вам необходимо явно указать Java использовать протокол TLSv1.2. Вот пример использования конфигурации Spring XML.

***In any Spring bean (i.e. a controller)***

import org.apache.http.client.HttpClient;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import javax.net.ssl.SSLContext;

@Bean(name="client")
public HttpClient make() throws NoSuchAlgorithmException, KeyManagementException {
    SSLContext sslContext = SSLContexts.custom().build();
    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
            new String[]{"TLSv1.2", "TLSv1.1"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    return HttpClients.custom()
            .setSSLSocketFactory(sslConnectionSocketFactory)
            .build();
}

***In XML configuration files***

<bean id="factory"
      class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
    <property name="httpClient" ref="client"/>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
    <property name="requestFactory" ref="factory"/>
</bean>

Вы могли бы сделать то же самое без XML примерно так:

RestTemplate restTemplate;

public HttpClient getHttpClient() throws NoSuchAlgorithmException, KeyManagementException {
    SSLContext sslContext = SSLContexts.custom().build();
    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
            new String[]{"TLSv1.2", "TLSv1.1"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    return HttpClients.custom()
            .setSSLSocketFactory(sslConnectionSocketFactory)
            .build();
}

public void setUp() throws Exception {
    restTemplate = new RestTemplate(
            new HttpComponentsClientHttpRequestFactory(
                    getHttpClient()));
    ...
}
2
lmiguelmh 28 Апр 2017 в 23:04