Skip to content

Conversation

@modeverv
Copy link

@modeverv modeverv commented Oct 4, 2025

Add support for encrypting XLSX files with a password

I've implemented this feature by studying Apache POI’s code base.
With this PR, NPOI can now create password-protected .xlsx files.

Basic usage

XSSFWorkbook wb = new();
ISheet sheet = wb.CreateSheet("Sheet1");
sheet.CreateRow(0).CreateCell(0).SetCellValue("Hello");
// do something to xlsx...

using PasswordXlsxOutputStream outputStream = new(outputPath, password);
wb.Write(outputStream);

Tests

Includes byte-level comparison with a file encrypted by Apache POI.
See: TestCases.POIFS.FileSystem.TestPasswordXlsxOutputStream.TestEncryptor

Notes

I’m relatively new to C#, POI and NPOI.
Please review the code and give feedback on design, naming, and integration with the existing NPOI style.

ref: #1641

Thank you!

@modeverv modeverv marked this pull request as draft October 4, 2025 07:01
@modeverv modeverv changed the title [wip] xlsx encrypt Add support for encrypting XLSX files with a password Oct 4, 2025
@modeverv modeverv marked this pull request as ready for review October 4, 2025 08:03
@tonyqus
Copy link
Member

tonyqus commented Oct 7, 2025

Reference: https://poi.apache.org/encryption.html

XML-based formats - Encryption

try (POIFSFileSystem fs = new POIFSFileSystem()) {
    EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
    // EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null);
    Encryptor enc = info.getEncryptor();
    enc.confirmPassword("foobaa");
    // Read in an existing OOXML file and write to encrypted output stream
    // don't forget to close the output stream otherwise the padding bytes aren't added
    try (OPCPackage opc = OPCPackage.open(new File("..."), PackageAccess.READ_WRITE);
        OutputStream os = enc.getDataStream(fs)) {
        opc.save(os);
    }
    // Write out the encrypted version
    try (FileOutputStream fos = new FileOutputStream("...")) {
        fs.writeFilesystem(fos);
    }
}

https://github.com/nissl-lab/npoi/blob/master/ooxml/POIFS/Crypt/Agile/AgileEncryptionInfoBuilder.cs

@tonyqus
Copy link
Member

tonyqus commented Oct 7, 2025

Although the code is working (as you mentioned), it's not the expected coding style of NPOI.

using PasswordXlsxOutputStream outputStream = new(outputPath, password);
wb.Write(outputStream);

One way is to add IWorkbook.Write(stream, password) as a new interface for OOXML Excel files

@modeverv
Copy link
Author

modeverv commented Oct 7, 2025

thanks for review. I check your comment tommorow.
I now checking CI error.

Errors & Warnings
  [ERR] Test: Target Test has thrown an exception
​

I've write a "can not pass test" code?

@tonyqus
Copy link
Member

tonyqus commented Oct 9, 2025

windows-latest test always fails. Please ignore the error.

@modeverv
Copy link
Author

@tonyqus

Thank you for your review! I've updated the implementation to follow the NPOI coding style as you suggested.

Changes

  • Added IWorkbook.Write(stream, password) method for easier usage
  • Kept the POI-compatible approach using EncryptionInfo and Encryptor

Usage Examples

Easy Way (New NPOI-style API)

XSSFWorkbook wb = new();
ISheet sheet = wb.CreateSheet("Sheet1");
sheet.CreateRow(0).CreateCell(0).SetCellValue("Hello");

using(FileStream fs = File.Create(outputPath))
{
    wb.Write(fs, password);
}

POI-Compatible Way

csharpXSSFWorkbook wb = new();
ISheet sheet = wb.CreateSheet("Sheet1");
sheet.CreateRow(0).CreateCell(0).SetCellValue("Hello");

using(POIFSFileSystem fs = new())
{
    EncryptionInfo info = new(EncryptionMode.AgileXlsx);
    Encryptor enc = info.Encryptor;
    enc.ConfirmPassword(password);
    
    using(OutputStream os = enc.GetDataStream(fs))
    {
        wb.Write(os);
    }
    
    using(FileStream fos = File.Create(outputPath))
    {
        fs.WriteFileSystem(fos);
    }
}

Please let me know if there are any other changes needed. Thank you!

@modeverv
Copy link
Author

modeverv commented Nov 2, 2025

add encrypt options

  • AES-256 + SHA-256
  • AES-192 + SHA-384
  • AES-256 + SHA-512
  • AES-128 + MD5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants