2012-05-21 1 views
14

J'ai lu un article précédent concernant l'erreur 'Impossible de générer la paire de clés DH' lorsque le serveur émet une clé de plus de 1024 bits. Le téléchargement des fichiers jar JCE illimités devrait résoudre ce problème. Dans l'environnement de test j'ai rencontré le suivant, pour le même serveur web si j'utilise Java 6 je n'obtiens pas d'erreurs lors de l'exécution de la requête https mais si j'utilise Java 7 alors je ne peux pas générer de keypair DH.Java 7 et n'a pas pu générer la paire de clés DH

J'ai essayé de remplacer les fichiers jar pour JCE illimité, mais toujours la même erreur. Le bug est rapporté depuis 2007, mais pourquoi est-il exécuté pour Java 6 et pas pour Java 7? Les fichiers à télécharger ne sont-ils pas les bons? J'ai eu le lien d'un post précédent Java: Why does SSL handshake give 'Could not generate DH keypair' exception?.

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

Répondre

8

Je suis tombé sur le même problème avec SSLScokets et je pense que j'ai identifié la raison de cette régression avec Java 7. La raison vient aux chiffrements négociés entre le client et le serveur.

Par défaut Java 6 permet à ces chiffrements 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 chiffrements:

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 

chiffrements à l'aide viennent Diffie-Hellman en plus priorité sur Java 7, mais ils ne semblent pas prendre en charge les clés de plus de 1024 bits, sauf si le package crypto fort est installé.

La solution j'était de spécifier les chiffrements activés par Java 6 sur la 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(); 
+0

Merci un lot je rencontré un étrange * java.security.ProviderException: soleil .security.pkcs11.wrapper.PKCS11Exception: erreur CKR_DOMAIN_PARAMS_INVALID * lors de l'utilisation de Webscarab (proxy SSL MITM). La spécification explicite de ces suites de chiffrement le fait à nouveau fonctionner. – Lekensteyn

+1

Je suis nouveau pour les titres java. Où dois-je écrire ce morceau de code? – Shashank

0

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

+0

Cool. Cela a été corrigé dans les versions plus récentes de Java. Mais ma question est sur l'utilisation de la version plus ancienne .. Quand j'utilise une version plus ancienne, parfois cela fonctionne et parfois il donne l'exception ci-dessus .. Pourquoi un comportement si aléatoire? Si c'est un bug dans Java, alors je suppose que ça ne devrait jamais marcher? –

+2

Malheureusement, je reçois toujours cette erreur dans '7u21-2.3.9-1ubuntu1'. – expert

+0

Je reçois toujours cette erreur dans la construction 1.7.0_45-b18 – duffymo

10

Quelques ajouts ou précisions:

(Suncle) Java 7 depuis 7u09 utilise un ordre cohérent plus sensible ciphersuites par défaut, contrairement à l'ordre apparemment aléatoire dans 7u04. (Je n'ai pas de tests entre 04 et 09.) Cette commande met ECDHE et plain-RSA (alias akRSA) avant DHE, et évite ainsi le problème si ET SEULEMENT SI le serveur supporte ECDHE ou RSA et accepte la préférence du client. (Ou ECDH-fixe, 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 n'importe qui d'autre) se connecte à un serveur qui requiert vraiment un entier DH (et non pas ECDH ou RSA), la seule façon de travailler avec Java avant 8 est d'utiliser le serveur DH 1024 bits . Quel AFAWK est techniquement sûr pour quelques années de plus, mais avec une marge mince il est interdit par des autorités importantes comme NIST (voir le Pub spécial 800-57 à csrc.nist.gov). (Même RSA 1024 n'est pas encore brisé, mais il sera probablement bientôt interdit.)

La "politique de force illimitée" n'est pas pertinente à ce problème, ou du moins pas directement, et les bonnes réponses à # 6851461 n'a pas dit que c'est. Il ne modifie pas la restriction sur les paramètres DH dans SunJCE, qui est (à tort) traitée comme une question standard et non comme un problème de force. (Spécifiquement, il prend les restrictions qui étaient correctes pour DSA, et les applique à DH.) Il active les suites AES-256 et SHA-2 (seulement pour TLSv1.2), et étant donné une liste de préférences suffisamment étrange, cela pourrait changer le résultat de la sélection de DHE (échoue) à non-DHE (fonctionne).

Vous n'avez pas besoin de retourner 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 abandonner entièrement DHE. Vous ne devriez certainement pas revenir à l'activation de suites EXPORT ou single-DES, à moins que cela ne soit absolument nécessaire pour un serveur hérité; ils n'ont pas été sécurisés depuis plusieurs années maintenant, et sont restés activés par défaut dans 6 beaucoup plus longtemps qu'ils auraient dû.

1

Nous courions aussi ce problème avec java7 et Java8. Nous avons également utilisé une solution similaire aux suggestions d'Emanual Borg. Mais notre objectif était d'éviter le codage en dur d'une liste fixe de CipherSuites. Nous avons donc essayé de supprimer les entrées qui ont causé le 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: Est-ce que quelqu'un voit un problème avec cette approche?

BTW: Si vous utilisez Apache HTTPClient, alors https://issues.apache.org/jira/browse/HTTPCLIENT-1111 est intéressant qui montre comment définir les CipherSuites (en commençant par HTTPClient v4.2) via la méthode

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

Mise à jour 31/10/2015 : pour aider à mieux comprendre le contexte où l'utiliser, ici comme exemple complet pseudo-code où vous voyez comment crochet 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(); 

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

+0

J'utilise httpclient apache, mais ne pouvait pas comprendre comment utiliser prepareSocket –

6

Étant donné que vous utilisez la dernière édition java et toujours obtenir 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 \ security

.!
# 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 

tomcat Redémarrer ou relancer votre programme

Questions connexes