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


169

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

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

有没有一种方法可以使我的应用程序运行而不会覆盖最终用户计算机上的文件?可以在不安装策略文件的情况下处理它的第三方软件?还是仅从JAR内引用这些策略文件的方法?




11
我怀疑Sun / Oracle的意图是客户端将使用不太安全的密码,以便NSA可以监听连接。我不是在开玩笑,也不是偏执狂,但是加密被视为一种武器,并且禁止共享加密
雪橇

Answers:


175

有两个通常引用的解决方案。不幸的是,这些都不是完全令人满意的:

  • 安装无限强度策略文件虽然这可能是适合您的开发工作站的解决方案,但要让非技术用户在每台计算机上安装文件,很快就会成为一个主要麻烦(如果不是障碍)。有没有办法来分发与您的程序文件; 它们必须安装在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上自动跳过该过程。毕竟是一个丑陋的骇客,它可能无法在其他供应商的VM上运行。

它在Oracle Java 6上也不起作用,因为私有的JCE类在那里被混淆了。尽管混淆不会随版本的变化而变化,所以从技术上讲,仍然可以支持Java 6。


23
反射解决方案可能违反Java许可协议:“ F. JAVA技术限制。您不得……以任何方式标识为“ java”,“ javax”的类,接口或子包的行为。 ,“ sun”,“ oracle”或类似的约定...”
M. Dudley

14
@ M.Dudley可能是。如果涉及您的代码包含此段代码的产品,请先咨询律师。
ntoskrnl 2014年

3
@peabody在某些情况下,当然可以选择在程序中包含100MB JRE。但是,如果不是这样,即使您将策略文件包含在程序中,用户仍将必须手动安装策略文件(由于各种原因,例如文件权限)。根据我的经验,许多用户无法做到这一点。
ntoskrnl 2015年

8
似乎反射解决方案仅在1.8.0_112中停止工作。它工作在1.8.0_111,而不是112
约翰大号

3
@JohnL我在应用程序中使用它。final在8u111中的字段遇到问题后,我对其进行了修改,以便可以按照此答案更改最终字段。结果与ntoskrnl的新版本大致相同,除了我没有声明modifiersFieldfinal。我的一位用户报告说它也可在8u112中使用。
Arjan

87

现在,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");

4
实际上,该策略是默认策略,因此在Java 9中无需执行任何操作!
ntoskrnl

截至2018年1月14日(最新的Oracle JDK为8u151 / 152),在Java 8上仍默认未启用此功能,早在最初编写此答案后已有一年以上的时间...但是根据java.com/en/jre -jdk-cryptoroadmap.html这是打算于2018
Alex

就我而言,要在此站点上获得A标记:ssllabs.com/ssltest ...我必须以这种方式进行设置:Security.setProperty(“ crypto.policy”,“ unlimited”); 然后...使用本文中指出的基于256个算法在我的application.properties中设置server.ssl.ciphers-> wetdh.org/sysadmin.html
Artanis Zeratul

也与OpenJDK 8安装有关。请参阅:stackoverlow-文章:JCE策略是否与openjdk 8捆绑在一起?
leole

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

除了没有“ defaultPolicy”部分外,这与我的解决方案相同。该博客文章的日期在我回答之后。
ntoskrnl 2016年

1
但这是正确的做法吗?该代码可以实时挑战应用程序的安全性吗?我不确定请帮助我了解其影响。

1
运行此命令后出现此错误:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
Andy

3
从Java 8 build 111开始,该解决方案将是不够的,因为该isRestricted领域已经成为最终领域(bugs.openjdk.java.net/browse/JDK-8149417)。@ntoskrnl的答案会考虑是否包含“最终”修饰符。@ M.Dudley对Java许可协议的评论仍然适用。
MPelletier '16


13

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

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


7
你总是可以使用更多的思考stackoverflow.com/questions/3301635/...
通用电气

是的,@ M.Dudley的解决方案仍然适用于该isRestricted领域,因为它考虑了可能添加的“最终”修饰符。
MPelletier '16

1
新版本的JDK 8u151具有“用于控制加密策略的新安全性”。底线:从“ lib \ security \ java.security”中的“#crypto.policy = unlimited”行中删除“#”:oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
hemisphire

8

对于其他加密库,请查看Bouncy Castle。它具有AES和许多附加功能。这是一个开放的开源库。但是,您必须使用轻巧的专有Bouncy Castle API才能正常工作。


19
他们是出色的加密提供商,但仍需要无限强度的JCE文件才能使用大密钥。
约翰·梅格

16
如果直接使用Bouncy Castle API,则不需要无限强度文件。
laz

4

你可以使用方法

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

测试可用的密钥长度,使用它并通知用户正在发生的事情。例如,某件事表明您的应用程序由于未安装策略文件而回退到128位密钥。具有安全意识的用户将安装策略文件,其他用户将继续使用较弱的密钥。


3

对于我们的应用程序,我们具有客户端服务器体系结构,并且仅允许在服务器级别解密/加密数据。因此,仅在此处需要JCE文件。

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

这使它起作用。


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

它工作得很好,但是该行会((Map<?, ?>) perms.get(defaultPolicy)).clear();产生编译器错误。注释掉似乎并不影响其功能。这行是必要的吗?
Andreas Unterweger

2

这是@ntoskrnl 代码的修改版本,具有isRestrictedCryptography通过实际的Cipher.getMaxAllowedKeyLength slf4j日志进行检查的功能,并支持从应用程序引导程序进行的单例初始化,如下所示:

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

当@cranphin的答案预言时,在Java 8u162中默认情况下无限策略可用时,此代码将正确停止反射处理。


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;

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

-1

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

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


7
“使我的应用程序运行而不会覆盖最终用户计算机上的文件
erickson

我对答案做了完整的编辑,因为最初的答案是错误的。
djangofan 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.