excel_decryptor

pub package Dart CI License: MIT Dart Platforms

A lightweight, 100% pure-Dart package to detect and decrypt password-protected Microsoft Excel (.xlsx) and Office Open XML (OOXML) files in memory.

Designed for Flutter mobile/web/desktop apps and server-side Dart backends that process password-protected files (such as e-statements from SBI, HDFC, ICICI, payroll, and financial reports).


Highlights

  • 100% Pure Dart: Zero native C++/MethodChannel dependencies. Compiles seamlessly to iOS, Android, Web, Windows, macOS, Linux, and backend runtimes (Shelf, Dart Frog).
  • Modern Encryption Support: Decrypts both Agile Encryption (AES-128/AES-256 with SHA-1, SHA-256, SHA-384, SHA-512) and Standard Encryption (AES-128-ECB).
  • Dynamic CFB Support: Full Compound File Binary (OLE2) parser supporting both standard 512-byte sectors (CFB v3) and 4096-byte sectors (CFB v4).
  • Fast Password Validation: Verifies password hashes before decrypting the complete file. Rejects wrong passwords in ~20-50 ms.
  • Zero Lock-in: Decrypts encrypted documents directly into unencrypted OOXML ZIP bytes that can be read by any standard parser (e.g. archive, excel, spreadsheet_decoder).
  • No Dependency Conflicts: Does not lock older versions of archive or Flutter SDKs.

Feature Matrix

Encryption Specification Cipher / Key Size Hash Function Used In / Source Status
Agile Encryption ([MS-OFFCRYPTO] 2.3.4.10) AES-CBC (128-bit) SHA-1 Core banking (Finacle, SBI statements) Supported
Agile Encryption ([MS-OFFCRYPTO] 2.3.4.10) AES-CBC (256-bit) SHA-512 / SHA-256 Microsoft Office 2013, 2016, 2019, 365 Supported
Standard Encryption ([MS-OFFCRYPTO] 2.3.4.5) AES-ECB (128-bit) SHA-1 Microsoft Office 2007, 2010, Apache POI Supported
CFB v3 & v4 Container ([MS-CFB]) Dynamic 512B / 4KB sectors N/A Large files (>2GB) and standard files Supported

Getting Started

Installation

Add excel_decryptor to your pubspec.yaml:

dependencies:
  excel_decryptor: ^1.0.0

Or via terminal:

dart pub add excel_decryptor
# or for Flutter:
flutter pub add excel_decryptor

Usage

1. Quick Check if a File is Protected

Check whether raw file bytes represent an encrypted Excel/OOXML workbook:

import 'dart:io';
import 'dart:typed_data';
import 'package:excel_decryptor/excel_decryptor.dart';

void main() async {
  final Uint8List fileBytes = await File('statement.xlsx').readAsBytes();

  if (ExcelDecryptor.isProtected(fileBytes)) {
    print('Password required!');
  } else {
    print('File is unencrypted, can be opened directly.');
  }
}

2. Simple Decryption

If the file is encrypted, decrypt it with the user's password. If the file is already unencrypted, ExcelDecryptor.decrypt returns the original bytes untouched.

import 'dart:io';
import 'dart:typed_data';
import 'package:excel_decryptor/excel_decryptor.dart';

void main() async {
  final Uint8List encryptedBytes = await File('statement.xlsx').readAsBytes();

  final Uint8List? decryptedBytes = ExcelDecryptor.decrypt(
    encryptedBytes,
    password: 'Password123',
  );

  if (decryptedBytes != null) {
    // Save or parse directly with your favorite Excel reader:
    await File('unlocked.xlsx').writeAsBytes(decryptedBytes);
    print('Decrypted successfully!');
  } else {
    print('Failed to decrypt. Password might be incorrect or file corrupted.');
  }
}

3. Detailed Decryption Status

Use ExcelDecryptor.decryptWithResult to receive detailed feedback on why decryption failed:

import 'dart:typed_data';
import 'package:excel_decryptor/excel_decryptor.dart';

void handleExcel(Uint8List fileBytes, String password) {
  final DecryptResult result = ExcelDecryptor.decryptWithResult(
    fileBytes,
    password: password,
  );

  switch (result.status) {
    case DecryptStatus.success:
      final Uint8List cleanZipBytes = result.bytes!;
      print('Unlocked! Byte size: ${cleanZipBytes.length}');
      break;

    case DecryptStatus.notProtected:
      print('File was not password protected.');
      break;

    case DecryptStatus.invalidPassword:
      print('Incorrect password: ${result.errorMessage}');
      break;

    case DecryptStatus.corruptedData:
      print('Corrupted or invalid spreadsheet: ${result.errorMessage}');
      break;

    case DecryptStatus.unsupportedFormat:
      print('Format not supported: ${result.errorMessage}');
      break;
  }
}

4. Integration with Excel Parsers

Once decrypted, the resulting bytes are standard unencrypted OOXML ZIP bytes, ready to be fed into any parser:

import 'package:archive/archive.dart';
import 'package:excel_decryptor/excel_decryptor.dart';

void parseDecryptedExcel(Uint8List encryptedBytes, String password) {
  final decryptedBytes = ExcelDecryptor.decrypt(encryptedBytes, password: password);
  if (decryptedBytes == null) throw Exception('Decryption failed');

  // Read with archive package:
  final archive = ZipDecoder().decodeBytes(decryptedBytes);
  for (final file in archive) {
    print('Found file inside package: ${file.name}');
  }
}

Command-Line Example

You can run the bundled example directly from the repository:

dart run example/example.dart test/fixtures/sample_protected.xlsx Password123

Security & Privacy

  • Zero Network Traffic: All cryptographic operations occur entirely in-memory on the client machine. No documents or passwords ever leave the device.
  • Pure Dart Crypto: Uses audited cryptographic primitives from Dart's official crypto and pointycastle libraries.

License

MIT License. See LICENSE for details.

Libraries

excel_decryptor