Как избежать установки файлов политики Unlimited Strength JCE при развертывании приложения?

У меня есть приложение, использующее 256-битное шифрование AES, которое не поддерживается Java из коробки. Я знаю, что для правильной работы я устанавливаю jar-файлы неограниченной силы JCE в папку безопасности. Это нормально для меня как разработчика, я могу их установить.

У меня вопрос: поскольку это приложение будет распространяться, конечные пользователи, скорее всего, не будут устанавливать эти файлы политик. Когда конечный пользователь загружает их только для того, чтобы приложение работало, решение не является привлекательным.

Есть ли способ заставить мое приложение работать без перезаписи файлов на компьютере конечного пользователя? Программное обеспечение сторонних производителей, которое может справиться с этим без установленных файлов политики? Или способ просто сослаться на эти файлы политики из JAR?


person Community    schedule 24.07.2009    source источник
comment
Взгляните сюда: docs.oracle .com / javase / 1.5.0 / docs / guide / security / jce /.   -  person Petey B    schedule 31.05.2012
comment
Я подозреваю, что намерение Sun / Oracle состояло в том, чтобы клиент использовал менее безопасный шифр, чтобы АНБ могло отслеживать соединение. Я не шучу и не параноик, но криптография рассматривается как оружие, и существуют запреты на экспорт при совместном использовании шифрования.   -  person Sled    schedule 23.09.2015


Ответы (11)


Есть несколько часто цитируемых решений этой проблемы. К сожалению, ни один из них не является полностью удовлетворительным:

  • Установите файлы политики неограниченной стойкости. Хотя это, вероятно, правильное решение для вашей рабочей станции разработчика, быстро становится серьезной проблемой (если не препятствием), когда нетехнические пользователи устанавливают файлы на каждый компьютер. Невозможно распространить файлы вместе с вашей программой; они должны быть установлены в каталог JRE (который может быть даже доступен только для чтения из-за разрешений).
  • Пропустите JCE API и используйте другую криптографическую библиотеку, например Bouncy Castle. Для этого подхода требуется дополнительная библиотека размером 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, где он не нужен. В конце концов, будучи уродливым взломом, он, скорее всего, не работает на виртуальных машинах других производителей.

Он также не работает в Oracle Java 6, потому что там скрыты частные классы JCE. Обфускация не меняется от версии к версии, поэтому технически поддержка Java 6 все еще возможна.

person ntoskrnl    schedule 18.03.2014
comment
Решение для отражения может нарушать Лицензионное соглашение Java: F. ОГРАНИЧЕНИЯ, КАСАЮЩИЕСЯ ТЕХНОЛОГИИ JAVA. Вы не можете ... изменять поведение ... классов, интерфейсов или подпакетов, которые каким-либо образом идентифицированы как 'java', 'javax', 'sun', 'oracle' или аналогичное соглашение ... - person M. Dudley; 02.09.2014
comment
@ М.Дадли Может быть. Проконсультируйтесь с юристом перед отправкой продукта, который содержит этот фрагмент кода, если он вас беспокоит. - person ntoskrnl; 03.09.2014
comment
Нет возможности распространять файлы вместе с вашей программой; они должны быть установлены в каталог JRE - я не эксперт, но насколько я понимаю, распространять / связывать jre с вашей программой, включая файлы политик JCE, законно, при условии, что вы соблюдаете условия лицензирования. - person peabody; 28.01.2015
comment
@peabody В некоторых случаях, безусловно, можно включить JRE размером 100 МБ в вашу программу. Но в противном случае пользователям все равно придется устанавливать файлы политики вручную, даже если вы включите их в свою программу (по разным причинам, например, из-за прав доступа к файлам). По моему опыту, многие пользователи на это не способны. - person ntoskrnl; 30.01.2015
comment
Возможно, я столкнулся с тем же сценарием - ›stackoverflow.com/questions/30792156/ просто для того, чтобы протестировать удаление этого ограничения, по-прежнему не пытались подключиться через что-либо меньшее, чем TLSv1.2 - person jmj; 12.06.2015
comment
Я получаю эту ошибку после запуска этого: java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required - person Andrew; 24.08.2016
comment
Похоже, что решение для отражения просто перестало работать в 1.8.0_112. Он работает в 1.8.0_111, но не в 112. - person John L; 27.10.2016
comment
@JohnL Я использую это в приложении. После того, как я столкнулся с проблемой с полем final в 8u111, я изменил его, чтобы оно могло изменить последнее поле, следуя этому ответу. Результат примерно такой же, как и в новой версии ntoskrnl, за исключением того, что я не объявлял modifiersField как final. Один из моих пользователей сообщает, что он работает и в 8u112. - person Arjan; 27.11.2016
comment
Все это связано с загадочным законом о Второй мировой войне, в котором упоминается экспорт (который включает размещение в Интернете) сильных криптографических реализаций, что правительство США сравнивало с экспортом боеприпасов (оружия / оружия). Фил Циммерман из PGP был привлечен к ответственности в этом направлении - поэтому Oracle до недавнего времени не предлагала надежные криптографические библиотеки как часть стандартной загрузки Java. en.wikipedia.org/wiki/ - person Darrell Teague; 09.01.2017

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

Согласно JDK-8170157 неограниченная криптографическая политика теперь включена по умолчанию.

Конкретные версии из выпуска JIRA:

  • Java 9 (10, 11 и т.д ..): Любой официальный релиз!
  • Java 8u161 или новее (доступно сейчас)
  • Java 7u171 или новее (доступно только через My Oracle Support)
  • Java 6u181 или новее (доступно только через My Oracle Support)

Обратите внимание, что если по какой-то странной причине в Java 9 требуется старое поведение, его можно настроить с помощью:

Security.setProperty("crypto.policy", "limited");
person bluemind    schedule 06.10.2016
comment
Фактически, эта политика используется по умолчанию, поэтому в Java 9 никаких действий не требуется! - person ntoskrnl; 08.07.2017
comment
По состоянию на 2018/01/14 (последний Oracle JDK - 8u151 / 152) он все еще не включен по умолчанию на Java 8, спустя более года после того, как этот ответ был первоначально написан ... Однако, согласно java.com/en/jre-jdk-cryptoroadmap.html это предназначено для GA в 2018 г. / 16.01 - person Alex; 14.01.2018
comment
В моем случае и для меня, чтобы получить оценку A на этом сайте: ssllabs.com/ssltest ... Я должен установить это так: Security.setProperty (crypto.policy, unlimited); затем ... установите server.ssl.ciphers в my applications.properties с 256-ю алгоритмами, указанными в этой статье - ›weakdh.org/sysadmin.html - person Artanis Zeratul; 25.06.2019
comment
Также актуально для OpenJDK 8-Installations. См .: stackoverlow-Article: Связана ли политика JCE с openjdk 8? - person leole; 27.03.2020

Вот решение: 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) {
    }
}
person mike    schedule 25.01.2015
comment
Это то же самое решение, что и мое, за исключением части defaultPolicy. Сообщение в блоге датировано моим ответом. - person ntoskrnl; 12.04.2016
comment
Но правильно ли это делать? Может ли этот код в реальном времени бросить вызов безопасности приложения? Я не уверен, пожалуйста, помогите мне понять его влияние. - person Dish; 01.08.2016
comment
Я получаю эту ошибку после запуска этого: java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required - person Andrew; 24.08.2016
comment
Начиная с Java 8 build 111, этого решения будет недостаточно, поскольку поле isRestricted стало окончательным (bugs.openjdk.java.net/browse/JDK-8149417). Ответ @ntoskrnl заботится о любом возможном включении последнего модификатора. Комментарий @ M.Dudley к лицензионному соглашению Java также применим. - person MPelletier; 20.10.2016

Начиная с JDK 8u102 опубликованные решения, основанные на отражении, больше не будут работать: поле, установленное этими решениями, теперь равно final (https://bugs.openjdk.java.net/browse/JDK-8149417).

Похоже, он вернулся либо к (а) использованию Bouncy Castle, либо (б) к установке файлов политики JCE.

person Sam Roberton    schedule 28.07.2016
comment
Вы всегда можете использовать больше отражения stackoverflow.com/questions/3301635/ - person Universal Electricity; 14.08.2016
comment
Да, решение @M.Dudley по-прежнему будет работать для поля isRestricted, потому что оно заботится о возможном добавлении последнего модификатора. - person MPelletier; 20.10.2016
comment
В новом выпуске JDK 8u151 добавлено свойство New Security для управления политикой шифрования. Итог: удалите # из строки # crypto.policy = unlimited в lib \ security \ java.security: oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html - person hemisphire; 19.10.2017

Насколько я могу судить, Bouncy Castle все еще требует установки фляг.

Я провел небольшой тест, и он, похоже, подтвердил это:

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

person timothyjc    schedule 08.05.2011

Альтернативную библиотеку криптографии можно найти на странице Bouncy Castle. Он имеет AES и множество дополнительных функций. Это либеральная библиотека с открытым исходным кодом. Однако вам придется использовать легкий проприетарный API Bouncy Castle, чтобы это работало.

person Community    schedule 05.08.2009
comment
Они являются отличным поставщиком криптовалют, но для работы с большими ключами по-прежнему требуется файл JCE неограниченной надежности. - person John Meagher; 05.08.2009
comment
Если вы используете API Bouncy Castle напрямую, вам не нужны файлы неограниченной прочности. - person laz; 20.08.2009

Вы можете использовать метод

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

чтобы проверить доступную длину ключа, используйте ее и проинформируйте пользователя о том, что происходит. Что-то, говорящее о том, что ваше приложение возвращается к 128-битным ключам, например, из-за того, что файлы политики не установлены. Пользователи, заботящиеся о безопасности, будут устанавливать файлы политик, другие продолжат использовать более слабые ключи.

person Christian Schulte    schedule 26.07.2015

Для нашего приложения у нас была архитектура клиент-сервер, и мы разрешали дешифрование / шифрование данных только на уровне сервера. Следовательно, файлы JCE нужны только там.

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

Это заставило его работать.

person Mohamed Mansour    schedule 24.07.2009

Вот обновленная версия ответа ntoskrnl. Он дополнительно содержит функцию для удаления последнего модификатора, такого как Arjan, упомянутого в комментариях.

Эта версия работает с 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"));
}
person xoned    schedule 18.05.2017
comment
Работает нормально, но строка ((Map<?, ?>) perms.get(defaultPolicy)).clear(); выдает ошибку компилятора. Комментирование не влияет на его функциональность. Нужна ли эта линия? - person Andreas Unterweger; 06.09.2017

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

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;

// 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
    }
}
person Vadzim    schedule 16.10.2017

Во время установки программы просто предложите пользователю загрузить пакетный сценарий DOS или сценарий оболочки Bash и скопировать JCE в соответствующее системное место.

Раньше мне приходилось делать это для серверного веб-сервиса, и вместо формального установщика я просто предоставлял сценарии для настройки приложения, прежде чем пользователь сможет его запустить. Вы можете сделать приложение недоступным для запуска, пока они не запустят сценарий установки. Вы также можете заставить приложение пожаловаться на отсутствие JCE, а затем попросить загрузить и перезапустить приложение?

person djangofan    schedule 24.07.2009
comment
заставить мое приложение работать без перезаписи файлов на машине конечного пользователя - person erickson; 06.08.2009
comment
Я полностью отредактировал свой ответ, так как мой первоначальный ответ был неправильным. - person djangofan; 19.10.2013