我有一个使用256位AES加密的应用程序,Java开箱即用不支持。我知道要使其正常运行,我在security文件夹中安装了JCE不限强度的jars。作为开发人员,这对我来说很好,我可以安装它们。
我的问题是,由于此应用将被分发,最终用户很可能不会安装这些策略文件。让最终用户下载这些文件只是为了使应用程序功能并不是一个有吸引力的解决方案。
有没有一种方法可以使我的应用程序运行而不会覆盖最终用户计算机上的文件?可以在不安装策略文件的情况下处理它的第三方软件?还是仅从JAR内引用这些策略文件的方法?
我有一个使用256位AES加密的应用程序,Java开箱即用不支持。我知道要使其正常运行,我在security文件夹中安装了JCE不限强度的jars。作为开发人员,这对我来说很好,我可以安装它们。
我的问题是,由于此应用将被分发,最终用户很可能不会安装这些策略文件。让最终用户下载这些文件只是为了使应用程序功能并不是一个有吸引力的解决方案。
有没有一种方法可以使我的应用程序运行而不会覆盖最终用户计算机上的文件?可以在不安装策略文件的情况下处理它的第三方软件?还是仅从JAR内引用这些策略文件的方法?
Answers:
有两个通常引用的解决方案。不幸的是,这些都不是完全令人满意的:
但是,有反思。使用反射您无法做任何事情吗?
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。
现在,Java 9或任何最新版本的Java 6、7或8都不再需要此功能。:)
根据JDK-8170157,现在默认启用了无限加密策略。
JIRA问题的特定版本:
请注意,如果出于某些奇怪的原因,Java 9需要旧的行为,则可以使用以下命令进行设置:
Security.setProperty("crypto.policy", "limited");
这是解决方案: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) {
}
}
java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
isRestricted领域已经成为最终领域(bugs.openjdk.java.net/browse/JDK-8149417)。@ntoskrnl的答案会考虑是否包含“最终”修饰符。@ M.Dudley对Java许可协议的评论仍然适用。
据我所知,弹跳城堡仍然需要安装广口瓶。
我做了一点测试,似乎可以确认这一点:
http://www.bouncycastle.org/wiki/display/JA1/Frequently+Asked+Questions
从JDK 8u102开始,依赖反射的已发布解决方案将不再起作用:这些解决方案设置的字段现在为final(https://bugs.openjdk.java.net/browse/JDK-8149417)。
看起来它要么回到(a)使用Bouncy Castle,要么(b)安装JCE策略文件。
isRestricted领域,因为它考虑了可能添加的“最终”修饰符。
对于其他加密库,请查看Bouncy Castle。它具有AES和许多附加功能。这是一个开放的开源库。但是,您必须使用轻巧的专有Bouncy Castle API才能正常工作。
对于我们的应用程序,我们具有客户端服务器体系结构,并且仅允许在服务器级别解密/加密数据。因此,仅在此处需要JCE文件。
我们还有另一个问题,我们需要通过JNLP更新客户端计算机上的安全性jar,它会覆盖${java.home}/lib/security/第一次运行时和JVM。
这使它起作用。
这是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();产生编译器错误。注释掉似乎并不影响其功能。这行是必要的吗?
这是@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
}
}
在安装程序的过程中,只需提示用户并下载DOS Batch脚本或Bash shell脚本,然后将JCE复制到正确的系统位置即可。
我以前必须对服务器Web服务执行此操作,而不是使用正式的安装程序,我只是提供了脚本来设置应用程序,然后用户才能运行它。您可以使应用程序无法运行,直到他们运行安装脚本。您还可以使应用程序抱怨缺少JCE,然后要求下载并重新启动应用程序?