web-dev-qa-db-fra.com

Comment éviter d'installer des fichiers de stratégie JCE "Unlimited Strength" lors du déploiement d'une application?

J'ai une application qui utilise le cryptage AES 256 bits qui n'est pas pris en charge par Java prêt à l'emploi. Je sais que cela fonctionne correctement, j'installe les jars JCE à puissance illimitée dans le dossier de sécurité. C’est bien pour moi d’être développeur, je peux les installer.

Ma question est que, puisque cette application sera distribuée, les fichiers de stratégie ne seront probablement pas installés par les utilisateurs finaux. Demander à l’utilisateur final de les télécharger uniquement pour que la fonction de l’application fonctionne n’est pas une solution attrayante.

Existe-t-il un moyen de faire fonctionner mon application sans écraser les fichiers sur la machine de l'utilisateur final? Un logiciel tiers capable de le gérer sans les fichiers de stratégie installés? Ou un moyen de simplement référencer ces fichiers de règles à partir d'un fichier JAR?

166
Ken

Il existe plusieurs solutions couramment citées à ce problème. Malheureusement, ni l'un ni l'autre ne sont entièrement satisfaisants:

  • Installez le fichiers de stratégie de force illimitée . Bien qu'il s'agisse probablement de la bonne solution pour votre poste de travail de développement, cela devient rapidement un problème majeur. (si ce n’est pas un roadblock) pour que des utilisateurs non techniques installent les fichiers sur chaque ordinateur. Il n'y a aucun moyen de distribuer les fichiers avec votre programme; ils doivent être installés dans le répertoire JRE (qui peut même être en lecture seule en raison d'autorisations).
  • Ignorez l'API JCE et utilisez une autre bibliothèque de cryptographie telle que Bouncy Castle . Cette approche nécessite une bibliothèque supplémentaire de 1 Mo, ce qui peut représenter une charge importante en fonction de l'application. Il est également ridicule de dupliquer les fonctionnalités incluses dans les bibliothèques standard. De toute évidence, l’API est également complètement différente de l’interface JCE habituelle. (BC implémente un fournisseur JCE, mais cela n’aide en rien car les restrictions relatives à la force des clés sont appliquées avant passation à l’implémentation.) Cette solution ne vous permettra pas non plus d’utiliser 256 bits. Suites de chiffrement TLS (SSL), car les bibliothèques TLS standard appellent JCE en interne pour déterminer les restrictions éventuelles.

Mais alors il y a réflexion. Y a-t-il quelque chose que vous ne pouvez pas faire en utilisant la réflexion?

private static void removeCryptographyRestrictions() {
    if (!isRestrictedCryptography()) {
        logger.fine("Cryptography restrictions removal not needed");
        return;
    }
    try {
        /*
         * Do the following, but with reflection to bypass access checks:
         *
         * JceSecurity.isRestricted = false;
         * JceSecurity.defaultPolicy.perms.clear();
         * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
         */
        final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
        final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
        final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

        final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
        isRestrictedField.setAccessible(true);
        final Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
        isRestrictedField.set(null, false);

        final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
        defaultPolicyField.setAccessible(true);
        final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

        final Field perms = cryptoPermissions.getDeclaredField("perms");
        perms.setAccessible(true);
        ((Map<?, ?>) perms.get(defaultPolicy)).clear();

        final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        defaultPolicy.add((Permission) instance.get(null));

        logger.fine("Successfully removed cryptography restrictions");
    } catch (final Exception e) {
        logger.log(Level.WARNING, "Failed to remove cryptography restrictions", e);
    }
}

private static boolean isRestrictedCryptography() {
    // This matches Oracle Java 7 and 8, but not Java 9 or OpenJDK.
    final String name = System.getProperty("Java.runtime.name");
    final String ver = System.getProperty("Java.version");
    return name != null && name.equals("Java(TM) SE Runtime Environment")
            && ver != null && (ver.startsWith("1.7") || ver.startsWith("1.8"));
}

Appelez simplement removeCryptographyRestrictions() à partir d'un initialiseur statique ou autre avant d'effectuer toute opération cryptographique.

La partie JceSecurity.isRestricted = false Est tout ce qui est nécessaire pour utiliser directement des chiffrements 256 bits; cependant, sans les deux autres opérations, Cipher.getMaxAllowedKeyLength() continuera à en signaler 128, et les suites de chiffrement TLS 256 bits ne fonctionneront pas.

Ce code fonctionne sur Oracle Java 7 et 8 et ignore automatiquement le processus sur Java 9 et sur OpenJDK là où il n'est pas nécessaire. Après tout, étant un bidule laid, cela ne fonctionnera probablement pas sur les ordinateurs virtuels d'autres fournisseurs.

De plus, cela ne fonctionne pas sur Oracle Java 6, car les classes JCE privées y sont masquées. L'obfuscation ne change pas d'une version à l'autre, il est donc toujours techniquement possible de prendre en charge Java 6.

171
ntoskrnl

Ceci n’est plus nécessaire pour Java 9 , ni pour les versions récentes de Java 6, 7 ou 8 . Finalement! :)

Per JDK-8170157 , la stratégie cryptographique illimitée est maintenant activée par défaut.

Versions spécifiques du numéro JIRA:

  • Java 9 (10, 11, etc.): N'importe quelle version officielle!
  • Java 8u161 ou version ultérieure (disponible maintenant)
  • Java 7u171 ou version ultérieure (disponible uniquement via 'My Oracle Support')
  • Java 6u181 ou version ultérieure (disponible uniquement via 'My Oracle Support')

Notez que si, pour une raison quelconque, l'ancien comportement est requis dans Java 9, il peut être défini à l'aide de:

Security.setProperty("crypto.policy", "limited");
85
cranphin

Voici la solution: http://middlesphere-1.blogspot.ru/2014/06/this-code-allows-to-break-limit-if.html

//this code allows to break limit if client jdk/jre has no unlimited policy files for JCE.
//it should be run once. So this static section is always execute during the class loading process.
//this code is useful when working with Bouncycastle library.
static {
    try {
        Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
        field.setAccessible(true);
        field.set(null, Java.lang.Boolean.FALSE);
    } catch (Exception ex) {
    }
}
22
mike

À partir du JDK 8u102, les solutions publiées reposant sur la réflexion ne fonctionneront plus: le champ défini par ces solutions est désormais final ( https://bugs.openjdk.Java.net/browse/JDK -8149417 ).

On dirait que cela revient à (a) utiliser Bouncy Castle ou (b) à installer les fichiers de règles JCE.

13
Sam Roberton

Bouncy Castle nécessite toujours des bocaux installés pour autant que je sache.

J'ai fait un petit test et cela a semblé confirmer ceci:

http://www.bouncycastle.org/wiki/display/JA1/Frequent+Asked+Questions

13
timothyjc

Pour une bibliothèque de cryptographie alternative, consultez Bouncy Castle . Il a AES et beaucoup de fonctionnalités ajoutées. C'est une librairie libérale open source. Pour que cela fonctionne, vous devrez utiliser l’API propriétaire légère et légendaire de Bouncy Castle.

8
user150924

Vous pouvez utiliser la méthode

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

pour tester la longueur de clé disponible, utilisez-la et informez l'utilisateur de ce qui se passe. Quelque chose indiquant que votre application retombe sur des clés 128 bits en raison de fichiers non installés, par exemple. Les utilisateurs soucieux de la sécurité installeront les fichiers de règles, d’autres continueront à utiliser des clés plus faibles.

4
Christian Schulte

Pour notre application, nous avions une architecture client/serveur et nous ne permettions que le décryptage/cryptage des données au niveau du serveur. Par conséquent, les fichiers JCE ne sont nécessaires qu’à cet endroit.

Un autre problème nous a obligés à mettre à jour un fichier jar de sécurité sur les ordinateurs clients. JNLP écrase les bibliothèques in${Java.home}/lib/security/ et la JVM lors de la première exécution.

Cela a fonctionné.

3
Mohamed Mansour

Voici une version mise à jour de ntoskrnl answer. Il contient en outre une fonction pour supprimer le modificateur final comme Arjan mentionné dans les commentaires.

Cette version fonctionne avec JRE 8u111 ou une version plus récente.

private static void removeCryptographyRestrictions() {
    if (!isRestrictedCryptography()) {
        return;
    }
    try {
        /*
         * Do the following, but with reflection to bypass access checks:
         * 
         * JceSecurity.isRestricted = false; JceSecurity.defaultPolicy.perms.clear();
         * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
         */
        final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
        final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
        final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

        Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
        isRestrictedField.setAccessible(true);
        setFinalStatic(isRestrictedField, true);
        isRestrictedField.set(null, false);

        final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
        defaultPolicyField.setAccessible(true);
        final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

        final Field perms = cryptoPermissions.getDeclaredField("perms");
        perms.setAccessible(true);
        ((Map<?, ?>) perms.get(defaultPolicy)).clear();

        final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        defaultPolicy.add((Permission) instance.get(null));
    }
    catch (final Exception e) {
        e.printStackTrace();
    }
}

static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }

private static boolean isRestrictedCryptography() {
    // This simply matches the Oracle JRE, but not OpenJDK.
    return "Java(TM) SE Runtime Environment".equals(System.getProperty("Java.runtime.name"));
}
2
xoned

Voici une version modifiée du code de @ ntoskrnl avec isRestrictedCryptography check by real Cipher.getMaxAllowedKeyLength , enregistrement de slf4j et support de l’initialisation de singleton à partir de l’application bootstrap comme ceci:

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

Ce code cesserait correctement de perturber la réflexion lorsque la stratégie illimitée devient disponible par défaut dans Java 8u162, comme le prédit la réponse de @ cranphin.


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import Java.lang.reflect.Field;
import Java.lang.reflect.Modifier;
import Java.security.NoSuchAlgorithmException;
import Java.security.Permission;
import Java.security.PermissionCollection;
import Java.util.Map;

// https://stackoverflow.com/questions/1179672/how-to-avoid-installing-unlimited-strength-jce-policy-files-when-deploying-an
public class UnlimitedKeyStrengthJurisdictionPolicy {

    private static final Logger log = LoggerFactory.getLogger(UnlimitedKeyStrengthJurisdictionPolicy.class);

    private static boolean isRestrictedCryptography() throws NoSuchAlgorithmException {
        return Cipher.getMaxAllowedKeyLength("AES/ECB/NoPadding") <= 128;
    }

    private static void removeCryptographyRestrictions() {
        try {
            if (!isRestrictedCryptography()) {
                log.debug("Cryptography restrictions removal not needed");
                return;
            }
            /*
             * Do the following, but with reflection to bypass access checks:
             *
             * JceSecurity.isRestricted = false;
             * JceSecurity.defaultPolicy.perms.clear();
             * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
             */
            Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
            Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
            Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

            Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
            isRestrictedField.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
            isRestrictedField.set(null, false);

            Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
            defaultPolicyField.setAccessible(true);
            PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

            Field perms = cryptoPermissions.getDeclaredField("perms");
            perms.setAccessible(true);
            ((Map<?, ?>) perms.get(defaultPolicy)).clear();

            Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
            instance.setAccessible(true);
            defaultPolicy.add((Permission) instance.get(null));

            log.info("Successfully removed cryptography restrictions");
        } catch (Exception e) {
            log.warn("Failed to remove cryptography restrictions", e);
        }
    }

    static {
        removeCryptographyRestrictions();
    }

    public static void ensure() {
        // just force loading of this class
    }
}
1
Vadzim

Lors de l'installation de votre programme, il suffit d'inviter l'utilisateur et de télécharger un script Batch DOS ou Bash Shell et de copier le JCE à l'emplacement système approprié.

J'avais l'habitude de faire cela pour un service Web de serveur et au lieu d'un programme d'installation officiel, je venais de fournir des scripts pour configurer l'application avant que l'utilisateur ne puisse l'exécuter. Vous pouvez rendre l'application non exécutable jusqu'à ce qu'ils exécutent le script d'installation. Vous pouvez également demander à l'application de se plaindre de l'absence de l'entreprise criminelle commune, puis de demander de télécharger et de redémarrer l'application?

0
djangofan