2016-08-01 45 views
3

似乎与获得确定性的哈希值的POI XLSX格式,消息摘要SHA-256实现的问题,即使是空的ByteArray流。这种情况在数百次甚至数千次迭代之后随机发生。与POI消息摘要XSSF/XLSX散列非决定论SHA-256

用于重现该问题的相关代码片段:

// TestNG FileTest: 
@Test(enabled = true) // indeterminism at random iterations, such as 400 or 1290 
public void emptyXLSXTest() throws IOException, NoSuchAlgorithmException { 
    final Hasher hasher = new HasherImpl(); 
    boolean differentSHA256Hash = false; 
    for (int i = 0; i < 10000; i++) { 
     final ByteArrayOutputStream excelAdHoc1 = BusinessPlanInMemory.getEmptyExcel("xlsx"); 
     final ByteArrayOutputStream excelAdHoc2 = BusinessPlanInMemory.getEmptyExcel("xlsx"); 

     byte[] expectedByteArray = excelAdHoc1.toByteArray(); 
String expectedSha256 = hasher.sha256(expectedByteArray); 
byte[] actualByteArray = excelAdHoc2.toByteArray(); 
String actualSha256 = hasher.sha256(actualByteArray); 

if (!expectedSha256.equals(actualSha256)) { 
      differentSHA256Hash = true; 
      System.out.println("ITERATION: " + i); 
      System.out.println("EXPECTED HASH: " + expectedSha256); 
      System.out.println("ACTUAL HASH: " + actualSha256); 
      break; 
     } 
    } 
    Assert.assertTrue(differentSHA256Hash, "Indeterminism did not occur"); 
} 

引用的散列器和POI代码:

// HasherImpl class: 
public String sha256(final InputStream stream) throws IOException, NoSuchAlgorithmException { 
    final MessageDigest digest = MessageDigest.getInstance("SHA-256"); 
    final byte[] bytesBuffer = new byte[300000]; 
    int bytesRead = -1; 
    while ((bytesRead = stream.read(bytesBuffer)) != -1) { 
     digest.update(bytesBuffer, 0, bytesRead); 
    } 
    final byte[] hashedBytes = digest.digest(); 
    return bytesToHex(hashedBytes); 
} 

想消灭非决定论,由于像创建时间元数据,都无济于事:

// POI BusinessPlanInMemory helper class: 
public static ByteArrayOutputStream getEmptyExcel(final String fileextension) throws IOException { 
    Workbook wb; 

    if (fileextension.equals("xls")) { 
     wb = new HSSFWorkbook(); 
    } 
    else { 
     wb = new XSSFWorkbook(); 
     final POIXMLProperties props = ((XSSFWorkbook) wb).getProperties(); 
     final POIXMLProperties.CoreProperties coreProp = props.getCoreProperties(); 
     coreProp.setCreated(""); 
     coreProp.setIdentifier("1"); 
     coreProp.setModified(""); 
    } 

    wb.createSheet(); 

    final ByteArrayOutputStream excelStream = new ByteArrayOutputStream(); 
    wb.write(excelStream); 
    wb.close(); 
    return excelStream; 
} 

HSSF/XLS格式似乎不受Pro瑕疵描述。 没有任何人有一个线索,这可能是造成这一点,如果不是一个错误的POI本身?基本上,上面的代码是指 https://poi.apache.org/spreadsheet/examples.htmlBusinessPlan example

感谢您的输入!

回答

2

这不是一个明确的答案,但是这是我的怀疑会发生什么:

的docx和XLSX格式的文件基本上是一帮拉链式XML的文件。当将它们重命名为.zip并用您最喜爱的zip工具打开时,可以很容易地看到这一点。

当检查由单词创建的文件时,我注意到存档中包含的所有文件的更改时间戳始终为1980-01-01 00:00:00,而在使用POI创建的文件中,它将显示创建文件的实际时间戳。

所以,我怀疑当excelAdHoc1excelAdHoc2中的一个或多个文件之间存在时间戳差异时会出现问题。当创建一个或另一个文件时,时钟切换到下一秒时可能会发生这种情况。

这不会影响XLS-文件,因为该HSSF格式是不是“压缩XML”型的,因此不包含可能具有不同的时间戳任何嵌套文件。

为了写你可以尝试使用`java.util.zip``包文件后更改的时间戳。我没有测试它,但这应该做的伎俩:

ZipFile file = new ZipFile(pathToFile); 
Enumeration<ZipEntry> e = file.entries(); 
while(e.hasMoreElements()) { 
    ZipEntry entry = e.nextElement(); 
    entry.setTime(0L); 
} 
+0

感谢您对此的看法。我将不得不测试和写入实际的文件来重新检查它。但是不应该设置CoreProperty元数据(创建和修改时间,如上所述)来防止这种情况发生?或者它只影响内部元数据,而不影响档案的内容? – fozzybear

+0

我认为最好的方法来检查这将是你说的:写文件,并检查邮编内容。在我现有的文件中,我没有修改CoreProterties,所以我不能确定这是什么原因导致我的情况有所不同。 –

+0

看起来你已经把我放在了正确的轨道上,piet!我已经提取了生成的预期和实际内容,并且所有内容都是相似的,文件,文件夹,CRC,但修改时间相差2秒。鉴于事实,我明确告诉POI清除修改时间,这很奇怪。除非这会影响其他内部修改时间。现在我只需要弄清楚,在创建之前或之后如何处理XLSX内部文件的修改时间。否则,我看不到其他方式,但要解压缩,触摸并重新压缩文件。你怎么看? – fozzybear

1

好吧,我已经找到一种方法,可以将所有XSLX文件项文件时间属性,根据发现这里的SO一些例子。不幸的是,只有文件条目似乎可以通过ZipFile或OPCPackage等方法访问。我无法找到一个解决方案,还可以访问并重置存档,其中也有不同的时间属性里面的文件夹。

到目前为止,我没有成功消除POI生成的XLSX-archives的不同属性,以便从两个其他方面相同的文件中获得相同的SHA256哈希值,对此,不同的属性似乎是原因。

private void resetOPCPTimeAttributes(File file) 
     throws InvalidFormatException, IOException, OpenXML4JException, XmlException { 

    OPCPackage opcp = ZipPackage.open(file); 
    resetZipfileContentTimeAttributes(opcp.getParts()); 

    opcp.flush(); 
    opcp.close(); 
} 

private void resetZipfileContentTimeAttributes(List<PackagePart> parts) throws InvalidFormatException { 

    ArrayList<PackagePart> subParts = null; 
    for (PackagePart part: parts) { 

     PackageProperties props = part.getPackage().getPackageProperties(); 
     props.setLastModifiedByProperty(""); 
     props.setCreatedProperty(""); 
     props.setModifiedProperty(""); 

     subParts = part.getPackage().getParts(); 

     while (subParts != null) { 
      resetZipfileContentTimeAttributes(subParts); 
     } 
    } 
} 

编辑:

在此期间(直到我或其他人发现操纵zip存档文件夹里面的元数据的解决方案),我已经切换到比较深的解决方案在这里:Comparing XLSX files