web-dev-qa-db-fra.com

Java 7 et n'a pas pu générer la paire de clés DH

J'ai lu un article précédent concernant l'erreur "Impossible de générer la paire de clés DH" déclenchée lorsque le serveur envoie une clé de plus de 1024 bits. Le téléchargement des jars illimités JCE devrait résoudre ce problème. Dans l'environnement de test, j'ai rencontré les éléments suivants, pour le même serveur Web si j'utilise Java 6 Je ne reçois aucune erreur lors de l'exécution de la requête https mais si j'utilise Java 7 alors j'obtiens "Impossible de générer la paire de clés DH".

J'ai essayé de remplacer les fichiers jar pour JCE unlimited, mais j'obtiens toujours la même erreur. Le bogue est signalé depuis 2007, mais pourquoi s'exécute-t-il pour Java 6 et non pour Java 7? Les fichiers à télécharger ne sont-ils pas les bons? I a obtenu le lien d'un article précédent Java: Pourquoi la négociation SSL donne-t-elle l'exception "Impossible de générer la paire de clés DH"? .

À ce stade, je ne sais pas quoi faire. Si j'essaie de charger le fournisseur BouncyCastle, j'obtiens une exception ArrayOutOfIndex. Mon serveur n'autorise que l'algorithme DH, donc je ne peux pas utiliser un autre algorithme comme suggéré dans le post ci-dessus.

14
user1408089

Quelques ajouts ou clarifications:

(Suncle) Java 7 puisque 7u09 utilise par défaut un ordre cohérent de ciphersuites plus sensible, contrairement à l'ordre apparemment aléatoire de 7u04. (Je n'ai pas de tests entre 04 et 09.) Cet ordre met ECDHE et plain-RSA (alias akRSA) avant DHE, et évite ainsi le problème si ET SEULEMENT SI le serveur prend en charge ECDHE ou RSA et accepte la préférence du client (ou fixé par ECDH, mais pratiquement personne ne l'utilise.) Si le serveur insiste sur DHE (pour une raison quelconque) ET utilise DH> 1024 bits, vous avez toujours le problème.

Si le demandeur (ou toute autre personne) se connecte à un serveur qui nécessite vraiment un entier-DH (et non ECDH ou RSA), la seule façon de travailler avec Java avant 8 est d'obtenir le serveur pour utiliser DH 1024-bit. Ce qui AFAWK est techniquement sécurisé pour quelques années de plus, mais avec une marge mince, il est interdit par des autorités importantes comme NIST (voir Special Pub 800-57 à csrc.nist.gov). (Même RSA 1024 isn n'est pas encore cassé, mais ce sera probablement bientôt et est donc interdit.)

La "politique de force illimitée" n'est pas pertinente pour ce problème, ou du moins pas directement, et les bonnes réponses à # 6851461 ne disent pas que c'est le cas. Cela ne change pas la restriction des paramètres DH dans SunJCE, qui est (à tort) traité comme un problème standard et non comme un problème de résistance. (Plus précisément, il prend les restrictions qui étaient correctes pour DSA et les applique à DH.) Il active les suites AES-256 et SHA-2 (uniquement pour TLSv1.2), et avec une liste de préférences suffisamment étrange, qui pourrait changer le résultat de la sélection de DHE (échoue) en non-DHE (fonctionne).

Vous n'avez pas besoin de revenir entièrement à la liste Java 6, vous avez juste besoin de prioriser d'autres échanges de clés sur DHE, ou pour un serveur récalcitrant, supprimez entièrement DHE. Vous ne devriez certainement PAS revenir en arrière pour activer toutes les suites EXPORT ou single-DES, à moins qu'elles ne soient absolument nécessaires pour un serveur hérité; elles ne sont PAS SÉCURISÉES depuis plusieurs années maintenant et sont restées activées par défaut dans 6 bien plus longtemps qu'elles n'auraient dû.

11
dave_thompson_085

Je suis tombé sur le même problème avec SSLScokets et je pense avoir identifié la raison de cette régression avec Java 7. La raison vient des chiffres négociés entre le client et le serveur.

Par défaut Java 6 active ces chiffres pour une connexion TLS (par ordre de priorité):

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
TLS_EMPTY_RENEGOTIATION_INFO_SCSV

Et Java 7 permet ces chiffres:

TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_RC4_128_SHA
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_RC4_128_SHA
TLS_ECDH_ECDSA_WITH_RC4_128_SHA
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_RC4_128_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_RC4_128_MD5
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_3DES_EDE_CBC_SHA

Les chiffrements utilisant Diffie-Hellman ont une priorité plus élevée sur Java 7, mais ils ne semblent pas prendre en charge les clés de plus de 1024 bits à moins que le paquetage cryptographique fort ne soit installé.

La solution de contournement que j'ai utilisée était de spécifier les chiffres activés par Java 6 sur le SSLSocket:

SSLSocketFactory socketFactory = SSLContext.getInstance("TLS").getSocketFactory();
SSLSocket socket = (SSLSocket) socketFactory.createSocket(InetAddress.getByName(hostname), port);
socket.setEnabledCipherSuites(new String[] {
        "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",
        "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"});

socket.startHandshake();
8
Emmanuel Bourg

Étant donné que vous utilisez la dernière Java et que vous obtenez toujours l'erreur, vous pouvez modifier un paramètre dans Java.security (par exemple dans le dossier C:\Program Files\Java\jre1.8.0_xx\lib \Sécurité

# Example:
#   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
    jdk.tls.disabledAlgorithms=SSLv3, RC4

Ajouter DH comme algorithme désactivé dans jdk.tls.disabledAlgorithms

    jdk.tls.disabledAlgorithms=SSLv3, RC4, DH

Redémarrez Tomcat ou réexécutez votre programme.

6
Tolomir

Nous rencontrions également ce problème avec Java7 et Java8. Nous avons également utilisé une solution de contournement similaire aux suggestions d'Emanual Borg. Mais notre objectif était d'éviter de coder en dur une liste fixe de CipherSuites. Nous avons donc essayé de supprimer les entrées à l'origine du problème (par essais et erreurs ...).

String[] enabledCipherSuites = socket.getEnabledCipherSuites();

// avoid hardcoding a new list, we just remove the entries
// which cause the exception
List<String> asList = new ArrayList(Arrays.asList(enabledCipherSuites));

// we identified the following entries causeing the problems
// "Could not generate DH keypair"
// and "Caused by: Java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)"
asList.remove("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
asList.remove("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
asList.remove("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");

String[] array = asList.toArray(new String[0]);
socket.setEnabledCipherSuites(array);

Question: Quelqu'un voit-il un problème avec cette approche?

Btw: Dans le cas où vous utilisez Apache HTTPClient, alors https://issues.Apache.org/jira/browse/HTTPCLIENT-1111 est intéressant qui montre comment configurer les CipherSuites (à partir de HTTPClient v4. 2) via la méthode

SSLConnectionSocketFactory() {...}.prepareSocket(SSLSocket)

Mise à jour 2015/10/31: Pour mieux comprendre le contexte dans lequel l'utiliser, voici un exemple de pseudo-code complet où vous voyez comment accrocher- pour remplacer la méthode prepareSocket ():

HttpClientBuilder builder = HttpClients.custom();

SSLContextBuilder sslContextBuilder = SSLContexts.custom();
SSLContext sslContext = sslContextBuilder.build();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostNameVerfier)
{


    protected void prepareSocket(SSLSocket socket) throws IOException {

    // Workaround to use different order of CipherSuites used by Java6 in order
        // to avoid the the problem of Java7 "Could not generate DH keypair"
        String[] enabledCipherSuites = socket.getEnabledCipherSuites();

        // but to avoid hardcoding a new list, we just remove the entries
        // which cause the exception (via TrialAndError)
        List<String> asList = new ArrayList(Arrays.asList(enabledCipherSuites));

        // we identified the following entries causeing the problems
        // "Could not generate DH keypair"
        // and "Caused by: Java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)"
        asList.remove("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
        asList.remove("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
        asList.remove("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");

        String[] array = asList.toArray(new String[0]);
        socket.setEnabledCipherSuites(array);

    };
};

Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslsf).build();

PoolingHttpClientConnectionManager conman = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
builder.setConnectionManager(conman);

CloseableHttpClient httpClient =  builder.build();

Soyez prudent Nous utilisons ce morceau de code uniquement dans un contexte où l'utilisateur permet explicitement de faire confiance aux certificats auto-signés (par exemple pour les environnements de test, etc. ). Si vous ne voulez pas faire cela, mieux vaut ne pas jouer avec SSL.

2
Christoph

Si vous utilisez jdk1.7.0_04, passez à jdk1.7.0_21. Le problème a été corrigé dans cette mise à jour.

1
Lekkie