5

Próbuję spożywać spokojny ws z podstawowym uwierzytelnieniem. Nie zaimportowałem żadnego certyfikatu do mojego magazynu kluczy. Kiedy używam wtyczki chrome Advance Rest client, aby ją przetestować (używając podstawowego uwierzytelniania z zakodowaną w base64 nazwą użytkownika: pass). Widzę odpowiedź z powrotem. Jak na razie dobrze. Ale kiedy rozwijać kodu Java spożywać ten WS, mam SSL awarię handsake:Spring RestTemplate: Niepoprawna uścisk SSL 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) 

Moje pytanie brzmi: czy problem jest, bo nie importować cert do mojego magazynu kluczy, a następnie zarówno kodu Java i wtyczki powinny nie działają razem. Tutaj wtyczka działa, ale mój kod nie. Jaki jest tego powód? Coś jest nie tak z moim kodem?

Bellow jest kod

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); 

Oto link do pliku dziennika: (muszę wymienić moje imię przez serwer-XXXXXX) http://www.filedropper.com/ssllog

Po uruchomieniu: openssl s_client -showcerts -tls1 - połączyć 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) 
--- 

i to jest wyjście, gdy uruchamiam polecenie openssl s_client -connect serwer: 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 = [email protected] 
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 = [email protected] 
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/[email protected] 
    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/[email protected] 
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 
+0

Chrome i aplikacja może użyć innego języka Java, a więc mają różne ustawienia. Nawet jeśli Java jest taka sama, istnieje milion powodów, dla których nie działa. Jednak to dobry początek, że działa z Chrome. Włącz debugowanie SSL JVM, np .: dodaj '-Djavax.net.debug = ssl: handshake: verbose' i dodaj dane wyjściowe do twojego pytania, Dodatkowe argumenty debugowania są dostępne tutaj http://docs.oracle.com/javase/7/ docs/technotes/guides/security/jsse/JSSERefGuide.html # Debugowanie –

+0

Chciałbym zbadać twój serwer, ponieważ dziennik nie zawiera wiadomości 'ServerHello', tak jakby twój serwer nie był uruchomiony SSL. Jeśli masz openssl, spróbuj połączyć się z 'openssl s_client -showcerts -tls1 -connect [twój_host]: [port]'. Zobacz także http://www.smartjava.org/content/how-analyze-java-ssl-errors –

+0

Dzięki Michał. Dodałem dane wyjściowe do uruchamiania openssl. Proszę pomóż mi. Jestem nowicjuszem w sprawach SSL i certyfikatów – David

Odpowiedz

7

Od javax.net.debug log Widzę, że używasz Java 7, a klient rozwiązuje protokół TLSv1. Od openssl wyjście, że twój serwer nie obsługuje TLSv1.

TLS ver. 1.1 and 1.2 are disabled in Java 7 by default.

Chociaż SunJSSE w wersji Java SE 7 obsługuje TLS 1.1 i TLS 1.2 , ani wersja jest domyślnie włączona dla połączeń klienckich. Niektóre serwery nie implementują poprawnie zgodności do przodu i odmawiają rozmowy z klientami TLS 1.1 lub TLS 1.2. W celu zapewnienia interoperacyjności, SunJSSE nie włącza domyślnie TLS 1.1 lub TLS 1.2 dla połączeń klienta .

Włącz TLSv1.1 i TLSv1.2 poprzez:

  1. JVM argumentem:

    -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1 
    
  2. Albo ustawić tę samą właściwość od kodu Java:

    System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,TLSv1"); 
    
  3. Lub zainstaluj JCE Unlimited Strength policy files for Java 7. Nie jestem w 100% pewny, czy ten pojedynczy krok rozwiązałby problem, chociaż zawsze warto zainstalować JCE, podczas gdy pozwala JVM używać silniejszych wersji istniejących algorytmów.

UPDATE 29/Sep/2016:

Zmieniono kolejność protokołów z lepiej gorzej od TLS (ver 1.2. 1) w opcji 1 i 2.

+0

Dziękuję Michałowi za pomoc pacjentowi. Dowiadujemy się, że serwer obsługujący resztę ws korzysta z jdk8, a mój klient używa jdk7. Mamy więc serwer downgrade do jdk7. Muszę również wymusić na jdku użycie TSLv1.2 za pomocą -Dhttps.protocols = TLSv1, TLSv1.1, TLSv1.2. Już działa. – David

+0

przed próbą użycia -Dhttps.protocols = TLSv1, TLSv1.1, TLSv1.2. Próbowałem również zainstalować politykę JCE Unlimited Strength dla jdk7. Wymieniłem pliki w jdk/jre/lib/security na pliki w pliku zip. Ale to nie działa. – David

+0

Serdecznie witamy Davidzie i dziękujemy za Wasze opinie i uznanie. –

1

Jeśli używasz Java 7, musisz wyraźnie powiedzieć Java, aby używała protokołu TLSv1.2. Oto przykład użycia konfiguracji 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> 

Można zrobić to samo bez XML z czymś takim:

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())); 
    ... 
}