Wie vermeide ich die Installation von JCE-Richtliniendateien mit unbegrenzter Stärke beim Bereitstellen einer Anwendung?

169

Ich habe eine App, die 256-Bit-AES-Verschlüsselung verwendet, die von Java nicht sofort unterstützt wird. Ich weiß, damit dies richtig funktioniert, installiere ich die JCE-Gläser mit unbegrenzter Stärke im Sicherheitsordner. Für mich als Entwickler ist das in Ordnung, ich kann sie installieren.

Meine Frage ist, da diese App verteilt wird, werden Endbenutzer diese Richtliniendateien höchstwahrscheinlich nicht installiert haben. Es ist keine attraktive Lösung, wenn der Endbenutzer diese nur herunterlädt, damit die App funktioniert.

Gibt es eine Möglichkeit, meine App auszuführen, ohne Dateien auf dem Endbenutzercomputer zu überschreiben? Eine Software von Drittanbietern, die damit umgehen kann, ohne dass die Richtliniendateien installiert sind? Oder eine Möglichkeit, diese Richtliniendateien einfach aus einer JAR heraus zu referenzieren?

Duncan Jones
quelle
1
Werfen Sie einen Blick hier: docs.oracle.com/javase/1.5.0/docs/guide/security/jce/…
Petey B
11
Ich vermute, Sun / Oracle wollte, dass der Client eine weniger sichere Verschlüsselung verwendet, damit der NSA die Verbindung überwachen kann. Ich mache keine Witze oder bin paranoid, aber Kryptographie wird als Waffe behandelt und es gibt Exportverbote für die gemeinsame Nutzung von Verschlüsselung .
Schlitten

Antworten:

175

Es gibt einige häufig zitierte Lösungen für dieses Problem. Leider ist keines davon völlig zufriedenstellend:

  • Installieren Sie die Richtliniendateien mit unbegrenzter Stärke . Während dies wahrscheinlich die richtige Lösung für Ihre Entwicklungsarbeitsstation ist, wird es schnell zu einem großen Problem (wenn nicht sogar zu einer Straßensperre), wenn nicht technische Benutzer die Dateien auf jedem Computer installieren. Es gibt keine Möglichkeit , die Dateien mit Ihrem Programm zu verteilen. Sie müssen im JRE-Verzeichnis installiert sein (das aufgrund von Berechtigungen möglicherweise sogar schreibgeschützt ist).
  • Überspringen Sie die JCE-API und verwenden Sie eine andere Kryptografie-Bibliothek wie Bouncy Castle . Dieser Ansatz erfordert eine zusätzliche 1-MB-Bibliothek, was je nach Anwendung eine erhebliche Belastung darstellen kann. Es fühlt sich auch albern an, Funktionen zu duplizieren, die in den Standardbibliotheken enthalten sind. Natürlich unterscheidet sich die API auch völlig von der üblichen JCE-Schnittstelle. (BC implementiert zwar einen JCE-Anbieter, dies hilft jedoch nicht, da die Einschränkungen der Schlüsselstärke vor der Übergabe an die Implementierung angewendet werden .) Mit dieser Lösung können Sie auch keine 256-Bit-TLS-Verschlüsselungssuiten (SSL) verwenden, da die Standard-TLS-Bibliotheken rufen das JCE intern auf, um eventuelle Einschränkungen zu ermitteln.

Aber dann gibt es Reflexion. Gibt es etwas, das Sie mit Reflexion nicht tun können?

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"));
}

Rufen Sie einfach removeCryptographyRestrictions()von einem statischen Initialisierer oder ähnlichem auf, bevor Sie kryptografische Operationen ausführen.

Der JceSecurity.isRestricted = falseTeil ist alles, was benötigt wird, um 256-Bit-Chiffren direkt zu verwenden. Ohne die beiden anderen Vorgänge Cipher.getMaxAllowedKeyLength()werden jedoch weiterhin 128 gemeldet, und 256-Bit-TLS-Verschlüsselungssuiten funktionieren nicht.

Dieser Code funktioniert unter Oracle Java 7 und 8 und überspringt den Prozess unter Java 9 und OpenJDK automatisch, wenn er nicht benötigt wird. Da es sich schließlich um einen hässlichen Hack handelt, funktioniert es wahrscheinlich nicht auf den VMs anderer Anbieter.

Es funktioniert auch nicht unter Oracle Java 6, da die privaten JCE-Klassen dort verschleiert sind. Die Verschleierung ändert sich jedoch nicht von Version zu Version, sodass es technisch immer noch möglich ist, Java 6 zu unterstützen.

ntoskrnl
quelle
23
Die Reflection-Lösung verstößt möglicherweise gegen die Java-Lizenzvereinbarung : "F. JAVA TECHNOLOGY RESTRICTIONS. Sie dürfen das Verhalten von ... Klassen, Schnittstellen oder Unterpaketen, die in irgendeiner Weise als" Java "," Javax "bezeichnet werden, nicht ändern." , 'Sonne', 'Orakel' oder eine ähnliche Konvention ... "
M. Dudley
14
@ M.Dudley Könnte sein. Wenden Sie sich an einen Anwalt, bevor Sie ein Produkt versenden, das diesen Code enthält, wenn es Sie betrifft.
Ntoskrnl
3
@peabody In einigen Fällen ist es sicherlich eine Option, eine 100-MB-JRE in Ihr Programm aufzunehmen. Wenn nicht, müssen die Benutzer die Richtliniendateien weiterhin manuell installieren, selbst wenn Sie sie in Ihr Programm aufnehmen (aus verschiedenen Gründen wie Dateiberechtigungen). Nach meiner Erfahrung sind viele Benutzer dazu nicht in der Lage.
ntoskrnl
8
Es scheint, dass die Reflexionslösung in 1.8.0_112 gerade nicht mehr funktioniert. Es funktioniert in 1.8.0_111, aber nicht in 112.
John L
3
@ JohnL Ich benutze dies in einer Anwendung. Nachdem finalich in 8u111 Probleme mit dem Feld hatte, habe ich es so geändert, dass es das endgültige Feld nach dieser Antwort ändern kann . Das Ergebnis entspricht in etwa der neuen Version von ntoskrnl, nur dass ich es nicht modifiersFieldals deklariert habe final. Einer meiner Benutzer berichtet, dass es auch in 8u112 funktioniert.
Arjan
87

Dies wird jetzt weder für Java 9 noch für eine neuere Version von Java 6, 7 oder 8 benötigt. Endlich! :) :)

Per JDK-8170157 wird die unbegrenzte Verschlüsselungs Politik jetzt standardmäßig aktiviert.

Spezifische Versionen aus der JIRA-Ausgabe:

  • Java 9 (10, 11 usw.): Jede offizielle Veröffentlichung!
  • Java 8u161 oder höher ( jetzt verfügbar )
  • Java 7u171 oder höher (nur über 'My Oracle Support' verfügbar)
  • Java 6u181 oder höher (nur über 'My Oracle Support' verfügbar)

Beachten Sie, dass das alte Verhalten, wenn es aus irgendeinem Grund in Java 9 benötigt wird, wie folgt festgelegt werden kann:

Security.setProperty("crypto.policy", "limited");
Cranphin
quelle
4
Tatsächlich ist diese Richtlinie die Standardeinstellung, sodass in Java 9 keine Aktionen erforderlich sind!
Ntoskrnl
Ab dem 14.01.2018 (das neueste Oracle JDK ist 8u151 / 152) ist dies unter Java 8 noch nicht standardmäßig aktiviert, weit über ein Jahr nachdem diese Antwort ursprünglich geschrieben wurde ... Allerdings laut java.com/en/jre -jdk-cryptoroadmap.html Dies ist für GA am 16.01.2018
Alex
In meinem Fall und damit ich auf dieser Site die Note A bekomme : ssllabs.com/ssltest ... Ich muss es so einstellen: Security.setProperty ("crypto.policy", "unbegrenzt"); dann ... setze server.ssl.ciphers in meine application.properties mit 256-basierten Algorithmen, die in diesem Artikel angegeben sind -> schwachdh.org/sysadmin.html
Artanis Zeratul
Auch relevant für OpenJDK 8-Installationen. Siehe: stackoverlow-Artikel: Ist die JCE-Richtlinie mit openjdk 8 gebündelt?
Leole
22

Hier ist die Lösung: 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) {
    }
}
Mike
quelle
Dies ist die gleiche Lösung wie meine, außer ohne den Teil "defaultPolicy". Der Blog-Beitrag ist nach meiner Antwort datiert.
ntoskrnl
1
Aber ist das richtig? Kann dieser Code in Echtzeit die Anwendungssicherheit in Frage stellen? Ich bin mir nicht sicher, ob Sie mir helfen sollen, die Auswirkungen zu verstehen.
Gericht
1
Ich erhalte diesen Fehler, nachdem ich Folgendes ausgeführt habe:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
Andy
3
Ab Java 8 Build 111 ist diese Lösung nicht ausreichend, da das isRestrictedFeld endgültig ist ( bugs.openjdk.java.net/browse/JDK-8149417 ). Die Antwort von @ ntoskrnl kümmert sich um die mögliche Aufnahme eines "endgültigen" Modifikators. @ M.Dudleys Kommentar zum Java-Lizenzvertrag gilt auch weiterhin.
MPelletier
13

Ab JDK 8u102 funktionieren die auf Reflexion basierenden veröffentlichten Lösungen nicht mehr: Das Feld, das diese Lösungen festlegen, ist jetzt final( https://bugs.openjdk.java.net/browse/JDK-8149417 ).

Es sieht so aus, als ob entweder (a) Bouncy Castle verwendet oder (b) die JCE-Richtliniendateien installiert werden.

Sam Roberton
quelle
7
Sie könnten immer mehr Reflexion stackoverflow.com/questions/3301635/…
Universal Electricity
Ja, die Lösung von @ M.Dudley funktioniert weiterhin für das isRestrictedFeld, da sie sich um eine mögliche Hinzufügung eines "endgültigen" Modifikators kümmert.
MPelletier
1
Neue Version JDK 8u151 verfügt über die Eigenschaft "Neue Sicherheit zur Steuerung der Kryptorichtlinie". Fazit: Entfernen Sie das "#" aus der Zeile "# crypto.policy = unlimited" in "lib \ security \ java.security": oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
hemisphire
8

Eine alternative Kryptografiebibliothek finden Sie in Bouncy Castle . Es hat AES und viele zusätzliche Funktionen. Es ist eine liberale Open Source Bibliothek. Sie müssen jedoch die leichte, proprietäre Bouncy Castle-API verwenden, damit dies funktioniert.

Maarten Bodewes
quelle
19
Sie sind ein großartiger Krypto-Anbieter, benötigen jedoch die JCE-Datei mit unbegrenzter Stärke, um mit großen Schlüsseln arbeiten zu können.
John Meagher
16
Wenn Sie die Bouncy Castle-API direkt verwenden, benötigen Sie keine Dateien mit unbegrenzter Stärke.
laz
4

Sie könnten Methode verwenden

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

Um die verfügbare Schlüssellänge zu testen, verwenden Sie diese und informieren Sie den Benutzer über die aktuellen Vorgänge. Etwas, das besagt, dass Ihre Anwendung auf 128-Bit-Schlüssel zurückgreift, weil beispielsweise die Richtliniendateien nicht installiert sind. Sicherheitsbewusste Benutzer installieren die Richtliniendateien, andere verwenden weiterhin schwächere Schlüssel.

Christian Schulte
quelle
3

Für unsere Anwendung hatten wir eine Client-Server-Architektur und erlaubten nur das Entschlüsseln / Verschlüsseln von Daten auf Serverebene. Daher werden die JCE-Dateien nur dort benötigt.

Wir hatten ein weiteres Problem, bei dem wir ein Sicherheitsglas auf den Client-Computern aktualisieren mussten. Über JNLP werden die Bibliotheken ${java.home}/lib/security/und die JVM beim ersten Start überschrieben .

Das hat funktioniert.

Mohamed Mansour
quelle
2

Hier ist eine aktualisierte Version der Antwort von ntoskrnl . Es enthält zusätzlich eine Funktion zum Entfernen des endgültigen Modifikators wie Arjan , der in den Kommentaren erwähnt wird.

Diese Version funktioniert mit JRE 8u111 oder neuer.

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"));
}
xoned
quelle
Es funktioniert gut, aber die Zeile ((Map<?, ?>) perms.get(defaultPolicy)).clear();gibt einen Compilerfehler aus. Das Auskommentieren scheint die Funktionalität nicht zu beeinträchtigen. Ist diese Leitung notwendig?
Andreas Unterweger
2

Hier ist eine modifizierte Version des Codes von @ ntoskrnl, die die isRestrictedCryptographyÜberprüfung durch tatsächlicheCipher.getMaxAllowedKeyLength slf4j-Protokollierung und die Unterstützung der Singleton-Initialisierung vom Anwendungs-Bootstrap wie folgt enthält :

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

Dieser Code würde die Reflexion korrekt beenden, wenn in Java 8u162 standardmäßig unbegrenzte Richtlinien verfügbar sind, wie die Antwort von @ cranphin vorhersagt.


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;

// /programming/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
    }
}
Vadzim
quelle
-1

Fordern Sie den Benutzer während der Installation Ihres Programms einfach auf, ein DOS-Batch-Skript oder ein Bash-Shell-Skript herunterzuladen, und kopieren Sie das JCE an den richtigen Systemspeicherort.

Früher musste ich dies für einen Server-Webservice tun, und anstelle eines formalen Installationsprogramms habe ich nur Skripts zum Einrichten der App bereitgestellt, bevor der Benutzer sie ausführen konnte. Sie können die App so lange nicht mehr ausführen, bis das Setup-Skript ausgeführt wird. Sie könnten die App auch dazu bringen, sich über das Fehlen des JCE zu beschweren, und dann darum bitten, die App herunterzuladen und neu zu starten?

Djangofan
quelle
7
"Lass meine App laufen, ohne Dateien auf dem Endbenutzer-Computer zu überschreiben "
erickson
Ich habe meine Antwort vollständig bearbeitet, da meine ursprüngliche Antwort falsch war.
Djangofan