代码之家  ›  专栏  ›  技术社区  ›  Ken

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

  •  159
  • Ken  · 技术社区  · 15 年前

    我有一个应用程序,使用256位AES加密,这是不支持Java的开箱即用。我知道为了让它正常工作,我在security文件夹中安装了JCE unlimited strength jars。这是很好的,我作为开发人员,我可以安装它们。

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

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

    11 回复  |  直到 10 年前
        1
  •  177
  •   ntoskrnl    7 年前

    • 安装 unlimited strength policy files 虽然这可能是适合您的开发工作站的解决方案,但让非技术用户在每台计算机上安装文件很快就会成为一个主要的麻烦(如果不是障碍的话)。有 要随程序分发这些文件,必须将它们安装在JRE目录中(由于权限的原因,该目录甚至可能是只读的)。
    • 并使用另一个加密库,如 Bouncy Castle 之前 这个解决方案也不允许您使用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密码套件将无法工作。

    这段代码适用于oraclejava7和8,并自动跳过java9和OpenJDK上不需要它的过程。毕竟是一个丑陋的黑客,它可能不适用于其他供应商的虚拟机。

    它在oraclejava6上也不起作用,因为私有JCE类在那里是模糊的。不过,混淆并不会随着版本的不同而改变,因此从技术上讲,仍然可以支持Java6。

        2
  •  90
  •   bluemind    6 年前

    现在已经不需要这样了

    JDK-8170157

    JIRA发行的具体版本:

    • Java9(10、11等):任何官方版本!
    • Java 8u161或更高版本(可用 现在 )
    • Java 7u171或更高版本(仅通过“我的Oracle支持”提供)
    • Java 6u181或更高版本(仅通过“我的Oracle支持”提供)

    请注意,如果出于某种奇怪的原因Java 9中需要旧的行为,可以使用以下方法设置:

    Security.setProperty("crypto.policy", "limited");
    
        3
  •  22
  •   mike    10 年前

    以下是解决方案: 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
  •  14
  •   Sam Roberton    8 年前

    final ( https://bugs.openjdk.java.net/browse/JDK-8149417 ).

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

        5
  •  13
  •   timothyjc    14 年前

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

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

        6
  •  10
  •   Maarten Bodewes    13 年前

    对于另一个加密库,请看 Bouncy Castle

        7
  •  5
  •   Christian Schulte    9 年前

    你可以用这个方法

    javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)
    

    要测试可用的密钥长度,请使用它并告知用户正在发生什么。例如,由于没有安装策略文件,说明应用程序正在退回到128位密钥。有安全意识的用户将安装策略文件,其他用户将继续使用较弱的密钥。

        8
  •  3
  •   Mohamed Mansour    15 年前

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

    我们遇到了另一个问题,我们需要在客户机上更新一个安全jar,通过JNLP,它会覆盖 ${java.home}/lib/security/ 以及第一次运行时的JVM。

    这就成功了。

        9
  •  2
  •   Community CDub    8 年前

    以下是的更新版本 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"));
    }
    
        10
  •  2
  •   Vadzim    7 年前

    以下是@ntoskrnl代码的修改版本 isRestrictedCryptography 核对人 actual Cipher.getMaxAllowedKeyLength

    static {
        UnlimitedKeyStrengthJurisdictionPolicy.ensure();
    }
    

    正如@cranphin的答案所预测的那样,当java8u162中的无限制策略默认可用时,这段代码将正确地停止与反射的冲突。


    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
        }
    }
    
        11
  •  -1
  •   djangofan    11 年前

    在安装程序的过程中,只需提示用户并下载DOS批处理脚本或bashshell脚本,然后将JCE复制到适当的系统位置。

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