【问题标题】:Unable to decrypt excel file with apache poi in Android无法在 Android 中使用 apache poi 解密 excel 文件
【发布时间】:2020-06-09 09:30:03
【问题描述】:

希望大家在疫情期间身体健康,

我可以成功加密 Excel 文件,但无法解密。 我需要帮助在 Android 中解密 excel 文件。我正在使用 apache poi 库。 我不知道我缺哪里了。

密码就是密码

使用 encryptXLSX 函数加密的文件是:https://drive.google.com/file/d/1eQV62uFWj5Tg6g7gSmP61mNk_Ta5dMHq/view?usp=sharing

加密代码为:

public void encryptXLSX()
        throws IOException, GeneralSecurityException, InvalidFormatException {
 // input is unprotected excel named as excel.xlsx
 String input = "/storage/emulated/0/Android/data/com.protect.excel_protect_example/files/excel.xlsx";

/// output file path

String outputPath = "/storage/emulated/0/Android/data/com.protect.excel_protect_example/files/protected_excel.xlsx";

    POIFSFileSystem fs = new POIFSFileSystem();
    Biff8EncryptionKey.setCurrentUserPassword("password");
    EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);

    Encryptor enc = info.getEncryptor();
    enc.confirmPassword("password");

    InputStream fis = new FileInputStream(input);
    try (OPCPackage opc = OPCPackage.open(fis); OutputStream os = enc.getDataStream(fs)) {
        opc.save(os);
        os.close();
    }

    FileOutputStream fos1 = new FileOutputStream(outputPath);
    fs.writeFilesystem(fos1);
    fos1.close();
    fs.close();
    fis.close();
}

解密代码为:

public boolean isEncrypted(String path) {
    try {
        try {
            new POIFSFileSystem(new FileInputStream(path));
        } catch (IOException ignored) {
        }
        System.out.println("protected");
        return true;
    } catch (OfficeXmlFileException e) {
        System.out.println("not protected");
        return false;
    }
}

public byte[] decryptXLSX() throws Exception {

    String sourcepath = "/storage/emulated/0/Android/data/com.protect.excel_protect_example/files/protected_excel.xlsx";

    InputStream in = null;
    FileInputStream fis = new FileInputStream(sourcepath);
    if (isEncrypted(sourcepath)) {
        org.apache.poi.hssf.record.crypto.Biff8EncryptionKey.setCurrentUserPassword(password);
        POIFSFileSystem filesystem = new POIFSFileSystem(fis);
        print("Header Block:" + filesystem.getHeaderBlock().toString());
        print("property tables:" + filesystem.getRoot().getEntries().toString());
        EncryptionInfo info = new EncryptionInfo(filesystem);

        //EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);

        //EncryptionInfo info = new EncryptionInfo(filesystem.getRoot().createDocumentInputStream("EncryptionInfo"), EncryptionMode.agile);

        //EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes256, HashAlgorithm.sha512, 256, 16, ChainingMode.cbc);

        Decryptor d = Decryptor.getInstance(info);

        if (!d.verifyPassword("password")) {
            print("Wrong password");
        } else {
            print("Good!");
        }

        in = d.getDataStream(filesystem);
    } else {
        in = new FileInputStream(sourcepath);
    }
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    int nRead;
    byte[] data = new byte[1024];
    while ((nRead = in.read(data, 0, data.length)) != -1) {
        buffer.write(data, 0, nRead);
    }
    buffer.flush();
    byte[] byteArray = buffer.toByteArray();
    FileOutputStream fos1 = new FileOutputStream(
            "/storage/emulated/0/Android/data/com.protect.excel_protect_example/files/Unprotected_excel.xlsx");
    fos1.write(byteArray);
    fos1.close();
    return byteArray;
}

错误:

    org.apache.poi.EncryptedDocumentException: Unable to parse 
          encryption descriptor
      [        ] W/System.err(28825):   at 

 org.apache.poi.poifs.crypt.agile.AgileEncryptionInfoBuilder.parseDescriptor(AgileEncryptionInfoBuilder.java:106)
[        ] W/System.err(28825):     at org.apache.poi.poifs.crypt.agile.AgileEncryptionInfoBuilder.initialize(AgileEncryptionInfoBuilder.java:40)
[        ] W/System.err(28825):     at org.apache.poi.poifs.crypt.EncryptionInfo.<init>(EncryptionInfo.java:152)
[        ] W/System.err(28825):     at org.apache.poi.poifs.crypt.EncryptionInfo.<init>(EncryptionInfo.java:101)
[   +3 ms] W/System.err(28825):     at org.apache.poi.poifs.crypt.EncryptionInfo.<init>(EncryptionInfo.java:94)
[        ] W/System.err(28825):     at com.protect.excel_protect.decryptXLSX(ExcelProtect.java:182)

ExcelProtect.java Line 182中解密函数的问题:

        EncryptionInfo info = new EncryptionInfo(filesystem);

【问题讨论】:

  • 不管是excel文件还是其他类型的文件。此外,我们看不到 -excel- 文件开头。
  • 你没有展示你如何调用decryptXLSX(),也没有展示为什么它应该有一个byte[]参数,也没有展示它的内容。
  • 我已经用受加密功能保护且无法使用解密功能解密的代码和文件更新了问题。
  • 我能够在桌面目录上的核心 java 文件上运行和解密文件,即在 Android 项目之外,但在 Android 项目中我遇到了上述问题。我一直在尝试从昨天晚上更改 EncryptionInfo 但无法弄清楚,帮助将节省我的一天兄弟
  • InputStream fis 未被 fs 使用,也未被写入 fos1。因此,据我所知,您的输出文件与输入文件无关。

标签: java android excel encryption apache-poi


【解决方案1】:

在我的带有 Android 10.0(API-Level 29,Rev.4)的 IntelliJ IDEA 中,您的 decryptXLSX 方法按预期工作,因此看起来您的 Android 版本较低并且不支持内部方法或加密算法。也许您可以检查底层 Java 版本并将其呈现给我们。你可以这样做:

System.out.println("\nJava version:");
String[] javaVersionElements = System.getProperty("java.runtime.version").split("\\.|_|-b");
String discard, major, minor, update, build;
discard = javaVersionElements[0];
major   = javaVersionElements[1];
minor   = javaVersionElements[2];
update  = javaVersionElements[3];
build   = javaVersionElements[4];
System.out.println("discard: " + discard + " major: " + major + " minor: " + minor + " update: " + update + " build: " + build);

(Runtime.version 没有与我的 Android 版本一起运行)。

我的输出是:

Java version:
discard: 11 major: 0 minor: 5+10 update: 520 build: 17

我没有检查 XLSX 加密是否需要无限加密,但以防万一您可以使用一些代码行进行检查:

/**
 * Determines if cryptography restrictions apply.
 * Restrictions apply if the value of {@link Cipher#getMaxAllowedKeyLength(String)} returns a value smaller than {@link Integer#MAX_VALUE} if there are any restrictions according to the JavaDoc of the method.
 * This method is used with the transform <code>"AES/CBC/PKCS5Padding"</code> as this is an often used algorithm that is <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl">an implementation requirement for Java SE</a>.
 *
 * @return <code>true</code> if restrictions apply, <code>false</code> otherwise
 *
 * code by Maarten Bodewes, https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available#
 */
public static boolean restrictedCryptography() {
    try {
        return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding") < Integer.MAX_VALUE;
    } catch (final NoSuchAlgorithmException e) {
        throw new IllegalStateException("The transform \"AES/CBC/PKCS5Padding\" is not available (the availability of this algorithm is mandatory for Java SE implementations)", e);
    }
}

只需调用方法:

System.out.println("Java restricted cryptography: " + restrictedCryptography());

这是我的输出(“false”表示无限加密):

Java restricted cryptography: false

【讨论】:

  • 好的,我现在检查一下,告诉你它是否有效。
  • @groupwork:如果你可以调试你的程序,请检查 AgileEncryptionInfoBuilder::parseDescriptor 的异常 - 我感觉 XmlBeans 无法处理输入,因为你的类路径中缺少或重复的 jar ...即您的堆栈跟踪不包含嵌套异常
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多