如何避免在部署应用程序时安装“无限强度”JCE 策略文件?

2022-08-31 06:56:03

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

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

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


答案 1

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

  • 安装无限强度策略文件虽然这可能是您的开发工作站的正确解决方案,但让非技术用户在每台计算机上安装文件很快就会成为一个主要的麻烦(如果不是障碍)。没有办法随程序一起分发文件;它们必须安装在 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()

该部分是直接使用256位密码所需的全部内容;但是,如果没有其他两个操作,仍将继续报告128,并且256位TLS密码套件将不起作用。JceSecurity.isRestricted = falseCipher.getMaxAllowedKeyLength()

此代码适用于 Oracle Java 7 和 8,并在不需要的地方自动跳过 Java 9 和 OpenJDK 上的过程。毕竟,这是一个丑陋的黑客,它可能不适用于其他供应商的VM。

它也不适用于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");

推荐