Як уникнути встановлення файлів політик JCE "Необмежена сила" під час розгортання програми?


169

У мене є додаток, який використовує 256-бітне шифрування AES, яке не підтримується Java поза коробкою. Я знаю, щоб це правильно функціонувало, я встановлюю банки JCE з необмеженою міцністю у папку безпеки. Це добре, що я розробник, я можу їх встановити.

Моє питання, оскільки ця програма буде розповсюджуватися, кінцеві користувачі, швидше за все, не встановлять ці файли політики. Дозволити кінцевому користувачу завантажити їх лише для того, щоб функція програми не була привабливим рішенням.

Чи є спосіб змусити моє додаток працювати без перезапису файлів на машині кінцевого користувача? Програмне забезпечення сторонніх розробників, яке може впоратися з ним без встановлених файлів політики? Або спосіб просто посилатися на ці файли політики зсередини JAR?




11
Я підозрюю, що намір Sun / Oracle полягав у тому, що клієнт використовуватиме менш безпечний шифр, щоб НСУ міг прослуховувати з'єднання. Я не жартую і не буду параноїком, але криптографія трактується як зброя, і існують заборони експорту на спільне шифрування .
Санки

Відповіді:


175

Існує пара загально цитованих рішень цієї проблеми. На жаль, жодне з них не є цілком задовільним:

  • Встановіть файли політики необмеженої міцності . Хоча це, мабуть, правильне рішення для вашої робочої станції з розробки, це швидко стає головним клопотом (якщо не дорожнім блоком), щоб не-технічні користувачі встановлювали файли на кожному комп’ютері. Немає способу розповсюдження файлів у вашій програмі; вони повинні бути встановлені в каталозі JRE (який може бути доступним лише для читання через дозволи).
  • Пропустіть API JCE та використовуйте іншу бібліотеку криптовалют, таку як Замок Bouncy . Цей підхід вимагає додаткової бібліотеки на 1 МБ, що може бути суттєвим тягарем залежно від програми. Дуже дурно дублювати функціональні можливості, що входять до стандартних бібліотек. Очевидно, API також повністю відрізняється від звичайного інтерфейсу JCE. (BC не реалізує постачальника JCE, але це не допомагає, оскільки ключові обмеження міцності застосовуються перед передачею на реалізацію.) Це рішення також не дозволить вам використовувати 256-бітні набір шифрів TLS (SSL), оскільки стандартні бібліотеки TLS викликають JCE внутрішньо, щоб визначити будь-які обмеження.

Але тут є роздуми. Чи є щось, чого ви не можете зробити за допомогою рефлексії?

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

Просто зателефонуйте removeCryptographyRestrictions() зі статичного ініціалізатора чи іншого, перш ніж виконувати будь-які криптографічні операції.

JceSecurity.isRestricted = falseЧастина все , що необхідно для використання 256-бітних шифрів безпосередньо; однак без двох інших операцій Cipher.getMaxAllowedKeyLength()звіт буде продовжувати працювати 128, а 256-бітні набори шифрів TLS не працюватимуть.

Цей код працює в Oracle Java 7 і 8 і автоматично пропускає процес на Java 9 і OpenJDK там, де це не потрібно. Зрештою, це некрасивий хакер, він, ймовірно, не працює на VM інших постачальників.

Він також не працює на Oracle Java 6, оскільки там закриваються приватні класи JCE. Обфускація не змінюється від версії до версії, тому технічно можливо підтримувати Java 6.


23
Рішення для роздумів може порушити ліцензійну угоду Java : "F. JAVA ТЕХНОЛОГІЧНІ ОБМЕЖЕННЯ. Ви не можете ... змінювати поведінку ... класів, інтерфейсів або субпакетів, які будь-яким чином ідентифікуються як" java "," javax " , 'сонце', 'оракул' чи подібна умова ... »
М. Дадлі,

14
@ M.Dudley Можливо. Перш ніж відправити товар, який містить цей фрагмент коду, зверніться до юриста, якщо він стосується вас.
ntoskrnl

3
@peabody Включення у свою програму 100 МБ JRE - це, безумовно, варіант у деяких випадках. Але якщо ні, користувачі все одно повинні встановлювати файли політики вручну, навіть якщо ви включите їх у свою програму (через різні причини, наприклад дозволу на файли). На мій досвід, багато користувачів на це не здатні.
ntoskrnl

8
Здається, що рішення про роздуми просто перестало працювати в 1.8.0_112. Він працює в 1.8.0_111, але не 112.
Джон Л

3
@JohnL Я використовую це у додатку. finalНатрапивши на проблеми з полем у 8u111, я змінив його, щоб воно могло змінити остаточне поле, слідуючи цій відповіді . Результат приблизно такий же, як у новій версії ntoskrnl, за винятком того, що я не оголосив modifiersFieldяк final. Один з моїх користувачів повідомляє, що він працює і в 8u112.
Ар'ян

87

Тепер для Java 9 це більше не потрібно , а також для недавнього випуску Java 6, 7 або 8. Нарешті! :)

За JDK-8170157 , необмежена криптографічна політика тепер включена за замовчуванням.

Конкретні версії випуску JIRA:

  • Java 9 (10, 11 тощо): будь-який офіційний реліз!
  • Java 8u161 або новішої версії (Доступно зараз )
  • Java 7u171 або новішої версії (доступно лише через "Моя підтримка Oracle")
  • Java 6u181 або новішої версії (доступно лише через "Моя підтримка Oracle")

Зауважте, що якщо з явної причини потрібна стара поведінка в Java 9, її можна встановити, використовуючи:

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

4
Насправді ця політика є типовою, тому жодних дій, необхідних у Java 9!
ntoskrnl

Станом на 2018/01/14 (остання версія Oracle JDK становить 8u151 / 152), це все ще не включено за замовчуванням на Java 8, через рік після того, як ця відповідь була спочатку написана ... Однак згідно з java.com/en/jre -jdk-cryptoroadmap.html це призначено для GA 2018/01/16
Alex

У моєму випадку і для мене, щоб отримати позначку А на цьому веб-сайті: ssllabs.com/ssltest ... я повинен встановити це так: Security.setProperty ("crypto.policy", "unlimited"); потім ... встановіть server.ssl.ciphers у моїх програмах application.properties з 256-ти алгоритмами, зазначеними в цій статті -> слабкий.org/sysadmin.html
Артаніс Зератул,

Також актуально для 8-інсталяцій OpenJDK. Див.: Stackoverlow-Article: Чи поєднується політика СКВ з openjdk 8?
leole

22

Ось рішення: 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) {
    }
}

Це те саме рішення, що і моє, за винятком частини "defaultPolicy". Повідомлення в блозі датоване моєю відповіддю.
ntoskrnl

1
Але чи правильно це робити? Чи може в реальному часі цей код кинути виклик безпеці програми? Я не впевнений, будь ласка, допоможіть мені зрозуміти, що це вплив.
Блюдо

1
Я отримую цю помилку після запуску цього:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
Енді,

3
Що стосується Java 8 build 111, це рішення буде недостатнім, оскільки isRestrictedполе стало остаточним ( bugs.openjdk.java.net/browse/JDK-8149417 ). @ ntoskrnl відповідає на будь-яке можливе включення "остаточного" модифікатора. @ Коментар М.Дудлі щодо ліцензійної угоди Java все ще застосовується.
MPelletier


13

Станом на JDK 8u102, розміщені рішення, що покладаються на роздуми, більше не працюватимуть: поле, яке зараз встановлено для цих рішень final( https://bugs.openjdk.java.net/browse/JDK-8149417 ).

Схоже, це повернення або до (a) використання Bouncy Castle, або (b) встановлення файлів політики JCE.


7
Ви завжди можете використовувати більше роздумів stackoverflow.com/questions/3301635/…
Universal Electricity

Так, рішення М.Дудлі все одно буде працювати для isRestrictedполя, оскільки воно піклується про можливе додавання "остаточного" модифікатора.
MPelletier

1
У новому випуску JDK 8u151 є "Нова властивість безпеки для управління криптовалютою". Підсумок: видаліть "#" з рядка "# crypto.policy = unlimited" у "lib \ security \ java.security": oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
півсфера

8

Для альтернативної бібліотеки криптовалют ознайомтесь із замком Bouncy . Він має AES і багато додаткової функціональності. Це ліберальна бібліотека з відкритим кодом. Для цього вам доведеться використовувати легкий, фірмовий API Bouncy Castle.


19
Вони чудовий постачальник криптовалют, але все ж потребують файлу JCE необмеженої міцності для роботи з великими клавішами.
Джон Мізер

16
Якщо ви безпосередньо використовуєте API Bouncy Castle, вам не потрібні файли необмеженої міцності.
лаз

4

Ви можете використовувати метод

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

щоб перевірити доступну довжину ключа, використовуйте її та інформуйте користувача про те, що відбувається. Щось свідчить про те, що ваша програма повертається до 128 бітових ключів, наприклад, файли політики не встановлюються, наприклад. Користувачі, які охороняють безпеку, встановлюватимуть файли політики, інші продовжують використовувати більш слабкі клавіші.


3

Для нашої програми ми мали архітектуру сервера клієнтів, і ми дозволяли розшифровувати / шифрувати дані лише на рівні сервера. Отже, файли JCE потрібні лише там.

У нас виникла ще одна проблема, коли нам потрібно було оновити захисну систему на клієнтських машинах, через JNLP, вона перезаписує бібліотеки ${java.home}/lib/security/та JVM під час першого запуску.

Це змусило його працювати.


2

Ось оновлена ​​версія відповіді ntoskrnl . Він додатково містить функцію для видалення остаточного модифікатора, як Арджан, згаданий у коментарях.

Ця версія працює з JRE 8u111 або новішою.

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

Це добре працює, але рядок ((Map<?, ?>) perms.get(defaultPolicy)).clear();видає помилку компілятора. Коментування, здається, не впливає на його функціональність. Чи потрібна ця лінія?
Andreas Unterweger

2

Ось модифікована версія коду @ ntoskrnl, що включає isRestrictedCryptographyперевірку фактичногоCipher.getMaxAllowedKeyLength , журналу slf4j та підтримку одиночної ініціалізації з завантажувальної програми, наприклад:

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

Цей код правильно перестане керувати відображенням, коли необмежена політика стане доступною за замовчуванням у Java 8u162, як передбачає відповідь @ 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
    }
}

-1

Під час інсталяції вашої програми просто запропонуйте користувачеві та отримайте сценарій DOS Batch або скрипт оболонки Bash та скопіюйте JCE у потрібне системне місце.

Раніше я робив це для серверного веб-сервісу, а замість офіційного інсталятора я просто надав сценарії для установки програми, перш ніж користувач міг би запустити його. Ви можете зробити додаток неможливим до запуску сценарію настройки. Ви також можете змусити додаток скаржитися на відсутність СКВ, а потім попросити завантажити та перезапустити додаток?


7
"змусити мій додаток запускатись, не перезаписуючи файли на машині кінцевого користувача"
erickson

Я зробив повне редагування своєї відповіді, оскільки моя початкова відповідь була неправильною.
djangofan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.