Вопрос по policyfiles, aes, java, jce – Как избежать установки файлов политики JCE «Unlimited Strength» при развертывании приложения?

159

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

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

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

Посмотрите здесь:docs.oracle.com/javase/1.5.0/docs/guide/security/jce/… Petey B
Я подозреваю, что намерение Sun / Oracle заключалось в том, что клиент будет использовать менее безопасный шифр, чтобы АНБ могло отслеживать соединение. Я не шучу и не параноик, но криптография считается оружием и естьexport bans on sharing encryption. ArtB

Ваш Ответ

11   ответов
165

ни один из них не является полностью удовлетворительным:

Install the unlimited strength policy files. While this is probably the right solution for your development workstation, it quickly becomes a major hassle (if not a roadblock) to have non-technical users install the files on every computer. There is no way to distribute the files with your program; they must be installed in the JRE directory (which may even be read-only due to permissions). Skip the JCE API and use another cryptography library such as Bouncy Castle. This approach requires an extra 1MB library, which may be a significant burden depending on the application. It also feels silly to duplicate functionality included in the standard libraries. Obviously, the API is also completely different from the usual JCE interface. (BC does implement a JCE provider, but that doesn't help because the key strength restrictions are applied before handing over to the implementation.) This solution also won't let you use 256-bit TLS (SSL) cipher suites, because the standard TLS libraries call the JCE internally to determine any restrictions.

But then there's reflection. Есть ли что-то, что вы не можете сделать, используя отражение?

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.

@ М.Дадли Может быть. Проконсультируйтесь с юристом перед отправкой продукта, который содержит этот фрагмент кода, если он касается вас.
Похоже, что отражение решение просто перестало работать в 1.8.0_112. Это работает в 1.8.0_111, но не 112.
@peabody Включение 100-мегабайтного JRE в вашу программу, безусловно, вариант в некоторых случаях. Но если нет, пользователям все равно придется устанавливать файлы политики вручную, даже если вы включаете их в свою программу (по различным причинам, таким как права доступа к файлам). По моему опыту, многие пользователи не способны на это.
@JohnL Я использую это в приложении. После столкновения с проблемойfinal поле в 8u111, я изменил его, чтобы он мог изменить окончательное поле, после этогоanswer, Результат примерно такой же, как у новой версии ntoskrnl, за исключением того, что я не объявлялmodifiersField какfinal, Один из моих пользователей сообщает, что он работает и в 8u112.
Отражение раствора может нарушатьJava License Agreement: "F. ОГРАНИЧЕНИЯ JAVA ТЕХНОЛОГИИ. Вы не можете ... изменять поведение ... классов, интерфейсов или подпакетов, которые каким-либо образом определены как "java", "javax", "sun", "oracle". или аналогичное соглашение ... & quot;
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(); выдает ошибку компилятора. Комментирование не влияет на его функциональность. Нужна ли эта линия?
3

и мы разрешали только дешифрование / шифрование данных на уровне сервера. Следовательно файлы JCE нужны только там.

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

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

79

Java 9, ни для какого-либо недавнего выпуска Java 6, 7 или 8. Наконец! :)

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

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

Java 9: Any official release! Java 8u161 or later (Available now) Java 7u171 or later (Only available through 'My Oracle Support') Java 6u181 or later (Only available through 'My Oracle Support')

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

Security.setProperty("crypto.policy", "limited");
Соответствующая ссылка:bugs.openjdk.java.net/browse/JDK-8170157
Фактически, эта политика используется по умолчанию, поэтому никаких действий в Java 9 не требуется!
По состоянию на 2018/01/14 (последняя версия Oracle JDK - 8u151 / 152), эта функция все еще не включена по умолчанию в Java 8, через год после того, как этот ответ был первоначально написан ...java.com/en/jre-jdk-cryptoroadmap.html это предназначено для GA на 2018/01/16
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 J,CE.
//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) {
    }
}
Но правильно ли это делать? В реальном времени этот код может бросить вызов безопасности приложения? Я не уверен, пожалуйста, помогите мне понять его влияние.
Это то же решение, что и у меня, за исключением без & quot; defaultPolicy & quot; часть. Сообщение в блоге датировано после моего ответа.
Начиная с Java 8 build 111, этого решения будет недостаточно, так какisRestricted поле стало окончательным (bugs.openjdk.java.net/browse/JDK-8149417). Ответ @ ntoskrnl позаботится о любом возможном включении окончательного варианта ответа. модификатор. Комментарий @ M.Dudley к Лицензионному соглашению Java также остается в силе.
Я получаю эту ошибку после запуска этого:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
13

Bouncy Castle по-прежнему требует установки банок.

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

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

13

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

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

Вы всегда можете использовать больше отраженийstackoverflow.com/questions/3301635/…
Да, решение @M.Dudley все еще будет работать дляisRestricted поле, потому что оно заботится о возможном добавлении «окончательного» модификатор.
Новый выпуск JDK 8u151 имеет «Новое свойство безопасности для управления политикой шифрования». Итог: удалите & quot; # & quot; из строки & quot; # crypto.policy = unlimited & quot; в & quot; lib \ security \ java.security & quot ;:oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
0

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;

// 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
    }
}
4

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

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

8

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

Если вы используете API Bouncy Castle напрямую, вам не нужны файлы неограниченной силы.
Они являются отличным провайдером криптографии, но для работы с большими ключами требуется файл JCE неограниченной прочности.
0

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

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

& quot; заставить мое приложение работатьwithout overwriting files на компьютере конечного пользователя & quot;
Я полностью отредактировал свой ответ, поскольку мой первоначальный ответ был неправильным.

Похожие вопросы