1

I'm trying to create an encrypted xlsx file using Apache POI. Here's my code, which runs just fine:

public static void Encrypt(String data) throws IOException, GeneralSecurityException, InvalidFormatException {

    Workbook wb = new XSSFWorkbook();
    Sheet sheet1 = wb.createSheet("sheet1");
    sheet1.createRow(0).createCell(0).setCellValue(data);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    wb.write(bos);
    bos.close();

    POIFSFileSystem fs = new POIFSFileSystem();
    EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes256, HashAlgorithm.sha512, 256, 16, ChainingMode.cbc);
    Encryptor enc = info.getEncryptor();
    enc.confirmPassword("pass");

    OPCPackage opc = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()));
    OutputStream os = enc.getDataStream(fs);
    opc.save(os);
    opc.close();

    FileOutputStream fos = new FileOutputStream("provawrite.xlsx");
    fs.writeFilesystem(fos);
    fos.close();
}

The issue is when I open the generated file, excel keeps complaining that the file is corrupted. I've also tried to change the EncryptionInfo instantiation with different Encryption modes but nothing changes.

Can someone give me a hint?!?

Daniele Cervi
  • 75
  • 3
  • 11
  • Try using `try` / `finally` or try-with-resources statements, and flushing. Don't know much about POI encryption but you might also want to experiment with not closing your streams before the file's actually written in the file system. – Mena Sep 20 '18 at 15:48

1 Answers1

8

The encrypted output stream must be closed before writing out the file system. In your case it lacks os.close(); after opc.save(os);.

But why the detour using ByteArrayOutputStreamat all?

Following works for me:

import java.io.*;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.poifs.crypt.*;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class XSSFEncryption {

 public static void doEncrypt(String data) throws Exception {

  POIFSFileSystem fs = new POIFSFileSystem();
  EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);

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

  Workbook workbook = new XSSFWorkbook();
  Sheet sheet = workbook.createSheet("sheet1");
  sheet.createRow(0).createCell(0).setCellValue(data);

  // write the workbook into the encrypted OutputStream
  OutputStream encos = enc.getDataStream(fs);
  workbook.write(encos);
  workbook.close();
  encos.close(); // this is necessary before writing out the FileSystem

  OutputStream os = new FileOutputStream("provawrite.xlsx");
  fs.writeFilesystem(os);
  os.close();
  fs.close();
 }

 public static void main(String[] args) throws Exception {
  doEncrypt("Test");
 }
}
Axel Richter
  • 56,077
  • 6
  • 60
  • 87
  • Ouch, that was an easy one. Probably I was really tired yesterday. Thank you sir! – Daniele Cervi Sep 21 '18 at 06:14
  • Interestingly, the code works fine without the call to encos.close() using Apache POI 3.17, but fails for 4.0.1 and 4.1.1. This means that some older code examples out there now fail with Apache POI 4. – Martin Dirichs Jan 22 '20 at 22:14
  • @Martin Dirichs: Well, that's a "Binsenweisheit" aka truism ;-). The way `apache poi` changes always leads to the need for changing former used code. There never was much backwards compatibility. – Axel Richter Jan 23 '20 at 04:42