博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
POI读取加密Excel
阅读量:4080 次
发布时间:2019-05-25

本文共 6530 字,大约阅读时间需要 21 分钟。

摘要

解决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 方案

按照堆栈错误提示,很容易在网上找到解决步骤。首先下载安装的文件(,),根据提示,放到使用的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


参考文献

你可能感兴趣的文章
大数据学习:Spark RDD操作入门
查看>>
大数据框架:Spark 生态实时流计算
查看>>
大数据入门:Hive和Hbase区别对比
查看>>
大数据入门:ZooKeeper工作原理
查看>>
大数据入门:Zookeeper结构体系
查看>>
大数据入门:Spark RDD基础概念
查看>>
大数据入门:SparkCore开发调优原则
查看>>
大数据入门:Java和Scala编程对比
查看>>
大数据入门:Scala函数式编程
查看>>
【数据结构周周练】002顺序表与链表
查看>>
C++报错:C4700:使用了非初始化的局部变量
查看>>
【数据结构周周练】003顺序栈与链栈
查看>>
C++类、结构体、函数、变量等命名规则详解
查看>>
C++ goto语句详解
查看>>
【数据结构周周练】008 二叉树的链式创建及测试
查看>>
《软件体系结构》 第九章 软件体系结构评估
查看>>
《软件体系结构》 第十章 软件产品线体系结构
查看>>
《软件过程管理》 第六章 软件过程的项目管理
查看>>
《软件过程管理》 第九章 软件过程的评估和改进
查看>>
分治法 动态规划法 贪心法 回溯法 小结
查看>>