msoffcrypto-tool

Python library and CLI for decrypting and encrypting password-protected Microsoft Office documents across OOXML and legacy binary formats.

Library
PyPI
v6.0.0
622stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
39/100Needs Attention
Development Activity4
Maintenance0
Community72
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
75/100Good
Architecture78
Code Quality72
Innovation74
Learning Curve75

msoffcrypto-tool is a Python library and command-line tool for decrypting and encrypting Microsoft Office files — Word, Excel, and PowerPoint documents in both the modern OOXML formats (.docx/.xlsx/.pptx) and legacy binary formats (.doc/.xls/.ppt) — protected with a password or other cryptographic key material. It implements the MS-OFFCRYPTO specification, covering ECMA-376 Agile and Standard Encryption, Office Binary Document RC4 CryptoAPI, plain RC4, and XOR obfuscation, so it can unlock files saved by any Office version from 97 through the current OOXML generation.

The library auto-detects a file’s container format (OLE compound file vs. OOXML zip) and encryption scheme, then dispatches to the matching format handler, exposing a single load_key()/decrypt() interface regardless of whether the key is a password, an intermediate secret key, or a private key used for escrow certificates. It also ships an experimental OOXML encryption path for password-protecting plaintext documents. Beyond general document automation, it’s widely used in malware and maldoc analysis pipelines and forensic tooling to recover and inspect encrypted Office payloads.

What You Get

  • A single OfficeFile() factory that auto-detects OLE vs. OOXML containers and the underlying encryption scheme
  • Support for password, intermediate secret-key, and private-key (escrow certificate) decryption
  • Coverage of ECMA-376 Agile/Standard, RC4 CryptoAPI, plain RC4, and XOR obfuscation encryption methods
  • A CLI (msoffcrypto-tool) for decrypting, encrypting, and testing files without writing any Python
  • Experimental OOXML encryption for password-protecting plaintext Word/Excel/PowerPoint files
  • In-memory decryption via file-like objects (io.BytesIO), avoiding temp files entirely

Common Use Cases

  • Bulk-decrypting password-protected Office files in a data pipeline before parsing them with pandas or python-docx
  • Recovering readable content from encrypted MS Office attachments during malware/maldoc analysis
  • Automating password removal for legacy Office archives during a migration
  • Verifying whether a file is encrypted before attempting to open it, from a shell script or CI job
  • Testing forensic and e-discovery tooling against known Office encryption schemes

Under The Hood

Architecture The library centers on a format-dispatch factory, OfficeFile() in msoffcrypto/__init__.py, which sniffs the container (an OLE compound file via olefile.isOleFile, or a ZIP-based OOXML package via zipfile.is_zipfile) and, for OLE files, inspects internal streams (EncryptionInfo, WordDocument, Workbook, PowerPoint Document) to route to the correct format handler — OOXMLFile, Doc97File, Xls97File, or Ppt97File. Each handler implements the BaseOfficeFile abstract base class (format/base.py), which fixes a small, consistent contract (load_key, decrypt, is_encrypted) across every supported format. Format handlers further delegate the actual cryptographic work to method-level modules (method/ecma376_agile.py, ecma376_standard.py, rc4_cryptoapi.py, rc4.py, xor_obfuscation.py), cleanly separating container-format parsing from encryption-algorithm implementation, so adding a new key-derivation scheme doesn’t touch the format-detection layer.

Tech Stack The project is pure Python 3.10+, managed with Poetry, with only two runtime dependencies: cryptography (>=39.0) for AES primitives and olefile (>=0.46) for parsing OLE2 compound file streams; OOXML parsing relies on the standard library (zipfile, xml.dom.minidom). The CLI entry point (msoffcrypto-tool) is a thin argparse-based wrapper registered via Poetry’s [tool.poetry.scripts]. Documentation is built with Sphinx (furo theme) and published on Read the Docs; packaging uses poetry-core as the build backend.

Code Quality The test suite (tests/test_cli.py, tests/test_compare_known_output.py, tests/test_file_handle.py) exercises the CLI and round-trips known encrypted fixture files against expected plaintext output, and pytest is configured with --doctest-modules so the runnable examples embedded in docstrings (e.g. OfficeFile()’s docstring) are also verified as tests. CI runs the suite across Ubuntu, macOS, and Windows on Python 3.10 through 3.14, with coverage tracked via Codecov. Error handling is explicit and typed through a small custom exception hierarchy (FileFormatError, ParseError, DecryptionError, EncryptionError, InvalidKeyError), rather than relying on generic exceptions. The codebase is Black-formatted but does not yet use type hints — the project’s own Todo list lists “Add type hints” as outstanding.

API Design The public surface is deliberately small: three lines (OfficeFile(), load_key(), decrypt()) cover the common case, and the same calls work whether the source is a file handle or an in-memory io.BytesIO buffer. Key material is polymorphic — password, secret key, or private key are all accepted through the same load_key() signature via keyword arguments — so callers don’t need to branch on key type. Optional flags (verify_password, verify_integrity) let advanced users opt into stronger guarantees without complicating the default path. The one rough edge is that encryption uses a separate entry point (OOXMLFile.encrypt() rather than the shared OfficeFile() factory), reflecting its experimental, OOXML-only status.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search