Jak uniknąć instalowania plików zasad JCE o nieograniczonej sile podczas wdrażania aplikacji?

169

Mam aplikację używającą 256-bitowego szyfrowania AES, które nie jest obsługiwane przez Javę po wyjęciu z pudełka. Wiem, że aby to działało poprawnie, instaluję słoiki JCE o nieograniczonej sile w folderze bezpieczeństwa. Dla mnie jako programisty jest to w porządku, mogę je zainstalować.

Moje pytanie brzmi, ponieważ ta aplikacja będzie dystrybuowana, użytkownicy końcowi najprawdopodobniej nie będą mieli zainstalowanych tych plików zasad. Pobranie przez użytkownika końcowego tylko po to, aby aplikacja działała, nie jest atrakcyjnym rozwiązaniem.

Czy istnieje sposób, aby aplikacja działała bez nadpisywania plików na komputerze użytkownika końcowego? Oprogramowanie innej firmy, które może sobie z tym poradzić bez zainstalowanych plików zasad? Czy może sposób po prostu odwoływać się do tych plików strategii z pliku JAR?

Duncan Jones
źródło
11
Podejrzewam, że intencją Sun / Oracle było to, że klient użyje mniej bezpiecznego szyfru, aby NSA mógł podsłuchiwać połączenie. Nie żartuję ani nie popadam w paranoję, ale kryptografia jest traktowana jako broń i istnieją zakazy eksportu na współdzielenie szyfrowania .
Sanki

Odpowiedzi:

175

Istnieje kilka często cytowanych rozwiązań tego problemu. Niestety żaden z nich nie jest w pełni zadowalający:

  • Zainstaluj nieograniczone pliki zasad siły . Chociaż jest to prawdopodobnie właściwe rozwiązanie dla twojej programistycznej stacji roboczej, szybko staje się dużym kłopotem (jeśli nie przeszkodą), aby użytkownicy nietechniczni instalowali pliki na każdym komputerze. Nie ma możliwości rozpowszechniania plików wraz z programem; muszą być zainstalowane w katalogu JRE (który może być nawet tylko do odczytu ze względu na uprawnienia).
  • Pomiń interfejs JCE API i użyj innej biblioteki kryptograficznej, takiej jak Bouncy Castle . Takie podejście wymaga dodatkowej biblioteki 1 MB, co może być znacznym obciążeniem w zależności od aplikacji. Głupio jest również powielać funkcje zawarte w standardowych bibliotekach. Oczywiście API jest również zupełnie inne niż zwykły interfejs JCE. (BC implementuje dostawcę JCE, ale to nie pomaga, ponieważ ograniczenia siły klucza są stosowane przed przekazaniem do implementacji). standardowe biblioteki TLS wywołują wewnętrznie JCE w celu określenia wszelkich ograniczeń.

Ale jest też refleksja. Czy jest coś, czego nie możesz zrobić za pomocą odbicia?

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

Po prostu wywołaj removeCryptographyRestrictions()ze statycznego inicjatora lub takiego przed wykonaniem jakichkolwiek operacji kryptograficznych.

Ta JceSecurity.isRestricted = falseczęść jest wszystkim, czego potrzeba do bezpośredniego używania 256-bitowych szyfrów; jednak bez dwóch pozostałych operacji Cipher.getMaxAllowedKeyLength()nadal będzie raportować 128, a 256-bitowe zestawy szyfrów TLS nie będą działać.

Ten kod działa na Oracle Java 7 i 8 i automatycznie pomija proces w Javie 9 i OpenJDK, gdzie nie jest potrzebny. W końcu jest to brzydki hack, ale prawdopodobnie nie działa na maszynach wirtualnych innych dostawców.

Nie działa również na Oracle Java 6, ponieważ prywatne klasy JCE są tam zaciemnione. Obfuskacja nie zmienia się jednak z wersji na wersję, więc nadal jest technicznie możliwe wsparcie Java 6.

ntoskrnl
źródło
23
Rozwiązanie refleksyjne może naruszać Umowę licencyjną Java : „F. OGRANICZENIA DOTYCZĄCE TECHNOLOGII JAVA. Nie wolno ... zmieniać zachowania ... klas, interfejsów ani podpakietów, które są w jakikolwiek sposób identyfikowane jako„ java ”,„ javax ” , 'słońce', 'wyrocznia' lub podobna konwencja ... ”
M. Dudley,
14
@ M.Dudley może być. Jeśli dotyczy to Ciebie, skontaktuj się z prawnikiem przed wysłaniem produktu zawierającego ten fragment kodu.
ntoskrnl
3
@peabody Dołączenie 100MB JRE do programu jest z pewnością opcją w niektórych przypadkach. Ale jeśli nie, użytkownicy nadal będą musieli ręcznie zainstalować pliki zasad, nawet jeśli dołączysz je do swojego programu (z różnych powodów, takich jak uprawnienia do plików). Z mojego doświadczenia wynika, że ​​wielu użytkowników nie jest do tego zdolnych.
ntoskrnl
8
Wygląda na to, że rozwiązanie odbicia właśnie przestało działać w wersji 1.8.0_112. Działa w 1.8.0_111, ale nie 112.
John L
3
@JohnL Używam tego w aplikacji. Po kłopotach z finalpolem w 8u111 zmodyfikowałem je tak, aby mogło zmienić końcowe pole, postępując zgodnie z tą odpowiedzią . Wynik jest mniej więcej taki sam, jak w nowej wersji ntoskrnl, z tą różnicą, że nie zadeklarowałem modifiersFieldjako final. Jeden z moich użytkowników zgłasza, że ​​działa również w 8u112.
Arjan
87

Nie jest to już potrzebne w Javie 9 ani w żadnym nowym wydaniu Java 6, 7 lub 8. Wreszcie! :)

Zgodnie z JDK-8170157 nieograniczone zasady kryptograficzne są teraz domyślnie włączone.

Konkretne wersje z wydania JIRA:

  • Java 9 (10, 11 itd.): Dowolne oficjalne wydanie!
  • Java 8u161 lub nowsza (dostępna teraz )
  • Java 7u171 lub nowsza (dostępna tylko za pośrednictwem „My Oracle Support”)
  • Java 6u181 lub nowsza (dostępna tylko za pośrednictwem „My Oracle Support”)

Zauważ, że jeśli z jakiegoś dziwnego powodu stare zachowanie jest potrzebne w Javie 9, można je ustawić za pomocą:

Security.setProperty("crypto.policy", "limited");
cranphin
źródło
4
W rzeczywistości ta zasada jest domyślna, więc w Javie 9 nie są wymagane żadne działania!
ntoskrnl
Od 2018/01/14 (najnowszy Oracle JDK to 8u151 / 152) ta opcja nadal nie jest domyślnie włączona w Javie 8, ponad rok po pierwotnym napisaniu tej odpowiedzi ... Jednak według java.com/en/jre -jdk-cryptoroadmap.html to jest przeznaczone dla GA w dniu 2018/01/16
Alex,
W moim przypadku i dla mnie, aby uzyskać Znak A w tej witrynie: ssllabs.com/ssltest ... Muszę to ustawić w ten sposób: Security.setProperty ("crypto.policy", "unlimited"); następnie ... ustaw server.ssl.ciphers w my applications.properties za pomocą algorytmów opartych na 256 wskazanych w tym artykule -> slabydh.org/sysadmin.html
Artanis Zeratul
Dotyczy również ośmiu instalacji OpenJDK. Zobacz: stackoverlow-Article: Czy polityka JCE jest dołączona do openjdk 8?
Leole
22

Oto rozwiązanie: 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) {
    }
}
mikrofon
źródło
To jest to samo rozwiązanie co moje, ale bez części „defaultPolicy”. Post na blogu jest datowany na podstawie mojej odpowiedzi.
ntoskrnl
1
Ale czy to słuszne? Czy w czasie rzeczywistym ten kod może zagrozić bezpieczeństwu aplikacji? Nie jestem pewien, proszę, pomóż mi zrozumieć jego wpływ.
Dish
1
Otrzymuję ten błąd po uruchomieniu tego:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
Andy
3
Od Java 8 build 111 to rozwiązanie będzie niewystarczające, ponieważ isRestrictedpole stało się ostateczne ( bugs.openjdk.java.net/browse/JDK-8149417 ). Odpowiedź @ ntoskrnl uwzględnia ewentualne włączenie „końcowego” modyfikatora. Komentarz @ M.Dudley na temat umowy licencyjnej Java nadal ma zastosowanie.
MPelletier
13

Począwszy od JDK 8u102, opublikowane rozwiązania opierające się na refleksji nie będą już działać: polem, które te rozwiązania są ustawione, jest teraz final( https://bugs.openjdk.java.net/browse/JDK-8149417 ).

Wygląda na to, że wrócił do (a) korzystania z Bouncy Castle lub (b) instalacji plików zasad JCE.

Sam Roberton
źródło
7
Zawsze możesz użyć więcej refleksji stackoverflow.com/questions/3301635/…
Universal Electricity
Tak, rozwiązanie @ M.Dudley będzie nadal działać w tej isRestricteddziedzinie, ponieważ dba o ewentualne dodanie „końcowego” modyfikatora.
MPelletier
1
Nowa wersja JDK 8u151 ma „Nową właściwość zabezpieczeń do kontrolowania zasad kryptograficznych”. Podsumowując: usuń „#” z wiersza „# crypto.policy = unlimited” w „lib \ security \ java.security”: oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
hemisphire
8

Aby uzyskać alternatywną bibliotekę kryptograficzną, zajrzyj do Bouncy Castle . Ma AES i wiele dodatkowych funkcji. To liberalna biblioteka open source. Aby to zadziałało, będziesz musiał użyć lekkiego, zastrzeżonego interfejsu API Bouncy Castle.

Maarten Bodewes
źródło
19
Są świetnym dostawcą usług kryptograficznych, ale nadal wymagają pliku JCE o nieograniczonej sile do pracy z dużymi kluczami.
John Meagher,
16
Jeśli korzystasz z API Bouncy Castle bezpośrednio, nie potrzebujesz nieograniczonych plików siły.
laz
4

Możesz użyć metody

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

aby przetestować dostępną długość klucza, użyj go i poinformuj użytkownika o tym, co się dzieje. Coś stwierdzającego, że aplikacja spada do 128-bitowych kluczy z powodu, na przykład, plików zasad. Użytkownicy świadomi bezpieczeństwa zainstalują pliki zasad, inni będą nadal używać słabszych kluczy.

Christian Schulte
źródło
3

W przypadku naszej aplikacji mieliśmy architekturę serwera klienckiego i zezwalaliśmy na odszyfrowywanie / szyfrowanie danych tylko na poziomie serwera. Dlatego pliki JCE są potrzebne tylko tam.

Mieliśmy inny problem, w którym musieliśmy zaktualizować jar bezpieczeństwa na komputerach klienckich, za pośrednictwem JNLP, nadpisuje on biblioteki w${java.home}/lib/security/ i JVM przy pierwszym uruchomieniu.

To sprawiło, że to zadziałało.

Mohamed Mansour
źródło
2

Oto zaktualizowana wersja odpowiedzi ntoskrnl . Dodatkowo zawiera funkcję do usuwania końcowego modyfikatora, takiego jak wspomniany w komentarzach Arjan .

Ta wersja działa z JRE 8u111 lub nowszym.

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
źródło
Działa ładnie, ale wiersz ((Map<?, ?>) perms.get(defaultPolicy)).clear();powoduje błąd kompilatora. Wydaje się, że komentowanie nie wpływa na jego funkcjonalność. Czy ta linia jest konieczna?
Andreas Unterweger,
2

Oto zmodyfikowana wersja kodu @ ntoskrnl, obejmująca isRestrictedCryptographysprawdzanie przez rzeczywisteCipher.getMaxAllowedKeyLength logowanie slf4j i obsługę inicjalizacji singletona z programu ładującego aplikacji w następujący sposób:

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

Ten kod poprawnie przestałby manipulować refleksją, gdy nieograniczone zasady staną się domyślnie dostępne w Javie 8u162, jak przewiduje odpowiedź @ 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;

// /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
źródło
-1

Podczas instalacji programu wystarczy zachęcić użytkownika i pobrać skrypt DOS Batch lub skrypt powłoki Bash i skopiować JCE do odpowiedniej lokalizacji systemowej.

Kiedyś musiałem to robić dla usługi sieciowej serwera i zamiast formalnego instalatora dostarczyłem po prostu skrypty do konfiguracji aplikacji, zanim użytkownik będzie mógł ją uruchomić. Możesz uniemożliwić uruchamianie aplikacji, dopóki nie uruchomią skryptu instalacyjnego. Możesz również sprawić, by aplikacja narzekała, że ​​brakuje JCE, a następnie poprosić o pobranie i ponowne uruchomienie aplikacji?

djangofan
źródło
7
„ spraw, aby moja aplikacja działała bez nadpisywania plików na komputerze użytkownika końcowego”
erickson,
Dokonałem pełnej edycji mojej odpowiedzi, ponieważ moja początkowa odpowiedź była błędna.
djangofan