部署应用程序时如何避免安装“Unlimited Strength”JCE策略文件?

时间:2009-07-24 19:27:10

标签: java aes jce policyfiles

我有一个使用256位AES加密的应用程序,Java不支持开箱即用。我知道要使其正常运行我在安全文件夹中安装JCE无限强度jar。这对我作为开发人员来说很好,我可以安装它们。

我的问题是,由于此应用程序将被分发,最终用户很可能不会安装这些策略文件。让最终用户下载这些只是为了使应用程序功能不是一个有吸引力的解决方案。

有没有办法让我的应用运行而不会覆盖最终用户机器上的文件?可以在没有安装策略文件的情况下处理它的第三方软件?或者从JAR中引用这些策略文件的方法?

11 个答案:

答案 0 :(得分:167)

这个问题有几个常见的解决方案。不幸的是,这些都不是完全令人满意的:

  • 安装unlimited strength policy files虽然这可能是您的开发工作站的正确解决方案,但非技术用户安装后很快就会成为一个主要的麻烦(如果不是障碍)每台计算机上的文件。使用您的程序分发文件无法;它们必须安装在JRE目录中(由于权限,它甚至可能是只读的)。
  • 跳过JCE API 并使用其他加密库,例如Bouncy Castle。这种方法需要额外的1MB库,这可能是一个很大的负担,具体取决于应用程序。复制标准库中包含的功能也很愚蠢。显然,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.

答案 1 :(得分:81)

现在不再需要 Java 9 ,也不再需要Java 6,7或8的任何最新版本。最后! :)

JDK-8170157,默认情况下启用无限加密政策。

JIRA问题的具体版本:

  • Java 9:任何官方发布!
  • Java 8u161或更高版本(可用现在
  • Java 7u171或更高版本(仅通过&#39; My Oracle Support&#39;)
  • Java 6u181或更高版本(仅通过&#39; My Oracle Support&#39;)

请注意,如果由于某些奇怪的原因在Java 9中需要旧行为,可以使用以下命令设置:

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

答案 2 :(得分: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) {
    }
}

答案 3 :(得分:13)

就我所知,Bouncy Castle仍然需要安装罐子。

我做了一点测试,似乎证实了这一点:

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

答案 4 :(得分:13)

从JDK 8u102开始,依赖于反射的已发布解决方案将不再有效:这些解决方案设置的字段现在为finalhttps://bugs.openjdk.java.net/browse/JDK-8149417)。

看起来它回到(a)使用Bouncy Castle,或(b)安装JCE策略文件。

答案 5 :(得分:8)

对于替代加密库,请查看Bouncy Castle。它具有AES和许多附加功能。它是一个自由的开源库。您将不得不使用轻量级的专有Bouncy Castle API来实现此目的。

答案 6 :(得分:4)

您可以使用方法

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

测试可用的密钥长度,使用它并告知用户正在进行的操作。例如,由于未安装策略文件,您的应用程序会回退到128位密钥。注重安全的用户将安装策略文件,其他用户将继续使用较弱的密钥。

答案 7 :(得分:3)

对于我们的应用程序,我们有一个客户端服务器架构,我们只允许在服务器级别解密/加密数据。因此只需要那里的JCE文件。

我们还有另一个问题,我们需要通过JNLP更新客户端计算机上的安全jar,它会在第一次运行时覆盖${java.home}/lib/security/和JVM中的库。

这使它成功。

答案 8 :(得分:2)

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

答案 9 :(得分:1)

以下是@ ntoskrnl代码的修改版本,其中static { UnlimitedKeyStrengthJurisdictionPolicy.ensure(); } 检查actual Cipher.getMaxAllowedKeyLength,slf4j记录并支持应用程序引导程序中的单例初始化,如下所示:

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

默认情况下,当Java 8u162中的无限制策略可用时,此代码将正确地停止使用反射进行修改,如@ cranphin的回答所预测的那样。

<li class="@(isActive ? "active" : " ")">My link here</li>

答案 10 :(得分:0)

在程序安装过程中,只需提示用户并下载DOS Batch脚本或Bash shell脚本,并将JCE复制到正确的系统位置。

我以前必须为服务器web服务执行此操作,而不是正式的安装程序,我只是提供脚本来设置应用程序,然后用户才能运行它。您可以使应用程序无法运行,直到它们运行安装脚本。您还可以让应用程序抱怨JCE丢失,然后要求下载并重新启动应用程序?