170 votes

Réception d'une alerte fatale : handshake_failure par SSLHandshakeException

J'ai un problème avec la connexion SSL autorisée. J'ai créé une action Struts qui se connecte à un serveur externe avec un certificat SSL autorisé par le client. Dans mon action, j'essaie d'envoyer des données au serveur de la banque, mais sans succès, car j'ai comme résultat de la part du serveur 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()));

    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 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 certificat, j'ai converti le fichier .pfx en .jks, mais j'ai la même erreur, sans aucun changement.

26voto

Bela Vizy Points 723

L'échec de la poignée de main pourrait être dû à une implémentation défectueuse du protocole TLSv1.

Dans notre cas, cela a permis d'utiliser 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, ceux qui sont bogués descendront à v1 et cela fonctionne avec la v1 similaire dans java 7.

22voto

Simon Yu Points 219

Installation de Java Cryptography Extension (JCE) Unlimited Strength ( pour JDK7 pour JDK8 ) pourrait corriger ce bogue. Décompressez le fichier et suivez le readme pour l'installer.

18voto

Brig Points 2457

Cela peut également se produire lorsque le client doit présenter un certificat. Une fois que le serveur a établi la liste de la chaîne de certificats, il peut se produire ce qui suit :

3. Demande de certificat Le serveur émet une demande de certificat au client. La demande énumère 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. Chaîne de certificats du client Il s'agit du certificat 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 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 exige un, vous obtiendrez l'erreur "handshake" ici. Une cause probable est que le chemin d'accès à votre certificat n'a pas été trouvé.

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

*** CertificateVerify
... payload of verify check

Cette étape n'a lieu que si vous envoyez un certificat.

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

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

18voto

motobói Points 370

Je ne pense pas que cela résolve le problème pour la première personne qui a posé la question, mais pour les googleurs qui viennent ici pour obtenir des réponses :


Dans la mise à jour 51, java 1.8 interdit[1] les algorithmes de chiffrement RC4 par défaut, comme on peut le voir sur la page des notes de mise à jour :

Correction de bogues : Interdire les suites de chiffrement RC4

RC4 est désormais considéré comme un algorithme de chiffrement compromis.

Les suites de chiffrement RC4 ont été supprimées de la liste des suites de chiffrement activées par défaut par le client et le serveur dans la mise en œuvre d'Oracle JSSE. Ces suites de chiffrement peuvent toujours être activées par SSLEngine.setEnabledCipherSuites() y SSLSocket.setEnabledCipherSuites() des méthodes. Voir JDK-8077109 (non public).

Si votre serveur a une forte préférence pour ce cryptogramme (ou n'utilise que ce cryptogramme), cela peut déclencher une erreur de type handshake_failure sur java.

Vous pouvez tester la connexion au serveur en activant le chiffrement RC4 (essayez d'abord sans enabled pour voir si le déclenchement d'un handshake_failure , puis fixer 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

14voto

Maxim Votyakov Points 684

J'ai eu cette erreur en essayant d'utiliser le JDK 1.7. Lorsque j'ai mis à jour mon JDK en jdk1.8.0_66, tout a commencé à bien fonctionner.

La solution la plus simple à ce problème pourrait donc être - mettre à jour votre JDK et il pourrait commencer à bien fonctionner.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X