Вопрос-ответ

How to avoid installing "Unlimited Strength" JCE policy files when deploying an application?

Как избежать установки файлов политики JCE "Неограниченной мощности" при развертывании приложения?

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

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

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

Переведено автоматически
Ответ 1

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


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

Ответ 2

Теперь это больше не требуется ни для 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");
Ответ 3

Вот решение: 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) {
}
}
Ответ 4

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

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

java