2013-03-21 20 views
6

handshake_failure Mam Tomcat7 web-serwer, który próbowałem skonfigurować akceptować bezpieczne połączenia przez dodanie tego złącza do pliku server.xml:: odebrane krytyczny alertuSSLHandshakeException: podczas ustawiania szyfrów na Tomcat 7 serwerze

<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"/> 

I używam certyfikatu z podpisem własnym generowany za pomocą polecenia:

%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 

po stronie klienta mam aplikacji sprężynowy, który łączy się z serwerem za pomocą RestTemplate. Na starcie kontekstowego aplikacji I initalize instancji restTemplate ten sposób:

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

Klasa MyCustomClientHttpRequestFactory wygląda tak:

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

Aż tu wszystko działało w porządku. Po umieszczeniu punktu przerwania w klasie SslThumbprintVerifier kod dotarł do tego punktu, a także do klasy NullHostNameVerifier. Zostało to przetestowane w produkcji i działało świetnie.

Teraz chciałem przedłużyć bezpieczeństwo poprzez ograniczenie szyfrów i dodałem tę właściwość do złącza przedstawiłem na początku:

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" 

Teraz kiedy biegnę kod klienta otrzymuję ten wyjątek:

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

Dzieje się tak w metodzie restTemplate.doExecute(). Kod ten nie dociera do klasy weryfikatora odcisków palców ani do klasy weryfikatora nazw hostów, jak przed dodaniem szyfru.

W debugowania Sprawdziłem ctx.getSocketFactory().getSupportedCipherSuites() które wykazały:

[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] 

a także sprawdzone 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] 

i wreszcie 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] 

O ile mi zrozumieć, jeśli istnieje przecięcie między sup klienta portowane/włączone pakiety szyfrów i obsługiwane przez serwer pakiety szyfrów powinny działać (mamy takie skrzyżowanie z np. SSL_RSA_WITH_RC4_128_SHA). A jednak dostaję tego błędu.

W następnym kroku dodanej tego parametru Java:

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

Dziennik pokazał tylko klient-hello, bez obciążania serwera cześć odpowiedzi:

 

[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 
 

Wyjmując szyfry od server.xml działa ponownie. Wspomnę również, że testuję zarówno serwer, jak i klienta na tej samej maszynie. Próbowałem ustawić IP serwera w żądaniu klienta na "localhost" i na rzeczywistą maszynę ip i nie działało w obu przypadkach.Ponadto, miałem ten serwer uruchomiony na innym komputerze z Linuksem (magazyn kluczy był generowany na maszynie Linux-owej ze ścieżką linuksową oczywiście) i nadal - działa bez szyfrów i przestaje działać z szyframi.

Odpowiedz

12

Cóż, problem został rozwiązany. Wygląda na to, że utworzenie samopodpisanego certyfikatu za pomocą keytool, bez podania parametru -keyalg powoduje, że algorytm par kluczy jest domyślnie ustawiony na DSA. Żaden z moich szyfrów nie zawierał algorytmu DSA. W tym przypadku, mimo że klient i serwer posiadały przecięcie między pakietami szyfrów, żaden z nich nie był odpowiedni dla kluczowego algorytmu.

Dodanie -keyalg RSA podczas generowania magazynu kluczy rozwiązało problem.