Java 如何避免安装";“无限力量”;部署应用程序时的JCE策略文件?
我有一个应用程序,使用256位AES加密,这是Java开箱即用不支持的。我知道为了让它正常工作,我在security文件夹中安装了JCE unlimited strength jars。作为开发人员,这对我来说很好,我可以安装它们 我的问题是,由于此应用程序将被分发,最终用户很可能不会安装这些策略文件。让最终用户下载这些只是为了让应用程序发挥功能,这不是一个有吸引力的解决方案Java 如何避免安装";“无限力量”;部署应用程序时的JCE策略文件?,java,aes,jce,policyfiles,Java,Aes,Jce,Policyfiles,我有一个应用程序,使用256位AES加密,这是Java开箱即用不支持的。我知道为了让它正常工作,我在security文件夹中安装了JCE unlimited strength jars。作为开发人员,这对我来说很好,我可以安装它们 我的问题是,由于此应用程序将被分发,最终用户很可能不会安装这些策略文件。让最终用户下载这些只是为了让应用程序发挥功能,这不是一个有吸引力的解决方案 有没有一种方法可以让我的应用程序在不覆盖最终用户计算机上的文件的情况下运行?可以在不安装策略文件的情况下处理它的第三方软
有没有一种方法可以让我的应用程序在不覆盖最终用户计算机上的文件的情况下运行?可以在不安装策略文件的情况下处理它的第三方软件?还是从JAR中引用这些策略文件的方法 在安装程序的过程中,只需提示用户下载DOS批处理脚本或Bash shell脚本,并将JCE复制到正确的系统位置即可
我以前必须为服务器Web服务这样做,而不是正式的安装程序,我只是在用户运行应用程序之前提供脚本来安装它。在应用程序运行安装脚本之前,您可以使其无法运行。您还可以让应用程序抱怨JCE丢失,然后要求下载并重新启动应用程序?对于我们的应用程序,我们有一个客户机-服务器体系结构,只允许在服务器级别解密/加密数据。因此,那里只需要JCE文件 我们还遇到了另一个问题,我们需要通过JNLP更新客户端机器上的安全jar,它会在第一次运行时覆盖
${java.home}/lib/security/
和JVM中的库
这使它工作起来。对于另一种加密库,请查看。它有AES和许多附加功能。这是一个开放源码的图书馆。不过,您必须使用轻量级、专有的Bouncy Castle API才能工作。据我所知,Bouncy Castle仍然需要安装JAR 我做了一个小测试,它似乎证实了这一点:
对于这个问题,有几种常用的解决方案。不幸的是,这两项都不完全令人满意:
- 安装。虽然这可能是适合您的开发工作站的解决方案,但让非技术用户在每台计算机上安装文件很快就会成为一个大麻烦(如果不是障碍的话)。无法将文件与您的程序一起分发;它们必须安装在JRE目录中(由于权限的原因,该目录甚至可能是只读的)
- 跳过JCE API并使用另一个加密库,如。这种方法需要额外的1MB库,这可能是一个很大的负担,具体取决于应用程序。复制标准库中包含的功能也让人觉得很愚蠢。显然,API也与通常的JCE接口完全不同。(BC确实实现了JCE提供程序,但这没有帮助,因为密钥强度限制是在移交给实现之前应用的。)此解决方案也不允许您使用256位TLS(SSL)密码套件,因为标准TLS库在内部调用JCE来确定任何限制
private static void removeCryptographyRestrictions(){
如果(!isRestrictedCryptography()){
logger.fine(“不需要删除加密限制”);
返回;
}
试一试{
/*
*执行以下操作,但使用反射绕过访问检查:
*
*JceSecurity.isRestricted=false;
*JceSecurity.defaultPolicy.perms.clear();
*JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
*/
最终类jceSecurity=Class.forName(“javax.crypto.jceSecurity”);
最终类cryptoPermissions=Class.forName(“javax.crypto.cryptoPermissions”);
最终类cryptoAllPermission=Class.forName(“javax.crypto.cryptoAllPermission”);
最终字段isRestrictedField=jceSecurity.getDeclaredField(“isRestricted”);
isRestrictedField.setAccessible(true);
最终字段修饰符字段=Field.class.getDeclaredField(“修饰符”);
modifiersField.setAccessible(true);
setInt(isRestrictedField,isRestrictedField.getModifiers()&~Modifier.FINAL);
isRestrictedField.set(null,false);
最终字段defaultPolicyField=jceSecurity.getDeclaredField(“defaultPolicy”);
defaultPolicyField.setAccessible(true);
最终PermissionCollection defaultPolicy=(PermissionCollection)defaultPolicyField.get(null);
final Field perms=cryptoPermissions.getDeclaredField(“perms”);
perms.setAccessible(true);
((Map)perms.get(defaultPolicy)).clear();
最终字段实例=cryptoAllPermission.getDeclaredField(“实例”);
setAccessible(true);
add((权限)instance.get(null));
logger.fine(“成功删除加密限制”);
}捕获(最终异常e){
logger.log(Level.WARNING,“未能删除加密限制”,e);
}
}
私有静态布尔isRestrictedCryptography(){
//这与Oracle Java 7和8相匹配,但与Java 9或OpenJDK不匹配。
最终字符串名称=System.getProperty(“java.runtime.name”);
最终字符串ver=System.getProperty(“java.version”);
返回name!=null&&name.equals(“Java(TM)SE运行时环境”)
&&ver!=null&(ver.startsWith(“1.7”)| | ver.startsWith(“1.8”);
}
在执行任何加密操作之前,只需从静态初始值设定项等调用removeCryptographyRestrictions()
JceSecurity.isRestricted=false部分是我们所需要的全部
//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) {
}
}
javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)
Security.setProperty("crypto.policy", "limited");
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"));
}
static {
UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}
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
}
}