2016-08-01 3 views
3

빈 ByteArray 스트림에 대해서도 MessageDigest SHA-256 구현으로 POI XLSX 형식에 대한 결정적인 해시 값을 얻는 데 문제가있는 것 같습니다. 이것은 수백 또는 수천 번의 반복 작업 후에 임의로 발생합니다. 문제를 재현하는 데MessageDigest와 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 형식은 프로에 영향을받지 않는 것으로 보입니다. blem 설명. POI 자체의 버그가 아니라면 원인을 알 수있는 단서가 있습니까? 기본적으로 위 코드는 다음을 말합니다. https://poi.apache.org/spreadsheet/examples.htmlBusinessPlan example

감사합니다.

답변

2

이것은 확실한 대답은 아니지만이 무슨 내 의심입니다 :

DOCX 및 XLSX 파일 형식은 기본적으로 압축 업의 XML 파일의 무리입니다. zip 파일로 이름을 바꾸고 좋아하는 zip 도구로 열면 쉽게 볼 수 있습니다.

단어로 생성 된 파일을 검사 할 때 아카이브에 포함 된 모든 파일의 변경 타임 스탬프는 항상 1980-01-01 00:00:00이며 POI로 만든 파일의 경우 파일이 실제로 생성 된 시간 스탬프가 표시됩니다.

그래서 하나 이상의 파일 사이에 타임 스탬프 차이가있을 때 excelAdHoc1excelAdHoc2에 문제가 있다고 의심됩니다. 이것은 하나 또는 다른 파일을 생성하는 동안 시계가 다음 초로 전환 할 때 발생할 수 있습니다.

HSSF 형식이 "압축 된 XML"형식이 아니므로 다른 타임 스탬프를 포함 할 수있는 중첩 파일이 없으므로 XLS 파일에 영향을 미치지 않습니다.

파일을 작성한 후에 타임 스탬프를 변경하려면 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

확인을, 나는 SO에서 여기 몇 가지 예에 따라, 속성 모든 XSLX 파일 항목 파일 시간을 초기화 할 수있는 방법을 발견했습니다. 불행히도 파일 엔트리 만 ZipFile이나 OPCPackage 같은 방법으로 액세스 할 수 있습니다. 나는 또한 시간 속성이 다른 아카이브 내부의 폴더에 액세스하여 재설정하는 솔루션을 찾을 수 없었습니다.

지금까지 POI에서 생성 된 XLSX- 아카이브의 다른 속성을 제거하지 못했지만 동일한 속성의 다른 두 파일에서 동일한 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); 
     } 
    } 
} 

편집 : (I 또는 다른 사람이 찾을 때까지 ZIP 아카이브 내부에 폴더 메타 데이터를 조작하기위한 솔루션) 한편

, 내가 여기에 깊은 비교 솔루션으로 전환했습니다 : Comparing XLSX files