Alerte fatale reçue: défaillance de la poignée de main par SSLHandshakeException
j'ai un problème avec la connexion SSL autorisée. J'ai créé une Action Struts qui se connecte au serveur externe avec un certificat SSL autorisé par le Client. Dans mon Action, j'essaie d'envoyer des données au serveur de banque, mais sans succès, parce que j'ai comme résultat de serveur l'erreur suivante:
error: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
ma méthode de ma classe D'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()));
String IPStr = inIP.readLine(); //IP as a String
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;
}
String result = merchant.sendTransData(amount, currency, IPStr, description);
System.out.println("result: " + result);
return SUCCESS;
mon marchand.fichier de propriétés:
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é que c'était un problème de certificat, Je l'ai converti à partir .pfx .jks, mais j'ai la même erreur, sans aucune modification.
16 réponses
La poignée de main de défaillance a pu se produire pour diverses raisons:
- Suites de chiffrement incompatibles utilisées par le client et le serveur. Cela exigerait que le client utilise (ou active) une suite de chiffrement qui est supportée par le serveur.
- versions incompatibles de SSL utilisées (le serveur peut n'accepter que TLS v1, alors que le client n'est capable d'utiliser que SSL v3). Encore une fois, le client peut avoir à s'assurer qu'il utilise un version compatible du protocole SSL/TLS.
- chemin de confiance incomplet pour le certificat du serveur; le certificat du serveur n'est probablement pas fiable par le client. Cela se traduirait généralement par une erreur plus verbeuse, mais c'est tout à fait possible. Habituellement, la solution consiste à importer le certificat CA du serveur dans le magasin de confiance du client.
- le certificat est délivré pour un domaine différent. Encore une fois, cela aurait abouti à un message plus verbeux, mais je vais précisez la solution ici au cas où c'est la cause. La résolution serait dans ce cas le serveur (il ne semble pas être le vôtre) pour utiliser le certificat approprié.
étant donné que la défaillance sous-jacente ne peut pas être identifiée, il est préférable d'activer le drapeau -Djavax.net.debug=all
pour permettre le débogage de la connexion SSL établie. Avec le débogage activé, vous pouvez identifier quelle activité dans la poignée de main a échoué.
mise à Jour
D'après les détails maintenant disponibles, il semble que le problème soit dû à un chemin de confiance incomplet entre le certificat émis au serveur et une AC racine. Dans la plupart des cas, cela est dû au fait que le certificat de l'AC racine est absent dans la réserve de confiance, ce qui conduit à la situation où un chemin de confiance de certificat ne peut pas exister; le certificat est essentiellement non fiable par le client. Les navigateurs peuvent présenter un avertissement que les utilisateurs puissent l'ignorer, mais ce n'est pas le cas pour les clients SSL (comme la classe HttpsURLConnection , ou toute bibliothèque client HTTP comme Apache HttpComponents Client ).
la plupart de ces classes de clients/bibliothèques s'appuieraient sur la mémoire de confiance utilisée par la JVM pour la validation des certificats. Dans la plupart des cas, ce sera le fichier cacerts
dans le répertoire JRE_HOME/lib/security. Si l'emplacement du magasin en fiducie a a été spécifié en utilisant la propriété du système JVM javax.net.ssl.trustStore
, alors le magasin dans ce chemin est habituellement celui utilisé par la bibliothèque du client. Si vous avez des doutes, jetez un coup d'oeil à votre classe Merchant
, et découvrez la classe/bibliothèque qu'elle utilise pour faire la connexion.
L'ajout de L'AC émettrice du certificat du serveur à cette boutique en fiducie devrait résoudre le problème. Vous pouvez vous référer à mon réponse sur une question connexe sur l'obtention d'outils pour ce but, mais L'utilitaire Java keytool est suffisant à cet effet.
Avertissement : La banque de confiance est essentiellement la liste de toutes les autorités de certification de confiance. Si vous mettez dans un certificat qui n'appartient pas à une AC en qui vous n'avez pas confiance, alors les connexions SSL/TLS aux sites ayant des certificats émis par cette entité peuvent être déchiffrées si la clé privée est disponible.
mise à Jour #2: Compréhension à la sortie de la JSSE trace
Le fichier de clés et la truststores utilisé par la JVM sont généralement répertoriés au début, un peu comme les suivantes:
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 mauvais truststore est utilisé, alors vous aurez besoin de réimporter le certificat du serveur vers le bon, ou reconfigurer le serveur pour utiliser celui indiqué (PAS RECOMMANDÉ Si vous avez plusieurs JVM, et toutes sont utilisées pour des besoins différents).
Si vous voulez vérifier si la liste de confiance certs contient le certificat, puis il ya une section pour les mêmes, qui commence ainsi:
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 chercher si L'AC du serveur est un sujet.
le processus de poignée de main aura quelques entrées saillantes (vous aurez besoin de connaître SSL pour les comprendre en détail, mais pour le but de déboguer le problème actuel, il suffira de sachez qu'un handshake_failure est généralement signalé dans le ServerHello.
1. ClientHello
une série d'entrées sera rapportée lorsque la connexion est initialisée. Le premier message envoyé par le client dans une configuration de connexion SSL/TLS est le message ClientHello, généralement rapporté dans les logs comme:
*** 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. Ce pourrait devoir convenir avec la mention dans votre marchand.fichier de propriétés, pour la même convention pourrait être utilisé par la bibliothèque de la banque. Si la convention utilisée est différente, il n'y a pas de raison de s'inquiéter, car le ServerHello l'indiquera, 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 de le 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 qu'il a choisie; c'est la meilleure suite disponible à la fois pour le serveur et le client. Habituellement, la suite de chiffrement n'est pas spécifiée s'il y a une erreur. Le certificat du serveur (et éventuellement la chaîne entière) est envoyé par le serveur, et se trouve dans les entrées comme:
*** 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 certificate
***
Si la vérification du certificat a réussi, vous trouverez une entrée similaire à:
Found trusted certificate:
[
[
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 réussi, résultant en la handshake_failure, car la poignée de main est généralement complète à ce stade (pas vraiment, mais les étapes suivantes de la poignée de main ne causent généralement pas un échec de la poignée de main). Vous aurez besoin de comprendre quelle étape a échoué, et de poster le message approprié comme une mise à jour à la question (à moins que vous avez déjà compris le message, et vous savez quoi faire pour le résoudre).
Je ne pense pas que cela résolve le problème pour le premier questionneur, mais pour les googleurs venant ici pour des réponses:
Sur la mise à jour 51, java 1.8 interdit[1] RC4 algorithmes de chiffrement par défaut, comme on peut le voir sur les Notes de Version de la page:
Bug Fix: d'Interdire le chiffrement RC4 suites
RC4 est maintenant considéré comme un cryptage compromis.
RC4 les suites de chiffrement ont été retirées de la liste de suite de chiffrement activée par défaut du client et du serveur dans L'implémentation JSSE D'Oracle. Ces suites de chiffrement peuvent encore être activées par les méthodes
SSLEngine.setEnabledCipherSuites()
etSSLSocket.setEnabledCipherSuites()
. Voir JDK-8077109 (non public).
si votre serveur a une forte préférence pour ce cipher (ou utilisez seulement ce cipher) cela peut déclencher un handshake_failure
sur java.
vous pouvez tester la connexion au serveur en activant les chiffrements RC4 (d'abord, Essayez sans enabled
argument pour voir si déclenche un handshake_failure
, puis mettre 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(String[] args) {
String[] cyphers;
if (args.length < 2) {
System.out.println("Usage: "+SSLRC4Poke.class.getName()+" <host> <port> 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 String[]{
"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
Cela peut aussi se passer lorsque le client doit présenter un certificat. Après que le serveur liste la chaîne de certificats, CE qui suit peut arriver:
3. Demande De Certificat Le serveur émettra une demande de certificat du client. La requête listera tous les certificats acceptés par le serveur.
*** CertificateRequest
Cert Types: RSA
Cert Authorities:
<CN=blah, OU=blah, O=blah, L=blah, ST=blah, C=blah>
<CN=yadda, DC=yadda, DC=yadda>
<CN=moreblah, OU=moreblah, O=moreblah, C=moreblah>
<CN=moreyada, OU=moreyada, O=moreyada, C=moreyada>
... the rest of the request
*** ServerHelloDone
4. Certificat Du Client De La Chaîne D' C'est le certificat, le client est envoi vers le 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 certificate
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
... key exchange info
S'il n'y a pas de certificat dans la chaîne et que le serveur en nécessite un, vous obtiendrez l'erreur de poignée de main ici. Une cause probable est que le chemin vers votre certificat n'a pas été trouvé.
5. Certificat Verify Le client demande au serveur de vérifier le certificat
*** CertificateVerify
... payload of verify check
cette étape n'aura lieu que si vous envoyez un certificat.
6. Fini Le serveur répondra avec une réponse verify
*** Finished
verify_data: { 345, ... }
l'échec de poignée de main pourrait être une mise en œuvre du protocole TLSv1 buggy.
dans notre cas cela a aidé avec java 7:
java -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1
la jvm négociera dans cet ordre. Les serveurs avec la dernière mise à jour vont faire 1.2, Les buggy vont descendre à v1 et cela fonctionne avec la même v1 en java 7.
j'ai cette erreur alors que j'ai essayé d'utiliser JDK 1.7. Quand j'ai mis à jour mon JDK en jdk1.8.0_66 all started to work fine.
donc la solution la plus simple pour ce problème pourrait être - mise à jour de votre JDK et il pourrait commencer à bien fonctionner.
en supposant que vous utilisez les protocoles SSL/TLS appropriés, que vous avez correctement configuré vos keyStore
et trustStore
, et que vous avez confirmé qu'il n'y a aucun problème avec les certificats eux-mêmes, vous pourriez avoir besoin de renforcer vos algorithmes de sécurité .
comme mentionné dans la réponse de Vineet , une raison possible pour laquelle vous recevez cette erreur est due à des suites de chiffrement incompatibles étant utilisées. En mettant à jour mon local_policy
et US_export_policy
bocaux dans le dossier security
de mon JDK avec ceux fournis dans le Java Cryptography Extension (JCE) , j'ai été en mesure de compléter la poignée de main avec succès.
je rencontre le même problème aujourd'hui avec le client OkHttp pour obtenir une url basée sur https. Il s'agit de causé par la version du protocole Https et l'inadéquation de la méthode de chiffrement entre le côté serveur et le côté client .
1) Vérifiez la version du protocole https de votre site web et la méthode de chiffrement. openssl>s_client -connect your_website.com:443 -showcerts
vous obtiendrez beaucoup d'informations détaillées, l'information clé est énumérée comme suit:
SSL-Session:
Protocol : TLSv1
Cipher : RC4-SHA
2) config de votre client http, par exemple, dans OkHttp client case:
@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().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
on obtiendra ce qu'on veut.
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 le ServerHello
avait terminé avec succès mais avant le flux de données a commencé.
il n'y avait pas de message d'erreur clair qui identifiait le problème, l'erreur ressemblait juste à
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 le " -Djsse.enableSNIExtension=false
"option
Mine était une erreur incompatible de version TLS
.
auparavant, il était TLSv1
Je l'ai changé TLSV1.2
cela a résolu mon problème.
j'utilise com.Google.api http client. Lorsque je communique avec un site interne de l'entreprise, j'ai eu 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://<I-replaced>]][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)
j'ai eu un problème similaire; la mise à niveau vers Apache HTTPClient 4.5.3 l'a corrigé.
dans mon cas, cert est importé, erreur reste, résolu en ajoutant System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3");
avant de connecter
Ugg! Cela s'est avéré être simplement un problème de version Java pour moi. J'ai eu l'erreur de poignée de main en utilisant JRE 1.6 et tout a fonctionné parfaitement en utilisant JRE 1.8.0_144.
avertissement : Je ne sais pas si la réponse sera utile pour beaucoup de gens,juste le partage parce qu'il pourrait .
j'ai eu cette erreur en utilisant Parasoft SOATest pour envoyer la requête XML(SOAP) .
le problème était que j'avais sélectionné le faux alias dans la liste déroulante après avoir ajouté le certificat et l'avoir authentifié.
dans mon cas, le site web peut utiliser TLSv1.2. et j'utilise apache httpclient 4.5.6, j'utilise ce code et j'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 String[]{"TLSv1.2"}, // important
null,
NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConnectionFactory)
.register("http", PlainConnectionSocketFactory.INSTANCE)
.build();
HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
httpclient = HttpClientBuilder.create().
.setSSLSocketFactory(sslConnectionFactory)
.setConnectionManager(ccm)
.build();