Alerte fatale: handshake_failure via SSLHandshakeException

J’ai un problème avec la connexion SSL autorisée. J’ai créé l’action Struts qui se connecte à un serveur externe avec un certificate SSL autorisé par le client. Dans mon action, j’essaye d’envoyer des données au serveur de la banque, mais sans succès, car le serveur a l’erreur suivante:

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

Ma méthode de ma classe Action qui envoie des données au serveur

 //Getting external IP from host URL whatismyip = new URL("http://automation.whatismyip.com/n09230945.asp"); BufferedReader inIP = new BufferedReader(new InputStreamReader(whatismyip.openStream())); Ssortingng IPStr = inIP.readLine(); //IP as a Ssortingng Merchant merchant; System.out.println("amount: " + amount + ", currency: " + currency + ", clientIp: " + IPStr + ", description: " + description); try { merchant = new Merchant(context.getRealPath("/") + "merchant.properties"); } catch (ConfigurationException e) { Logger.getLogger(HomeAction.class.getName()).log(Level.INFO, "message", e); System.err.println("error: " + e.getMessage()); return ERROR; } Ssortingng result = merchant.sendTransData(amount, currency, IPStr, description); System.out.println("result: " + result); return SUCCESS; 

Mon fichier merchant.properties:

 bank.server.url=https://-servernameandport-/ https.cipher=-cipher- keystore.file=-key-.jks keystore.type=JKS keystore.password=-password- ecomm.server.version=2.0 encoding.source=UTF-8 encoding.native=UTF-8 

Pour la première fois, j’ai pensé qu’il s’agissait d’un problème de certificate, je l’ai converti de .pfx à .jks, mais j’ai la même erreur, sans aucune modification.

    L’échec de la poignée de main pourrait être dû à diverses raisons:

    • Des suites de chiffrement incompatibles utilisées par le client et le serveur. Cela nécessiterait que le client utilise (ou active) une suite de chiffrement prise en charge par le serveur.
    • Versions incompatibles de SSL en cours d’utilisation (le serveur peut accepter uniquement TLS v1, alors que le client ne peut utiliser que SSL v3). Là encore, le client devra s’assurer qu’il utilise une version compatible du protocole SSL / TLS.
    • Chemin de confiance incomplet pour le certificate du serveur; le certificate du serveur n’est probablement pas approuvé par le client. Cela entraînerait généralement une erreur plus verbeuse, mais c’est tout à fait possible. Le correctif consiste généralement à importer le certificate de l’autorité de certificateion du serveur dans le magasin de clés du client.
    • Le certificate est délivré pour un domaine différent. Encore une fois, cela aurait abouti à un message plus détaillé, mais j’indiquerai la solution au cas où cela en serait la cause. La résolution dans ce cas serait d’obtenir le serveur (il ne semble pas être le vôtre) d’utiliser le bon certificate.

    Comme l’échec sous-jacent ne peut pas être identifié, il est préférable d’activer l’ -Djavax.net.debug=all pour permettre le débogage de la connexion SSL établie. Lorsque le débogage est activé, vous pouvez identifier l’activité qui a échoué dans la négociation.

    Mettre à jour

    En fonction des détails maintenant disponibles, il semble que le problème soit dû à un chemin d’approbation de certificate incomplet entre le certificate émis sur le serveur et une autorité de certificateion racine. Dans la plupart des cas, cela est dû au fait que le certificate de l’autorité de certificateion racine est absent du magasin de clés de confiance, ce qui peut entraîner un chemin de confiance de certificate. le certificate est essentiellement non approuvé par le client. Les navigateurs peuvent présenter un avertissement afin que les utilisateurs puissent l’ignorer, mais il n’en va pas de même pour les clients SSL (tels que la classe HttpsURLConnection ou toute bibliothèque de client HTTP telle qu’Apache HttpComponents Client ).

    La plupart de ces classes / bibliothèques client s’appuient sur le magasin de confiance utilisé par la machine virtuelle Java pour la validation des certificates. Dans la plupart des cas, ce sera le fichier cacerts répertoire JRE_HOME / lib / security. Si l’emplacement du magasin de javax.net.ssl.trustStore de confiance a été spécifié à l’aide de la propriété système JVM javax.net.ssl.trustStore , le magasin de ce chemin est généralement celui utilisé par la bibliothèque cliente. Si vous avez un doute, jetez un coup d’œil à votre classe Merchant et déterminez la classe / bibliothèque utilisée pour établir la connexion.

    L’ajout de l’autorité de certificateion émetsortingce du serveur à ce magasin de confiance doit résoudre le problème. Vous pouvez vous référer à ma réponse sur une question connexe sur l’obtention d’outils à cette fin, mais l’ utilitaire keytool Java est suffisant à cette fin.

    Avertissement : Le magasin de clés de confiance est essentiellement la liste de toutes les autorités de certificateion auxquelles vous faites confiance. Si vous insérez un certificate qui n’appartient pas à une autorité de certificateion à laquelle vous ne faites pas confiance, les connexions SSL / TLS aux sites dont les certificates sont émis par cette entité peuvent être déchiffrées si la clé privée est disponible.

    Mise à jour n ° 2: Comprendre la sortie de la trace JSSE

    Le fichier de clés et les magasins de clés de confiance utilisés par la machine virtuelle Java sont généralement répertoriés au tout début, un peu comme suit:

     keyStore is : keyStore type is : jks keyStore provider is : init keystore init keymanager of type SunX509 trustStore is: C:\Java\jdk1.6.0_21\jre\lib\security\cacerts trustStore type is : jks trustStore provider is : 

    Si le fichier de clés certifiées est incorrect, vous devrez réimporter le certificate du serveur sur le bon ou reconfigurer le serveur pour utiliser celui qui est répertorié (non recommandé si vous disposez de plusieurs machines virtuelles Java et que celles-ci sont utilisées pour des serveurs différents). Besoins).

    Si vous voulez vérifier si la liste des certificates de confiance contient les certificates requirejs, il existe une section pour le même, qui commence par:

     adding as trusted cert: Subject: CN=blah, O=blah, C=blah Issuer: CN=biggerblah, O=biggerblah, C=biggerblah Algorithm: RSA; Serial number: yadda Valid from SomeDate until SomeDate 

    Vous devrez rechercher si l’autorité de certificateion du serveur est un sujet.

    Le processus de prise de contact aura quelques entrées saillantes (vous aurez besoin de connaître SSL pour les comprendre en détail, mais dans le but de déboguer le problème actuel, il suffira de savoir qu’un handshake_failure est généralement signalé dans ServerHello.

    1. ClientHello

    Une série d’entrées sera signalée lors de l’initialisation de la connexion. Le premier message envoyé par le client dans une installation de connexion SSL / TLS est le message ClientHello, généralement indiqué dans les journaux sous la forme:

     *** ClientHello, TLSv1 RandomCookie: GMT: 1291302508 bytes = { some byte array } Session ID: {} Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_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] Compression Methods: { 0 } *** 

    Notez les suites de chiffrement utilisées. Cela pourrait être en accord avec l’entrée dans votre fichier merchant.properties, car la même convention pourrait être utilisée par la bibliothèque de la banque. Si la convention utilisée est différente, il n’y a pas lieu de s’inquiéter, car ServerHello le précisera, si la suite de chiffrement est incompatible.

    2. ServerHello

    Le serveur répond avec un ServerHello, qui indiquera si la configuration de la connexion peut continuer. Les entrées dans les journaux sont généralement du type suivant:

     *** ServerHello, TLSv1 RandomCookie: GMT: 1291302499 bytes = { some byte array} Cipher Suite: SSL_RSA_WITH_RC4_128_SHA Compression Method: 0 *** 

    Notez la suite de chiffrement choisie. C’est la meilleure suite disponible pour le serveur et le client. Habituellement, la suite de chiffrement n’est pas spécifiée en cas d’erreur. Le certificate du serveur (et éventuellement la chaîne entière) est envoyé par le serveur et se trouve dans les entrées sous la forme:

     *** Certificate chain chain [0] = [ [ Version: V3 Subject: CN=server, O=server's org, L=server's location, ST =Server's state, C=Server's country Signature Algorithm: SHA1withRSA, OID = some identifer .... the rest of the certificatee *** 

    Si la vérification du certificate a réussi, vous trouverez une entrée similaire à:

     Found trusted certificatee: [ [ Version: V1 Subject: OU=Server's CA, O="Server's CA's company name", C=CA's country Signature Algorithm: SHA1withRSA, OID = some identifier 

    L’une des étapes ci-dessus n’aurait pas abouti, entraînant le handshake_failure, car le handshake est généralement terminé à ce stade (pas vraiment, mais les étapes suivantes de la prise de contact ne provoquent généralement pas un échec de handshake). Vous devez déterminer quelle étape a échoué et publier le message approprié en tant que mise à jour de la question (sauf si vous avez déjà compris le message et que vous savez quoi faire pour le résoudre).

    Je ne pense pas que cela résout le problème pour le premier intervenant, mais pour les googleurs qui viennent ici pour des réponses:


    Sur la mise à jour 51, java 1.8 interdisait [1] les chiffrements RC4 par défaut, comme on peut le voir sur la page Notes de version:

    Correctif: Interdire les suites de chiffrement RC4

    RC4 est maintenant considéré comme un chiffrement compromis.

    Les suites de chiffrement RC4 ont été supprimées de la liste des suites de chiffrement activées par défaut du client et du serveur dans l’implémentation Oracle JSSE. Ces suites de chiffrement peuvent toujours être activées par les SSLEngine.setEnabledCipherSuites() et SSLSocket.setEnabledCipherSuites() . Voir JDK-8077109 (non public).

    Si votre serveur a une préférence marquée pour ce chiffrement (ou n’utilise que ce chiffrement), cela peut déclencher une handshake_failure de handshake_failure sur Java.

    Vous pouvez tester la connexion au serveur en activant les chiffrements RC4 (essayez d’abord sans argument enabled pour voir si déclenche une handshake_failure , puis enabled :

     import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import java.io.*; import java.util.Arrays; /** Establish a SSL connection to a host and port, writes a byte and * prints the response. See * http://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services */ public class SSLRC4Poke { public static void main(Ssortingng[] args) { Ssortingng[] cyphers; if (args.length < 2) { System.out.println("Usage: "+SSLRC4Poke.class.getName()+"   enable"); System.exit(1); } try { SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(args[0], Integer.parseInt(args[1])); cyphers = sslsocketfactory.getSupportedCipherSuites(); if (args.length ==3){ sslsocket.setEnabledCipherSuites(new Ssortingng[]{ "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_WITH_RC4_128_MD5", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_WITH_RC4_128_MD5", "SSL_RSA_WITH_RC4_128_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_RSA_WITH_RC4_128_SHA", "TLS_ECDH_anon_WITH_RC4_128_SHA", "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "TLS_KRB5_WITH_RC4_128_MD5", "TLS_KRB5_WITH_RC4_128_SHA" }); } InputStream in = sslsocket.getInputStream(); OutputStream out = sslsocket.getOutputStream(); // Write a test byte to get a reaction :) out.write(1); while (in.available() > 0) { System.out.print(in.read()); } System.out.println("Successfully connected"); } catch (Exception exception) { exception.printStackTrace(); } } } 

    1 – https://www.java.com/en/download/faq/release_changes.xml

    Installer Java Cryptography Extension (JCE) Unlimited Strength ( pour JDK7 | pour JDK8 ) corrigera ce problème. Décompressez le fichier et suivez le readme pour l’installer.

    Cela peut également se produire lorsque le client doit présenter un certificate. Une fois que le serveur a répertorié la chaîne de certificates, les événements suivants peuvent se produire:

    3. Demande de certificate Le serveur émet une demande de certificate du client. La demande listera tous les certificates acceptés par le serveur.

     *** CertificateRequest Cert Types: RSA Cert Authorities:     ... the rest of the request *** ServerHelloDone 

    4. Chaîne de certificate client Il s’agit du certificate que le client envoie au serveur.

     *** Certificate chain chain [0] = [ [ Version: V3 Subject: EMAILADDRESS=client's email, CN=client, OU=client's ou, O=client's Org, L=client's location, ST=client's state, C=client's Country Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 ... the rest of the certificatee *** ClientKeyExchange, RSA PreMasterSecret, TLSv1 ... key exchange info 

    S’il n’y a pas de certificate dans la chaîne et que le serveur en a besoin, vous obtenez l’erreur de prise de contact ici. Une cause probable est que le chemin d’access à votre certificate n’a pas été trouvé.

    5. Vérification du certificate Le client demande au serveur de vérifier le certificate

     *** CertificateVerify ... payload of verify check 

    Cette étape ne se produira que si vous envoyez un certificate.

    6. Terminé Le serveur répondra par une réponse de vérification

     *** Finished verify_data: { 345, ... } 

    L’échec de la prise de contact pourrait être une implémentation de protocole TLSv1 boguée.

    Dans notre cas, cela a aidé java 7:

     java -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1 

    Le jvm négociera dans cet ordre. Les serveurs avec la dernière mise à jour feront 1.2, les bogues passeront à v1 et cela fonctionnera avec la v1 similaire dans java 7.

    J’ai cette erreur pendant que j’essayais d’utiliser JDK 1.7. Lorsque j’ai mis à jour mon JDK vers jdk1.8.0_66, tout a bien fonctionné.

    La solution la plus simple à ce problème pourrait donc être la mise à niveau de votre JDK et son bon fonctionnement.

    En supposant que vous utilisez les protocoles SSL / TLS appropriés, que vous keyStore correctement configuré votre keyStore et votre keyStore , et que vous avez confirmé que les certificates ne présentent aucun problème, vous devrez peut-être renforcer vos algorithmes de sécurité .

    Comme mentionné dans la réponse de Vineet , une des raisons possibles pour lesquelles vous recevez cette erreur est due à l’utilisation de suites de chiffrement incompatibles. En mettant à jour mes local_policy US_export_policy local_policy et US_export_policy dans le dossier de security mon JDK avec ceux fournis dans JCE (Java Cryptography Extension) , j’ai réussi à établir la liaison avec succès.

    Je rencontre le même problème aujourd’hui avec le client OkHttp pour obtenir une URL basée sur https. Cela était dû à la version du protocole Https et à la non-concordance de la méthode Cipher entre le côté serveur et le côté client .

    1) Vérifiez la version du protocole https et la méthode de chiffrement de votre site Web.

    openssl>s_client -connect your_website.com:443 -showcerts

    Vous obtiendrez de nombreuses informations détaillées, les informations clés sont répertoriées comme suit:

     SSL-Session: Protocol : TLSv1 Cipher : RC4-SHA 

    2) configurer votre client http, par exemple, dans le cas du client OkHttp :

     @Test() public void testHttpsByOkHttp() { ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_0) //protocol version .cipherSuites( CipherSuite.TLS_RSA_WITH_RC4_128_SHA, //cipher method CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) .build(); OkHttpClient client = new OkHttpClient(); client.setConnectionSpecs(Collections.singletonList(spec)); Request request = new Request.Builder().url("https://your_website.com/").build(); try { Response response = client.newCall(request).execute(); if(response.isSuccessful()){ logger.debug("result= {}", response.body().ssortingng()); } } catch (IOException e) { e.printStackTrace(); } } 

    Cela va donner ce que nous voulons.

    J’ai trouvé un serveur HTTPS qui a échoué de cette façon si mon processus client Java était configuré avec

     -Djsse.enableSNIExtension=false 

    La connexion a échoué avec handshake_failure après la fin réussie de ServerHello mais avant le démarrage du stream de données.

    Il n’y avait pas de message d’erreur clair identifiant le problème, l’erreur ressemblait à

     main, READ: TLSv1.2 Alert, length = 2 main, RECV TLSv1.2 ALERT: fatal, handshake_failure %% Invalidated: [Session-3, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384] main, called closeSocket() main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 

    J’ai isolé le problème en essayant avec et sans l’ -Djsse.enableSNIExtension=false-Djsse.enableSNIExtension=false

    La mienne était une erreur incompatible avec la version TLS .

    Auparavant c’était TLSv1 je l’ai changé TLSV1.2 ceci a résolu mon problème.

    J’ai eu un problème similaire; la mise à niveau vers Apache HTTPClient 4.5.3 a résolu le problème.

    Ugg! Cela s’est avéré être simplement un problème de version Java pour moi. J’ai eu l’erreur de prise de contact avec JRE 1.6 et tout fonctionnait parfaitement avec JRE 1.8.0_144.

    Déni de responsabilité: Je ne sais pas si la réponse sera utile pour beaucoup de gens, simplement pour le partager.

    J’avais cette erreur en utilisant Parasoft SOATest pour envoyer la requête XML (SOAP).

    Le problème était que j’avais sélectionné le mauvais alias dans la liste déroulante après avoir ajouté le certificate et l’avoir authentifié.

    J’utilise le client com.google.api http. Lorsque je communique avec un site interne de l’entreprise, j’ai rencontré ce problème lorsque j’ai utilisé par erreur https, au lieu de http.

     main, READ: TLSv1.2 Alert, length = 2 main, RECV TLSv1.2 ALERT: fatal, handshake_failure main, called closeSocket() main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure main, IOException in getSession(): javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure main, called close() main, called closeInternal(true) 262 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Connection shut down main, called close() main, called closeInternal(true) 263 [main] DEBUG org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager - Released connection is not reusable. 263 [main] DEBUG org.apache.http.impl.conn.tsccm.ConnPoolByRoute - Releasing connection [HttpRoute[{s}->https://]][null] 263 [main] DEBUG org.apache.http.impl.conn.tsccm.ConnPoolByRoute - Notifying no-one, there are no waiting threads Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:431) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128) at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:339) at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:123) at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:147) at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:108) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:415) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554) at com.google.api.client.http.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:67) at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:960) 

    Dans mon cas, cert est importé, l’erreur persiste, résolue en ajoutant System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3"); avant de se connecter

    Dans mon cas, le site Web ne peut utiliser que TLSv1.2. et j’utilise apache httpclient 4.5.6, j’utilise ce code et installe jce pour résoudre ce problème (JDK1.7):

    jce

    jdk7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

    jdk 8 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

    code:

     SSLContext sslContext = SSLContext.getDefault(); SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory( sslContext, new Ssortingng[]{"TLSv1.2"}, // important null, NoopHostnameVerifier.INSTANCE); Registry registry = RegistryBuilder.create() .register("https", sslConnectionFactory) .register("http", PlainConnectionSocketFactory.INSTANCE) .build(); HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry); httpclient = HttpClientBuilder.create(). .setSSLSocketFactory(sslConnectionFactory) .setConnectionManager(ccm) .build();