- 摘要
- 1.POI读取加密Excel功能
- 2.解决办法
- 2.1安装JCE Unlimited
- 2.1.1 方案
- 2.1.2 结果
- 2.1.3 原因
- 2.2.Reflection
- 2.2.1 方案
- 2.2.2 结果
- 2.2.3 注意
- 参考文献
摘要
解决POI读取加密Excel文件时报的 EncryptedDocumentException: Export Restrictions in place错误,尝试了JCE和反射两种方法,猜想了一下JCE失败的原因。最后发现JCE是必须安装的,只是未必会生效,有条件。具体条件没有深究。
1.POI读取加密Excel功能
随便在网上搜一搜都会有这样功能的代码,贴在下面了。
public static XSSFWorkbook readExcel(String filePath, String password)
throws IOException, GeneralSecurityException {
File excelFile = new File(filePath);
InputStream is = new FileInputStream(excelFile);
XSSFWorkbook xssfWorkbook = null;
POIFSFileSystem poifsFileSystem = new POIFSFileSystem(is);
is.close();
EncryptionInfo encryptionInfo = new EncryptionInfo(poifsFileSystem);
Decryptor decryptor = Decryptor.getInstance(encryptionInfo);
boolean verifyPassword = decryptor.verifyPassword(password);
if (!verifyPassword) {
throw new SystemException("excel 密码错误!");
}
xssfWorkbook = new XSSFWorkbook(decryptor.getDataStream(poifsFileSystem));
/*
* try { xssfWorkbook=(XSSFWorkbook) WorkbookFactory.create(excelFile,password);
* } catch (EncryptedDocumentException e) {
* e.printStackTrace(); } catch (InvalidFormatException e) {
* Auto-generated catch block e.printStackTrace(); }
*/
return xssfWorkbook;
}
注释部分是POI自带的工具类,功能实现和方法里面一样。但是使用这段代码读取加密文件的时候同样会报错:
org.apache.poi.EncryptedDocumentException: Export Restrictions in place - please install JCE Unlimited Strength Jurisdiction Policy files
at org.apache.poi.poifs.crypt.CryptoFunctions.getCipher(CryptoFunctions.java:226)
at org.apache.poi.poifs.crypt.CryptoFunctions.getCipher(CryptoFunctions.java:200)
at org.apache.poi.poifs.crypt.agile.AgileDecryptor.hashInput(AgileDecryptor.java:269)
at org.apache.poi.poifs.crypt.agile.AgileDecryptor.verifyPassword(AgileDecryptor.java:114)
实际上是因为excel2013用的是256bit的SHA512去加密,但是java的Ciper功能会因为不同地区的要求受到不同的限制,恰巧有限制。所以只需要把限制放开就行了。
2.解决办法
2.1安装JCE Unlimited
2.1.1 方案
按照堆栈错误提示,很容易在网上找到解决步骤。首先下载安装的文件(oracle jdk8,oracle jdk 7),根据提示,放到使用的jdk home目录下的jre/lib/security目录下。这样再执行上面的方法就行了。
2.1.2 结果
很失望,我测试的结果是不行的。我有两个版本的jdk在电脑上,运行的是1.8,项目使用的是1.7。安装完成之后已经可以在项目中发现这个两个jar包了,但是仍然报同样的错误!
\img\jce_jar_in_project.png)
2.1.3 原因
打开local_policy.jar文件,可以看到其中有两个文件:default_local.policy和exempt_local.policy。我的两个文件内容如下:
default_local.policy
// Some countries have import limits on crypto strength. This policy file
// is worldwide importable.
grant {
permission javax.crypto.CryptoPermission "DES", 64;
permission javax.crypto.CryptoPermission "DESede", *;
permission javax.crypto.CryptoPermission "RC2", 128,
"javax.crypto.spec.RC2ParameterSpec", 128;
permission javax.crypto.CryptoPermission "RC4", 128;
permission javax.crypto.CryptoPermission "RC5", 128,
"javax.crypto.spec.RC5ParameterSpec", *, 12, *;
permission javax.crypto.CryptoPermission "RSA", *;
permission javax.crypto.CryptoPermission *, 128;
};
exempt_local.policy
// Some countries have import limits on crypto strength. So this file
// will be useful.
grant {
// There is no restriction to any algorithms if KeyRecovery is enforced.
permission javax.crypto.CryptoPermission *, "KeyRecovery";
// There is no restriction to any algorithms if KeyEscrow is enforced.
permission javax.crypto.CryptoPermission *, "KeyEscrow";
// There is no restriction to any algorithms if KeyWeakening is enforced.
permission javax.crypto.CryptoPermission *, "KeyWeakening";
};
另外还有一个文件:US_export_policy.jar,按注释的说明没有对加密有限制和要求了。内容如下:
US_export_policy.jar
// Manufacturing policy file.
grant {
// There is no restriction to any algorithms.
permission javax.crypto.CryptoAllPermission;
};
失败原因猜想:jdk是加载default_local.policy的策略,但是文件中并没有把256bite的方法加入permission。网上其他人说可以下载解决的,估计是default_local.policy有对应他们需要的加密算法。
2.2.Reflection
2.2.1 方案
从方案2.1中可以发现是因为jdk中的配置文件导致jdk对加密算法的秘钥长度有要求,如果能通过其他方法改掉这个限制就行了。实际上最后这个限制是用一个boolean字段标识,可以通过反射在解密前去除这个限制就行了。
在之前的工具类中增加下面的代码,在调用使用反射获取到标识字段,修改为false就行了。
static {
removeCryptographyRestrictions();
}
/**
* 去掉加密秘钥长度的限制
*/
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) {
log.error(e.getMessage(),e);
}
}
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);
}
/**
* 判断环境是否需要去除加密限制
* @return
*/
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"));
}
2.2.2 结果
运行解密方法,成功读取文件!
2.2.3 注意
即使是使用2.2方案的这段代码,依然需要安装JCE
参考文献
[1] how-to-avoid-installing-unlimited-strength-jce-policy-files-when-deploying-an
[2] java-opening-password-protected-xslx-file-throws-encrypteddocumentexception
[3] java-error-please-install-jce-unlimited-strength-jurisdiction-policy-files