PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Reader\Exception
XOr encryption not supported
Error message
XOr encryption not supported
What it means
The Xls (BIFF) reader can decrypt workbooks using no encryption or RC4-based encryption, but deliberately refuses the legacy XOR 'weak encryption' obfuscation used by very old Excel versions and old third-party writers. When the workbook globals declare MS_BIFF_CRYPTO_XOR, record decryption throws this unsupported-feature exception mid-load.
Source
Thrown at src/PhpSpreadsheet/Reader/Xls.php:373
$this->rc4Key->RC4(str_repeat("\0", $step));
// Decrypt record data (re-keying at the end of every block)
while ($block != $endBlock) {
$step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
$recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
$data = substr($data, $step);
$pos += $step;
$len -= $step;
++$block;
$this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
}
$recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
// Keep track of the position of this decryptor.
// We'll try and re-use it later if we can to speed things up
$this->rc4Pos = $pos + $len;
} elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
throw new Exception('XOr encryption not supported');
}
return $recordData;
}
/**
* Use OLE reader to extract the relevant data streams from the OLE file.
*/
protected function loadOLE(string $filename): void
{
// OLE reader
$ole = new OLERead();
// get excel data,
$ole->read($filename);
// Get workbook data: workbook stream + sheet streams
$this->data = $ole->getStream($ole->wrkbook) ?? '';
// Get summary information data
$this->summaryInformation = $ole->getStream($ole->summaryInformation);View on GitHub (pinned to 65b080eef4)
Solutions
- Open the file in Excel or LibreOffice and re-save it either unprotected or with modern (RC4/AES) encryption, then load the copy
- Strip protection upstream where lawful: LibreOffice headless conversion of the decrypted source (soffice --headless --convert-to xlsx) then use the Xlsx reader
- Pre-screen archives for encrypted workbooks and exclude them from automated ingestion with a clear report rather than a crash
Example fix
// before
$spreadsheet = IOFactory::load('legacy-protected.xls'); // XOR-encrypted -> 'XOr encryption not supported'
# after (shell): decrypt/re-save once with the password, then
soffice --headless --convert-to xlsx legacy-protected.xls
// then in PHP
$spreadsheet = IOFactory::load('legacy-protected.xlsx'); Defensive patterns
Strategy: fallback
Try / catch
try {
$spreadsheet = IOFactory::load($path);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
if (str_contains($e->getMessage(), 'XOr encryption not supported')) {
// legacy weak encryption: convert with the password holder once, e.g.
// soffice --headless --convert-to xlsx --outdir fixed "$path"
// then IOFactory::load('fixed/...xlsx')
}
throw $e;
} Prevention
- Pre-screen protected .xls archives and convert unsupported-encryption files before automated ingestion
- Re-save legacy protected workbooks with modern encryption (Excel 97+/RC4) or none
- Treat XOR-encrypted inputs as an unsupported-format routing decision, not a retryable failure
When it happens
Trigger: Loading a password-protected .xls saved with legacy XOR/weak encryption (Excel 5/95 era or old Delphi/VB/PERL tooling); corporate archives containing pre-97 protected workbooks.
Common situations: Legacy document migration projects; compliance extracts of decades-old spreadsheets; files generated by ancient export libraries.
Related errors
- Cannot read this Excel file. Version is too old.
- Unexpected file pass record length
- Unsupported encryption algorithm
- Decryption password incorrect
- Unrecognized space type in tAttrSpace token
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/419c2dd8c69821f3.
Report an issue: GitHub.