2016-08-01 46 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