Java 部署应用程序时如何避免安装“无限强度”JCE 策略文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1179672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to avoid installing "Unlimited Strength" JCE policy files when deploying an application?
提问by
I have an app that uses 256-bit AES encryption which is not supported by Java out of the box. I know to get this to function correctly I install the JCE unlimited strength jars in the security folder. This is fine for me being the developer, I can install them.
我有一个使用 Java 不支持的 256 位 AES 加密的应用程序。我知道要使其正常运行,我在安全文件夹中安装了 JCE 无限强度罐子。这对我作为开发人员来说很好,我可以安装它们。
My question is since this app will be distributed, end users most likely will not have these policy files installed. Having the end user download these just to make the app function is not an attractive solution.
我的问题是,由于此应用程序将被分发,最终用户很可能不会安装这些策略文件。让最终用户下载这些只是为了使应用程序具有功能并不是一个有吸引力的解决方案。
Is there a way to make my app run without overwriting files on the end user machine? A third party software that can handle it without the policy files installed? Or a way to just reference these policy files from within a JAR?
有没有办法让我的应用程序在不覆盖最终用户机器上的文件的情况下运行?没有安装策略文件就可以处理它的第三方软件?或者只是从 JAR 中引用这些策略文件的方法?
回答by djangofan
During installation of your program, just prompt the user and have a DOS Batch script or a Bash shell script download and copy the JCE into the proper system location.
在安装程序期间,只需提示用户并下载 DOS 批处理脚本或 Bash shell 脚本并将 JCE 复制到正确的系统位置即可。
I used to have to do this for a server webservice and instead of a formal installer, I just provided scripts to setup the app before the user could run it. You can make the app un-runnable until they run the setup script. You could also make the app complain that the JCE is missing and then ask to download and restart the app?
我曾经必须为服务器网络服务执行此操作,而不是正式的安装程序,我只是提供了脚本来在用户运行之前设置应用程序。您可以使应用程序无法运行,直到它们运行安装脚本。您还可以让应用程序抱怨缺少 JCE,然后要求下载并重新启动应用程序?
回答by Mohamed Mansour
For our application, we had a client server architecture and we only allowed decrypting/encrypting data in the server level. Hence the JCE files are only needed there.
对于我们的应用程序,我们有一个客户端服务器架构,我们只允许在服务器级别解密/加密数据。因此,只有在那里需要 JCE 文件。
We had another problem where we needed to update a security jar on the client machines, through JNLP, it overwrites the libraries in${java.home}/lib/security/
and the JVM on first run.
我们遇到了另一个问题,我们需要通过 JNLP 更新客户端计算机上的安全 jar,它会${java.home}/lib/security/
在首次运行时覆盖JVM 中的库和 JVM。
That made it work.
这使它起作用。
回答by Mohamed Mansour
For an alternative cryptography library, have a look at Bouncy Castle. It has AES and a lot of added functionality. It's a liberal open source library. You will have to use the lightweight, proprietary Bouncy Castle API for this to work though.
对于另一种加密库,请查看Bouncy Castle。它具有 AES 和许多附加功能。它是一个自由的开源库。不过,您必须使用轻量级的专有 Bouncy Castle API 才能使其正常工作。
回答by timothyjc
Bouncy Castle still requires jars installed as far as I can tell.
据我所知,充气城堡仍然需要安装罐子。
I did a little test and it seemed to confirm this:
我做了一个小测试,它似乎证实了这一点:
http://www.bouncycastle.org/wiki/display/JA1/Frequently+Asked+Questions
http://www.bouncycastle.org/wiki/display/JA1/Frequently+Asked+Questions
回答by ntoskrnl
There are a couple of commonly quoted solutions to this problem. Unfortunately neither of these are entirely satisfactory:
这个问题有几个常用的解决方案。不幸的是,这些都不是完全令人满意的:
- Install the unlimited strength policy files.While this is probably the right solution for your development workstation, it quickly becomes a major hassle (if not a roadblock) to have non-technical users install the files on every computer. There is no wayto distribute the files with your program; they must be installed in the JRE directory (which may even be read-only due to permissions).
- Skip the JCE APIand use another cryptography library such as Bouncy Castle. This approach requires an extra 1MB library, which may be a significant burden depending on the application. It also feels silly to duplicate functionality included in the standard libraries. Obviously, the API is also completely different from the usual JCE interface. (BC does implement a JCE provider, but that doesn't help because the key strength restrictions are applied beforehanding over to the implementation.) This solution also won't let you use 256-bit TLS (SSL) cipher suites, because the standard TLS libraries call the JCE internally to determine any restrictions.
- 安装无限强度策略文件。虽然这可能是您的开发工作站的正确解决方案,但让非技术用户在每台计算机上安装文件很快就会成为一个主要的麻烦(如果不是障碍的话)。有没有办法来分发与您的程序文件; 它们必须安装在 JRE 目录中(由于权限,该目录甚至可能是只读的)。
- 跳过 JCE API并使用另一个加密库,例如Bouncy Castle。这种方法需要一个额外的 1MB 库,这可能是一个很大的负担,具体取决于应用程序。复制标准库中包含的功能也感觉很愚蠢。显然,API 也与通常的 JCE 接口完全不同。(BC 确实实现了 JCE 提供程序,但这无济于事,因为在移交给实现之前应用了密钥强度限制。)此解决方案也不会让您使用 256 位 TLS (SSL) 密码套件,因为标准 TLS 库在内部调用 JCE 以确定任何限制。
But then there's reflection.Is there anything you can't do using reflection?
但随后就有了反思。使用反射有什么不能做的吗?
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"));
}
Simply call removeCryptographyRestrictions()
from a static initializer or such before performing any cryptographic operations.
removeCryptographyRestrictions()
在执行任何加密操作之前,只需从静态初始化程序等调用。
The JceSecurity.isRestricted = false
part is all that is needed to use 256-bit ciphers directly; however, without the two other operations, Cipher.getMaxAllowedKeyLength()
will still keep reporting 128, and 256-bit TLS cipher suites won't work.
这JceSecurity.isRestricted = false
部分是直接使用 256 位密码所需的全部内容;但是,如果没有其他两个操作,Cipher.getMaxAllowedKeyLength()
仍然会报告 128,并且 256 位 TLS 密码套件将不起作用。
This code works on Oracle Java 7 and 8, and automatically skips the process on Java 9 and OpenJDK where it's not needed. Being an ugly hack after all, it likely doesn't work on other vendors' VMs.
此代码适用于 Oracle Java 7 和 8,并自动跳过 Java 9 和 OpenJDK 上不需要的过程。毕竟作为一个丑陋的黑客,它可能不适用于其他供应商的虚拟机。
It also doesn't work on Oracle Java 6, because the private JCE classes are obfuscated there. The obfuscation does not change from version to version though, so it is still technically possible to support Java 6.
它也不适用于 Oracle Java 6,因为私有 JCE 类在那里被混淆了。不过,混淆不会因版本而异,因此从技术上讲,支持 Java 6 仍然是可能的。
回答by mike
Here is solution: http://middlesphere-1.blogspot.ru/2014/06/this-code-allows-to-break-limit-if.html
这是解决方案: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) {
}
}
回答by Christian Schulte
You could use method
你可以使用方法
javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)
to test the available key length, use that and inform the user about what is going on. Something stating that your application is falling back to 128 bit keys due to the policy files not being installed, for example. Security conscious users will install the policy files, others will continue using weaker keys.
测试可用的密钥长度,使用它并通知用户发生了什么。例如,说明您的应用程序由于未安装策略文件而回退到 128 位密钥的内容。有安全意识的用户将安装策略文件,其他人将继续使用较弱的密钥。
回答by Sam Roberton
As of JDK 8u102, the posted solutions relying on reflection will no longer work: the field that these solutions set is now final
(https://bugs.openjdk.java.net/browse/JDK-8149417).
从 JDK 8u102 开始,依赖反射的已发布解决方案将不再有效:这些解决方案设置的字段现在是final
( https://bugs.openjdk.java.net/browse/JDK-8149417)。
Looks like it's back to either (a) using Bouncy Castle, or (b) installing the JCE policy files.
看起来它又回到了 (a) 使用 Bouncy Castle,或 (b) 安装 JCE 策略文件。
回答by cranphin
This is now no longer needed for Java 9, nor for any recent release of Java 6, 7, or 8. Finally! :)
Java 9现在不再需要它,也不再需要Java 6、7 或 8 的任何最新版本。终于!:)
Per JDK-8170157, the unlimited cryptographic policy is now enabled by default.
根据JDK-8170157,现在默认启用无限制加密策略。
Specific versions from the JIRA issue:
来自 JIRA 问题的特定版本:
- Java 9 (10, 11, etc..): Any official release!
- Java 8u161 or later (Available now)
- Java 7u171 or later (Only available through 'My Oracle Support')
- Java 6u181 or later (Only available through 'My Oracle Support')
- Java 9(10、11 等):任何正式版本!
- Java 8u161 或更高版本(现在可用)
- Java 7u171 或更高版本(仅可通过“My Oracle Support”获得)
- Java 6u181 或更高版本(仅可通过“My Oracle Support”获得)
Note that if for some odd reason the old behavior is needed in Java 9, it can be set using:
请注意,如果由于某种奇怪的原因在 Java 9 中需要旧行为,则可以使用以下方法进行设置:
Security.setProperty("crypto.policy", "limited");
回答by xoned
Here's a updated version of ntoskrnlanswer. It additionally contains a function to remove the final modifier like Arjanmentioned in the comments.
这是ntoskrnl答案的更新版本。它还包含一个删除最终修饰符的函数,如注释中提到的Arjan。
This version works with JRE 8u111 or newer.
此版本适用于 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"));
}