You've already forked lazarus-ccr
fpspreadsheet: Initial commit of experimental xlsx decryption support (xlsx decryptor written by forum user shobits1).
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5806 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
Binary file not shown.
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CONFIG>
|
||||||
|
<ProjectOptions>
|
||||||
|
<Version Value="10"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<General>
|
||||||
|
<Flags>
|
||||||
|
<LRSInOutputDirectory Value="False"/>
|
||||||
|
</Flags>
|
||||||
|
<SessionStorage Value="InProjectDir"/>
|
||||||
|
<MainUnit Value="0"/>
|
||||||
|
<Title Value="ooxml_decrypt_and_read"/>
|
||||||
|
<UseAppBundle Value="False"/>
|
||||||
|
</General>
|
||||||
|
<BuildModes Count="1">
|
||||||
|
<Item1 Name="Default" Default="True"/>
|
||||||
|
</BuildModes>
|
||||||
|
<PublishOptions>
|
||||||
|
<Version Value="2"/>
|
||||||
|
<IgnoreBinaries Value="False"/>
|
||||||
|
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
|
||||||
|
<ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
|
||||||
|
</PublishOptions>
|
||||||
|
<RunParams>
|
||||||
|
<local>
|
||||||
|
<FormatVersion Value="1"/>
|
||||||
|
<LaunchingApplication PathPlusParams="\usr\X11R6\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
|
||||||
|
</local>
|
||||||
|
</RunParams>
|
||||||
|
<RequiredPackages Count="3">
|
||||||
|
<Item1>
|
||||||
|
<PackageName Value="laz_fpspreadsheet_crypto"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<PackageName Value="laz_fpspreadsheet"/>
|
||||||
|
</Item2>
|
||||||
|
<Item3>
|
||||||
|
<PackageName Value="LazUtils"/>
|
||||||
|
</Item3>
|
||||||
|
</RequiredPackages>
|
||||||
|
<Units Count="1">
|
||||||
|
<Unit0>
|
||||||
|
<Filename Value="ooxml_decrypt_and_read.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit0>
|
||||||
|
</Units>
|
||||||
|
</ProjectOptions>
|
||||||
|
<CompilerOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<Target>
|
||||||
|
<Filename Value="ooxml_decrypt_and_read"/>
|
||||||
|
</Target>
|
||||||
|
<SearchPaths>
|
||||||
|
<OtherUnitFiles Value="..\..\..\source\common"/>
|
||||||
|
<UnitOutputDirectory Value="..\..\lib\$(TargetCPU)-$(TargetOS)"/>
|
||||||
|
</SearchPaths>
|
||||||
|
<Parsing>
|
||||||
|
<SyntaxOptions>
|
||||||
|
<UseAnsiStrings Value="False"/>
|
||||||
|
</SyntaxOptions>
|
||||||
|
</Parsing>
|
||||||
|
<Linking>
|
||||||
|
<Debugging>
|
||||||
|
<DebugInfoType Value="dsDwarf2Set"/>
|
||||||
|
<UseExternalDbgSyms Value="True"/>
|
||||||
|
</Debugging>
|
||||||
|
</Linking>
|
||||||
|
</CompilerOptions>
|
||||||
|
</CONFIG>
|
@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
ooxml_decrypt_and_read.lpr
|
||||||
|
|
||||||
|
Demonstrates how to read an Excel 2007 xlsx file which is workbook-protected
|
||||||
|
and thus encrypted by an internal password.
|
||||||
|
|
||||||
|
Basic operating procedure
|
||||||
|
- Add package laz_fpspreadsheet_crypto
|
||||||
|
- Use xlsxooxml_crypto (instead of xlsxooxml)
|
||||||
|
- In Workbook.ReadFromFile specify the file format id spfidOOXML_crypto instead
|
||||||
|
of the the file format sfOOXML.
|
||||||
|
}
|
||||||
|
|
||||||
|
program ooxml_decrypt_and_read;
|
||||||
|
|
||||||
|
{$mode delphi}{$H+}
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, LazUTF8, fpstypes, fpspreadsheet, laz_fpspreadsheet,
|
||||||
|
xlsxooxml_crypto;
|
||||||
|
|
||||||
|
var
|
||||||
|
MyWorkbook: TsWorkbook;
|
||||||
|
MyWorksheet: TsWorksheet;
|
||||||
|
InputFilename: String;
|
||||||
|
MyDir: string;
|
||||||
|
cell: PCell;
|
||||||
|
i: Integer;
|
||||||
|
password: String;
|
||||||
|
Prot_enc: Integer = 1; // 0 - protected, 1 - encrypted workbook
|
||||||
|
|
||||||
|
begin
|
||||||
|
MyDir := ExtractFilePath(ParamStr(0));
|
||||||
|
|
||||||
|
// Open the input file
|
||||||
|
MyDir := ExtractFilePath(ParamStr(0));
|
||||||
|
|
||||||
|
case Prot_enc of
|
||||||
|
0: begin
|
||||||
|
InputFileName := MyDir + 'protected_workbook.xlsx';
|
||||||
|
password := '';
|
||||||
|
end;
|
||||||
|
1: begin
|
||||||
|
InputFileName := MyDir + 'encrypted_workbook.xlsx';
|
||||||
|
password := 'test';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not FileExists(InputFileName) then begin
|
||||||
|
WriteLn('Input file ', InputFileName, ' does not exist. Please run opendocwrite first.');
|
||||||
|
Halt;
|
||||||
|
end;
|
||||||
|
WriteLn('Opening input file ', InputFilename);
|
||||||
|
|
||||||
|
// Create the spreadsheet
|
||||||
|
MyWorkbook := TsWorkbook.Create;
|
||||||
|
|
||||||
|
MyWorkbook.Options := MyWorkbook.Options + [boReadFormulas];
|
||||||
|
MyWorkbook.ReadFromFile(InputFilename, sfidOOXML_crypto, password);
|
||||||
|
|
||||||
|
MyWorksheet := MyWorkbook.GetFirstWorksheet;
|
||||||
|
|
||||||
|
// Write all cells with contents to the console
|
||||||
|
WriteLn('');
|
||||||
|
WriteLn('Contents of the first worksheet of the file:');
|
||||||
|
WriteLn('');
|
||||||
|
|
||||||
|
for cell in MyWorksheet.Cells do
|
||||||
|
WriteLn(
|
||||||
|
'Row: ', cell^.Row,
|
||||||
|
' Col: ', cell^.Col,
|
||||||
|
' Value: ', UTF8ToConsole(MyWorkSheet.ReadAsText(cell^.Row, cell^.Col))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Finalization
|
||||||
|
MyWorkbook.Free;
|
||||||
|
|
||||||
|
{$ifdef WINDOWS}
|
||||||
|
WriteLn;
|
||||||
|
WriteLn('Press ENTER to quit...');
|
||||||
|
ReadLn;
|
||||||
|
{$ENDIF}
|
||||||
|
end.
|
||||||
|
|
Binary file not shown.
@ -0,0 +1,8 @@
|
|||||||
|
This demo program reads encrypted xlsx files.
|
||||||
|
|
||||||
|
File "encrypted_workbook.xlsx" was encrypted in the File menu of Excel 2007.
|
||||||
|
The password is "test".
|
||||||
|
|
||||||
|
The file "protected_workbook.xlsx" was worksheet-protected in the Inspect
|
||||||
|
menu of Excel 2007. The password "test" was specified, it is not the password
|
||||||
|
used for encryption of the file, it is only needed to unlock worksheet protection.
|
@ -22,8 +22,10 @@ type
|
|||||||
procedure ReadNumber(AStream: TStream); override;
|
procedure ReadNumber(AStream: TStream); override;
|
||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsWorkbook); override;
|
constructor Create(AWorkbook: TsWorkbook); override;
|
||||||
procedure ReadFromFile(AFileName: String; AParams: TsStreamParams = []); override;
|
procedure ReadFromFile(AFileName: String; APassword: String = '';
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
AParams: TsStreamParams = []); override;
|
||||||
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); override;
|
||||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -173,14 +175,14 @@ begin
|
|||||||
Unused(AStream);
|
Unused(AStream);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsCSVReader.ReadFromFile(AFileName: String;
|
procedure TsCSVReader.ReadFromFile(AFileName: String; APassword: String = '';
|
||||||
AParams: TsStreamParams = []);
|
AParams: TsStreamParams = []);
|
||||||
begin
|
begin
|
||||||
FWorksheetName := ChangeFileExt(ExtractFileName(AFileName), '');
|
FWorksheetName := ChangeFileExt(ExtractFileName(AFileName), '');
|
||||||
inherited ReadFromFile(AFilename, AParams);
|
inherited ReadFromFile(AFilename, APassword, AParams);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsCSVReader.ReadFromStream(AStream: TStream;
|
procedure TsCSVReader.ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
AParams: TsStreamParams = []);
|
AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
Parser: TCSVParser;
|
Parser: TCSVParser;
|
||||||
@ -227,7 +229,7 @@ var
|
|||||||
begin
|
begin
|
||||||
Stream := TStringStream.Create(AStrings.Text);
|
Stream := TStringStream.Create(AStrings.Text);
|
||||||
try
|
try
|
||||||
ReadFromStream(Stream, AParams);
|
ReadFromStream(Stream, '', AParams);
|
||||||
finally
|
finally
|
||||||
Stream.Free;
|
Stream.Free;
|
||||||
end;
|
end;
|
||||||
|
@ -57,7 +57,8 @@ type
|
|||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsWorkbook); override;
|
constructor Create(AWorkbook: TsWorkbook); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); override;
|
||||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1045,11 +1046,12 @@ begin
|
|||||||
SetLength(FCurrRichTextParams, 0);
|
SetLength(FCurrRichTextParams, 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsHTMLReader.ReadFromStream(AStream: TStream;
|
procedure TsHTMLReader.ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
AParams: TsStreamParams = []);
|
AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
list: TStringList;
|
list: TStringList;
|
||||||
begin
|
begin
|
||||||
|
Unused(APassword);
|
||||||
list := TStringList.Create;
|
list := TStringList.Create;
|
||||||
try
|
try
|
||||||
list.LoadFromStream(AStream);
|
list.LoadFromStream(AStream);
|
||||||
|
@ -165,7 +165,8 @@ type
|
|||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
{ General reading methods }
|
{ General reading methods }
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
procedure ReadFromStream(AStream: TStream;
|
||||||
|
APassword: String = ''; AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsSpreadOpenDocWriter }
|
{ TsSpreadOpenDocWriter }
|
||||||
@ -2422,7 +2423,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadOpenDocReader.ReadFromStream(AStream: TStream;
|
procedure TsSpreadOpenDocReader.ReadFromStream(AStream: TStream;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
Doc : TXMLDocument;
|
Doc : TXMLDocument;
|
||||||
BodyNode, SpreadSheetNode, TableNode: TDOMNode;
|
BodyNode, SpreadSheetNode, TableNode: TDOMNode;
|
||||||
@ -2445,7 +2446,7 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Unused(AParams);
|
Unused(APassword, AParams);
|
||||||
|
|
||||||
Doc := nil;
|
Doc := nil;
|
||||||
try
|
try
|
||||||
|
@ -756,15 +756,15 @@ type
|
|||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
procedure ReadFromFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
procedure ReadFromFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
||||||
AParams: TsStreamParams = []); overload;
|
APassword: String = ''; AParams: TsStreamParams = []); overload;
|
||||||
procedure ReadFromFile(AFileName: string; AFormat: TsSpreadsheetFormat;
|
procedure ReadFromFile(AFileName: string; AFormat: TsSpreadsheetFormat;
|
||||||
AParams: TsStreamParams = []); overload;
|
AParams: TsStreamParams = []); overload;
|
||||||
procedure ReadFromFile(AFileName: string;
|
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
||||||
AParams: TsStreamParams = []); overload;
|
AParams: TsStreamParams = []); overload;
|
||||||
procedure ReadFromFileIgnoringExtension(AFileName: string;
|
procedure ReadFromFileIgnoringExtension(AFileName: string;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
procedure ReadFromStream(AStream: TStream; AFormatID: TsSpreadFormatID;
|
procedure ReadFromStream(AStream: TStream; AFormatID: TsSpreadFormatID;
|
||||||
AParams: TsStreamParams = []); overload;
|
APassword: String = ''; AParams: TsStreamParams = []); overload;
|
||||||
procedure ReadFromStream(AStream: TStream; AFormat: TsSpreadsheetFormat;
|
procedure ReadFromStream(AStream: TStream; AFormat: TsSpreadsheetFormat;
|
||||||
AParams: TsStreamParams = []); overload;
|
AParams: TsStreamParams = []); overload;
|
||||||
|
|
||||||
@ -8199,7 +8199,7 @@ procedure TsWorkbook.ReadFromFile(AFileName: string;
|
|||||||
begin
|
begin
|
||||||
if AFormat = sfUser then
|
if AFormat = sfUser then
|
||||||
raise Exception.Create('[TsWorkbook.ReadFromFile] Don''t call this method for user-provided file formats.');
|
raise Exception.Create('[TsWorkbook.ReadFromFile] Don''t call this method for user-provided file formats.');
|
||||||
ReadFromFile(AFilename, ord(AFormat), AParams);
|
ReadFromFile(AFilename, ord(AFormat), '', AParams);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
@ -8209,8 +8209,8 @@ end;
|
|||||||
@param AFileName Name of the file to be read
|
@param AFileName Name of the file to be read
|
||||||
@param AFormatID Identifier of the file format assumed
|
@param AFormatID Identifier of the file format assumed
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorkbook.ReadFromFile(AFileName: string;
|
procedure TsWorkbook.ReadFromFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
||||||
AFormatID: TsSpreadFormatID; AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
AReader: TsBasicSpreadReader;
|
AReader: TsBasicSpreadReader;
|
||||||
ok: Boolean;
|
ok: Boolean;
|
||||||
@ -8219,7 +8219,7 @@ begin
|
|||||||
raise Exception.CreateFmt(rsFileNotFound, [AFileName]);
|
raise Exception.CreateFmt(rsFileNotFound, [AFileName]);
|
||||||
|
|
||||||
if AFormatID = sfIDUnknown then begin
|
if AFormatID = sfIDUnknown then begin
|
||||||
ReadFromFile(AFileName, AParams);
|
ReadFromFile(AFileName, APassword, AParams);
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -8231,7 +8231,7 @@ begin
|
|||||||
FReadWriteFlag := rwfRead;
|
FReadWriteFlag := rwfRead;
|
||||||
inc(FLockCount); // This locks various notifications from being sent
|
inc(FLockCount); // This locks various notifications from being sent
|
||||||
try
|
try
|
||||||
AReader.ReadFromFile(AFileName, AParams);
|
AReader.ReadFromFile(AFileName, APassword, AParams);
|
||||||
ok := true;
|
ok := true;
|
||||||
UpdateCaches;
|
UpdateCaches;
|
||||||
if (boAutoCalc in Options) then
|
if (boAutoCalc in Options) then
|
||||||
@ -8253,7 +8253,8 @@ end;
|
|||||||
the extension. In the case of the ambiguous xls extension, it will simply
|
the extension. In the case of the ambiguous xls extension, it will simply
|
||||||
assume that it is BIFF8. Note that it could be BIFF2 or 5 as well.
|
assume that it is BIFF8. Note that it could be BIFF2 or 5 as well.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorkbook.ReadFromFile(AFileName: string; AParams: TsStreamParams = []);
|
procedure TsWorkbook.ReadFromFile(AFileName: string; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
formatID: TsSpreadFormatID;
|
formatID: TsSpreadFormatID;
|
||||||
canLoad, success: Boolean;
|
canLoad, success: Boolean;
|
||||||
@ -8293,7 +8294,7 @@ begin
|
|||||||
success := false;
|
success := false;
|
||||||
for i:=0 to High(fileformats) do begin
|
for i:=0 to High(fileformats) do begin
|
||||||
try
|
try
|
||||||
ReadFromFile(AFileName, fileformats[i], AParams);
|
ReadFromFile(AFileName, fileformats[i], APassword, AParams);
|
||||||
success := true;
|
success := true;
|
||||||
break; // Exit the loop if we reach this point successfully.
|
break; // Exit the loop if we reach this point successfully.
|
||||||
except
|
except
|
||||||
@ -8310,7 +8311,7 @@ end;
|
|||||||
Reads the document from a file, but ignores the extension.
|
Reads the document from a file, but ignores the extension.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorkbook.ReadFromFileIgnoringExtension(AFileName: string;
|
procedure TsWorkbook.ReadFromFileIgnoringExtension(AFileName: string;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
formatID: TsSpreadFormatID;
|
formatID: TsSpreadFormatID;
|
||||||
fileformats: TsSpreadFormatIDArray;
|
fileformats: TsSpreadFormatIDArray;
|
||||||
@ -8319,7 +8320,7 @@ begin
|
|||||||
fileformats := GetSpreadFormats(faRead, [ord(sfOOXML), ord(sfOpenDocument), ord(sfExcel8)]);
|
fileformats := GetSpreadFormats(faRead, [ord(sfOOXML), ord(sfOpenDocument), ord(sfExcel8)]);
|
||||||
for formatID in fileformats do begin
|
for formatID in fileformats do begin
|
||||||
try
|
try
|
||||||
ReadFromFile(AFileName, formatID, AParams);
|
ReadFromFile(AFileName, formatID, APassword, AParams);
|
||||||
success := true;
|
success := true;
|
||||||
break;
|
break;
|
||||||
except
|
except
|
||||||
@ -8342,7 +8343,7 @@ procedure TsWorkbook.ReadFromStream(AStream: TStream;
|
|||||||
begin
|
begin
|
||||||
if AFormat = sfUser then
|
if AFormat = sfUser then
|
||||||
raise Exception.Create('[TsWorkbook.ReadFromFile] Don''t call this method for user-provided file formats.');
|
raise Exception.Create('[TsWorkbook.ReadFromFile] Don''t call this method for user-provided file formats.');
|
||||||
ReadFromStream(AStream, ord(AFormat), AParams);
|
ReadFromStream(AStream, ord(AFormat), '', AParams);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
@ -8352,8 +8353,8 @@ end;
|
|||||||
@param AFormatID Identifier of the file format assumed.
|
@param AFormatID Identifier of the file format assumed.
|
||||||
@param AParams Optional parameters to control stream access.
|
@param AParams Optional parameters to control stream access.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorkbook.ReadFromStream(AStream: TStream;
|
procedure TsWorkbook.ReadFromStream(AStream: TStream; AFormatID: TsSpreadFormatID;
|
||||||
AFormatID: TsSpreadFormatID; AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
AReader: TsBasicSpreadReader;
|
AReader: TsBasicSpreadReader;
|
||||||
ok: Boolean;
|
ok: Boolean;
|
||||||
@ -8366,7 +8367,7 @@ begin
|
|||||||
inc(FLockCount);
|
inc(FLockCount);
|
||||||
try
|
try
|
||||||
AStream.Position := 0;
|
AStream.Position := 0;
|
||||||
AReader.ReadFromStream(AStream, AParams);
|
AReader.ReadFromStream(AStream, APassword, AParams);
|
||||||
ok := true;
|
ok := true;
|
||||||
UpdateCaches;
|
UpdateCaches;
|
||||||
if (boAutoCalc in Options) then
|
if (boAutoCalc in Options) then
|
||||||
|
@ -47,8 +47,10 @@ type
|
|||||||
TsBasicSpreadReader = class(TsBasicSpreadReaderWriter)
|
TsBasicSpreadReader = class(TsBasicSpreadReaderWriter)
|
||||||
public
|
public
|
||||||
{ General writing methods }
|
{ General writing methods }
|
||||||
procedure ReadFromFile(AFileName: string; AParams: TsStreamParams = []); virtual; abstract;
|
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); virtual; abstract;
|
AParams: TsStreamParams = []); virtual; abstract;
|
||||||
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); virtual; abstract;
|
||||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
|
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -112,8 +114,10 @@ type
|
|||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
{ General writing methods }
|
{ General writing methods }
|
||||||
procedure ReadFromFile(AFileName: string; AParams: TsStreamParams = []); override;
|
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
AParams: TsStreamParams = []); override;
|
||||||
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); override;
|
||||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
||||||
|
|
||||||
{@@ List of number formats found in the workbook. }
|
{@@ List of number formats found in the workbook. }
|
||||||
@ -460,7 +464,7 @@ end;
|
|||||||
@see TsWorkbook
|
@see TsWorkbook
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsCustomSpreadReader.ReadFromFile(AFileName: string;
|
procedure TsCustomSpreadReader.ReadFromFile(AFileName: string;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
stream, fs: TStream;
|
stream, fs: TStream;
|
||||||
begin
|
begin
|
||||||
@ -482,7 +486,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
try
|
try
|
||||||
ReadFromStream(stream, AParams);
|
ReadFromStream(stream, APassword, AParams);
|
||||||
finally
|
finally
|
||||||
stream.Free;
|
stream.Free;
|
||||||
end;
|
end;
|
||||||
@ -501,11 +505,12 @@ end;
|
|||||||
@param AData Workbook which is filled by the data from the stream.
|
@param AData Workbook which is filled by the data from the stream.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsCustomSpreadReader.ReadFromStream(AStream: TStream;
|
procedure TsCustomSpreadReader.ReadFromStream(AStream: TStream;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
AStringStream: TStringStream;
|
AStringStream: TStringStream;
|
||||||
AStrings: TStringList;
|
AStrings: TStringList;
|
||||||
begin
|
begin
|
||||||
|
Unused(APassword);
|
||||||
AStringStream := TStringStream.Create('');
|
AStringStream := TStringStream.Create('');
|
||||||
AStrings := TStringList.Create;
|
AStrings := TStringList.Create;
|
||||||
try
|
try
|
||||||
|
@ -77,7 +77,8 @@ type
|
|||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsWorkbook); override;
|
constructor Create(AWorkbook: TsWorkbook); override;
|
||||||
{ General reading methods }
|
{ General reading methods }
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -576,14 +577,14 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
procedure TsSpreadBIFF2Reader.ReadFromStream(AStream: TStream;
|
procedure TsSpreadBIFF2Reader.ReadFromStream(AStream: TStream;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
BIFF2EOF: Boolean;
|
BIFF2EOF: Boolean;
|
||||||
RecordType: Word;
|
RecordType: Word;
|
||||||
CurStreamPos: Int64;
|
CurStreamPos: Int64;
|
||||||
BOFFound: Boolean;
|
BOFFound: Boolean;
|
||||||
begin
|
begin
|
||||||
Unused(AParams);
|
Unused(APassword, AParams);
|
||||||
BIFF2EOF := False;
|
BIFF2EOF := False;
|
||||||
|
|
||||||
{ In BIFF2 files there is only one worksheet, let's create it }
|
{ In BIFF2 files there is only one worksheet, let's create it }
|
||||||
|
@ -89,7 +89,8 @@ type
|
|||||||
procedure ReadXF(AStream: TStream);
|
procedure ReadXF(AStream: TStream);
|
||||||
public
|
public
|
||||||
{ General reading methods }
|
{ General reading methods }
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsSpreadBIFF5Writer }
|
{ TsSpreadBIFF5Writer }
|
||||||
@ -925,13 +926,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream;
|
procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
OLEStream: TMemoryStream;
|
OLEStream: TMemoryStream;
|
||||||
OLEStorage: TOLEStorage;
|
OLEStorage: TOLEStorage;
|
||||||
OLEDocument: TOLEDocument;
|
OLEDocument: TOLEDocument;
|
||||||
begin
|
begin
|
||||||
Unused(AParams);
|
Unused(APassword, AParams);
|
||||||
|
|
||||||
OLEStream := TMemoryStream.Create;
|
OLEStream := TMemoryStream.Create;
|
||||||
try
|
try
|
||||||
|
@ -126,7 +126,8 @@ type
|
|||||||
procedure ReadXF(const AStream: TStream);
|
procedure ReadXF(const AStream: TStream);
|
||||||
public
|
public
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
procedure ReadFromStream(AStream: TStream;
|
||||||
|
APassword: String = ''; AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsSpreadBIFF8Writer }
|
{ TsSpreadBIFF8Writer }
|
||||||
@ -958,13 +959,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadBIFF8Reader.ReadFromStream(AStream: TStream;
|
procedure TsSpreadBIFF8Reader.ReadFromStream(AStream: TStream;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
OLEStream: TMemoryStream;
|
OLEStream: TMemoryStream;
|
||||||
OLEStorage: TOLEStorage;
|
OLEStorage: TOLEStorage;
|
||||||
OLEDocument: TOLEDocument;
|
OLEDocument: TOLEDocument;
|
||||||
begin
|
begin
|
||||||
Unused(AParams);
|
Unused(APassword, AParams);
|
||||||
OLEStream := TMemoryStream.Create;
|
OLEStream := TMemoryStream.Create;
|
||||||
try
|
try
|
||||||
// Only one stream is necessary for any number of worksheets
|
// Only one stream is necessary for any number of worksheets
|
||||||
|
@ -44,7 +44,7 @@ uses
|
|||||||
fpszipper,
|
fpszipper,
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
fpsTypes, fpSpreadsheet, fpsUtils, fpsReaderWriter, fpsNumFormat, fpsPalette,
|
fpsTypes, fpSpreadsheet, fpsUtils, fpsReaderWriter, fpsNumFormat, fpsPalette,
|
||||||
fpsxmlcommon, xlsCommon;
|
fpsxmlcommon, xlsCommon; //, xlsxdecrypter;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -105,7 +105,8 @@ type
|
|||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsWorkbook); override;
|
constructor Create(AWorkbook: TsWorkbook); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsSpreadOOXMLWriter }
|
{ TsSpreadOOXMLWriter }
|
||||||
@ -2352,7 +2353,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadOOXMLReader.ReadFromStream(AStream: TStream;
|
procedure TsSpreadOOXMLReader.ReadFromStream(AStream: TStream;
|
||||||
AParams: TsStreamParams = []);
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
var
|
var
|
||||||
Doc : TXMLDocument;
|
Doc : TXMLDocument;
|
||||||
RelsNode: TDOMNode;
|
RelsNode: TDOMNode;
|
||||||
@ -2374,7 +2375,7 @@ var
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Unused(AParams);
|
Unused(APassword, AParams);
|
||||||
Doc := nil;
|
Doc := nil;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
596
components/fpspreadsheet/source/crypto/xlsxdecrypter.pas
Normal file
596
components/fpspreadsheet/source/crypto/xlsxdecrypter.pas
Normal file
@ -0,0 +1,596 @@
|
|||||||
|
unit xlsxdecrypter;
|
||||||
|
{
|
||||||
|
Some of the ideas are aquired from http://www.lyquidity.com/devblog/?p=35
|
||||||
|
(the `internal` or `default password`): VelvetSweatshop
|
||||||
|
}
|
||||||
|
{$ifdef fpc}
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
// {$mode delphi}
|
||||||
|
{$endif}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes
|
||||||
|
, SysUtils
|
||||||
|
, sha1
|
||||||
|
, DCPrijndael
|
||||||
|
;
|
||||||
|
|
||||||
|
const
|
||||||
|
CFB_Signature = $E11AB1A1E011CFD0; // Compound File Binary Signature
|
||||||
|
// Weird is the documentation is equal to
|
||||||
|
// $D0CF11E0A1B11AE1, but here is inversed
|
||||||
|
// maybe related to litle endian thing?!!
|
||||||
|
|
||||||
|
// EncryptionHeaderFlags as defined in 2.3.1 [MS-OFFCRYPTO]
|
||||||
|
ehfAES = $00000004;
|
||||||
|
//ehfExternal = $00000008;
|
||||||
|
//ehfDocProps = $00000010;
|
||||||
|
ehfCryptoAPI = $00000020;
|
||||||
|
|
||||||
|
// AlgorithmID
|
||||||
|
algRC4 = $00006801;
|
||||||
|
algAES128 = $0000660E;
|
||||||
|
algAES192 = $0000660F;
|
||||||
|
algAES256 = $00006610;
|
||||||
|
|
||||||
|
// HashID
|
||||||
|
hsSHA1 = $00008004;
|
||||||
|
|
||||||
|
// ProviderType
|
||||||
|
prRC4 = $00000001;
|
||||||
|
prAES = $00000018;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
TVersion = packed record
|
||||||
|
Major : Word;
|
||||||
|
Minor : Word
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Defined in Section 2.3.2, 2.3.4.5 [MS-OFFCRYPTO] }
|
||||||
|
TEncryptionHeader = record
|
||||||
|
Flags : DWord; { defined in section 2.3.1 [MS-OFFCRYPTO] }
|
||||||
|
SizeExtra : DWord; { Must be equal to 0 }
|
||||||
|
AlgorithmID : DWord; { $00006801 -- RC4 }
|
||||||
|
{ $0000660E -- AES128}
|
||||||
|
{ $0000660F -- AES192}
|
||||||
|
{ $00006610 -- AES256}
|
||||||
|
|
||||||
|
HashID : DWord; { $00008004 -- SHA1 }
|
||||||
|
|
||||||
|
KeySize : DWord; { RC4 -- 40bits to 128bits (8-bit increments) }
|
||||||
|
{ AES128 -- 128 bits }
|
||||||
|
{ AES192 -- 192 bits }
|
||||||
|
{ AES256 -- 256 bits }
|
||||||
|
|
||||||
|
ProviderType: DWord; { $00000001 -- RC4 }
|
||||||
|
{ $00000018 -- AES }
|
||||||
|
|
||||||
|
Reserved1 : DWord; { Ignored }
|
||||||
|
Reserved2 : DWord; { Must be equal to 0 }
|
||||||
|
CSP_Name : string;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Defined in Section 2.3.3 [MS-OFFCRYPTO] }
|
||||||
|
TEncryptionVerifier = record
|
||||||
|
SaltSize : DWord;
|
||||||
|
Salt : array[0..15] of Byte;
|
||||||
|
EncryptedVerifier : array[0..15] of Byte;
|
||||||
|
VerifierHashSize : DWord;
|
||||||
|
EncryptedVerifierHash: array[0..31] of Byte; // RC4 needs only 20 bytes
|
||||||
|
end;
|
||||||
|
|
||||||
|
// The EncryptionInfo Stream as define in 2.3.4.5 [MS-OFFCRYPTO]
|
||||||
|
TEncryptionInfo = record
|
||||||
|
Version : TVersion;
|
||||||
|
Flags : DWord;
|
||||||
|
HeaderSize: DWord;
|
||||||
|
Header : TEncryptionHeader;
|
||||||
|
Verifier : TEncryptionVerifier;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TExcelFileDecryptor }
|
||||||
|
TExcelFileDecryptor = class
|
||||||
|
private
|
||||||
|
FEncInfo : TEncryptionInfo;
|
||||||
|
FEncryptionKey : TBytes;
|
||||||
|
|
||||||
|
// return empty string if everything done right otherwise the error message.
|
||||||
|
function InitEncryptionInfo(AStream: TStream): string;
|
||||||
|
|
||||||
|
//CheckPasswordInternal should be called after InitEncryptionInfo
|
||||||
|
function CheckPasswordInternal( APassword: UnicodeString ): Boolean;
|
||||||
|
|
||||||
|
public
|
||||||
|
// return empty string if everything done right otherwise the error message.
|
||||||
|
function Decrypt(inFileName: string; outStream: TStream): string; overload;
|
||||||
|
function Decrypt(inStream: TStream; outStream: TStream):string; overload;
|
||||||
|
|
||||||
|
// made this private because I don't know if it'll work with other passwords
|
||||||
|
function Decrypt(inFileName: string; outStream: TStream; APassword: UnicodeString): string; overload;
|
||||||
|
function Decrypt(inStream: TStream; outStream: TStream; APassword: UnicodeString): string; overload;
|
||||||
|
|
||||||
|
// return true if the password is correct.
|
||||||
|
function CheckPassword(AFileName: string; APassword: UnicodeString): Boolean;
|
||||||
|
function CheckPassword(AStream: TStream; APassword: UnicodeString): Boolean;
|
||||||
|
|
||||||
|
function isEncryptedAndSupported(AFileName: string): Boolean;
|
||||||
|
function isEncryptedAndSupported(AStream: TStream): Boolean;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
fpolebasic
|
||||||
|
;
|
||||||
|
|
||||||
|
procedure ConcatToByteArray(var outArray: TBytes; Arr1: TBytes; Arr2: TBytes);
|
||||||
|
var
|
||||||
|
LenArr1 : Integer;
|
||||||
|
LenArr2 : Integer;
|
||||||
|
begin
|
||||||
|
LenArr1 := Length(Arr1);
|
||||||
|
LenArr2 := Length(Arr2);
|
||||||
|
|
||||||
|
SetLength( outArray, LenArr1 + LenArr2 );
|
||||||
|
|
||||||
|
if LenArr1 > 0 then
|
||||||
|
Move(Arr1[0], outArray[0], LenArr1);
|
||||||
|
|
||||||
|
if LenArr2 > 0 then
|
||||||
|
Move(Arr2[0], outArray[LenArr1], LenArr2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure ConcatToByteArray(var outArray: TBytes; AValue: DWord; Arr: TBytes);
|
||||||
|
var
|
||||||
|
LenArr : Integer;
|
||||||
|
begin
|
||||||
|
LenArr := Length(Arr);
|
||||||
|
|
||||||
|
SetLength( outArray, 4 + LenArr );
|
||||||
|
|
||||||
|
Move(AValue, outArray[0], 4);
|
||||||
|
|
||||||
|
if LenArr > 0 then
|
||||||
|
Move(Arr[0], outArray[4], LenArr);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure ConcatToByteArray(var outArray: TBytes; Arr: TBytes; AValue: DWord);
|
||||||
|
var
|
||||||
|
LenArr : Integer;
|
||||||
|
begin
|
||||||
|
LenArr := Length(Arr);
|
||||||
|
|
||||||
|
SetLength( outArray, 4 + LenArr );
|
||||||
|
|
||||||
|
if LenArr > 0 then
|
||||||
|
Move(Arr[0], outArray[0], LenArr);
|
||||||
|
|
||||||
|
Move(AValue, outArray[LenArr], 4);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.InitEncryptionInfo(AStream: TStream): string;
|
||||||
|
var
|
||||||
|
EncInfoStream: TMemoryStream;
|
||||||
|
OLEStorage: TOLEStorage;
|
||||||
|
OLEDocument: TOLEDocument;
|
||||||
|
FileSignature: QWord;
|
||||||
|
Pos : Int64;
|
||||||
|
|
||||||
|
Err : string;
|
||||||
|
begin
|
||||||
|
Err := '';
|
||||||
|
|
||||||
|
if not Assigned(AStream) then
|
||||||
|
Exit( 'Stream is null' );
|
||||||
|
|
||||||
|
AStream.Position := 0;
|
||||||
|
FileSignature := AStream.ReadQWord;
|
||||||
|
if FileSignature <> QWord(CFB_Signature) then
|
||||||
|
Exit( 'Wrong file signature' );
|
||||||
|
|
||||||
|
EncInfoStream := TMemoryStream.Create;
|
||||||
|
try
|
||||||
|
OLEStorage := TOLEStorage.Create;
|
||||||
|
try
|
||||||
|
OLEDocument.Stream := EncInfoStream;
|
||||||
|
AStream.Position := 0;
|
||||||
|
OLEStorage.ReadOLEStream(AStream, OLEDocument, 'EncryptionInfo');
|
||||||
|
if OLEDocument.Stream.Size = 0 then
|
||||||
|
raise Exception.Create('EncryptionInfo stream not found.');
|
||||||
|
|
||||||
|
EncInfoStream.Position := 0;
|
||||||
|
|
||||||
|
{ Major Version: $0002 = Excel 2003
|
||||||
|
$0003 = Excel 2007 | 2007 SP1
|
||||||
|
$0004 = Excel 2007 SP2 (not sure about 2010 | 2013) }
|
||||||
|
FEncInfo.Version.Major := EncInfoStream.ReadWord;
|
||||||
|
if (FEncInfo.Version.Major <> 3) and (FEncInfo.Version.Major <> 4) then
|
||||||
|
raise Exception.Create('File must be created with 2007 or 2010');
|
||||||
|
|
||||||
|
{ Minor Version: must be equal to $0002 }
|
||||||
|
FEncInfo.Version.Minor := EncInfoStream.ReadWord;
|
||||||
|
if FEncInfo.Version.Minor <> 2 then
|
||||||
|
raise Exception.Create('Incorrect File Version');
|
||||||
|
|
||||||
|
FEncInfo.Flags := EncInfoStream.ReadDWord;
|
||||||
|
FEncInfo.HeaderSize := EncInfoStream.ReadDWord;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ENCRYPTION HEADER
|
||||||
|
///
|
||||||
|
Pos := EncInfoStream.Position;
|
||||||
|
With FEncInfo.Header do
|
||||||
|
begin
|
||||||
|
Flags := EncInfoStream.ReadDWord;
|
||||||
|
if (Flags and ehfCryptoAPI) <> ehfCryptoAPI then
|
||||||
|
raise Exception.Create('File not encrypted');
|
||||||
|
if (Flags and ehfAES) <> ehfAES then
|
||||||
|
raise Exception.Create('Encryption must be AES');
|
||||||
|
|
||||||
|
SizeExtra := EncInfoStream.ReadDWord;
|
||||||
|
if SizeExtra <> 0 then
|
||||||
|
raise Exception.Create('Wrong Header.SizeExtra');
|
||||||
|
|
||||||
|
AlgorithmID := EncInfoStream.ReadDWord;
|
||||||
|
if ( AlgorithmID <> algAES128 )
|
||||||
|
and( AlgorithmID <> algAES192 )
|
||||||
|
and( AlgorithmID <> algAES256 )
|
||||||
|
//and( AlgorithmID <> algRC4 ) // not used by ECMA-376 format
|
||||||
|
then
|
||||||
|
raise Exception.Create('Unknown Encryption Algorithm');
|
||||||
|
|
||||||
|
HashID := EncInfoStream.ReadDWord;
|
||||||
|
if HashID <> hsSHA1 then
|
||||||
|
raise Exception.Create('Unknown Hashing Algorithm');
|
||||||
|
|
||||||
|
KeySize := EncInfoStream.ReadDWord;
|
||||||
|
if ( (AlgorithmID = algAES128) and (KeySize <> 128) )
|
||||||
|
or( (AlgorithmID = algAES192) and (KeySize <> 192) )
|
||||||
|
or( (AlgorithmID = algAES256) and (KeySize <> 256) )
|
||||||
|
//or( (AlgorithmID = algRC4) and (KeySize < 40 or KeySize > 128) )
|
||||||
|
then
|
||||||
|
raise Exception.Create('Incorrect Key Size');
|
||||||
|
|
||||||
|
ProviderType:= EncInfoStream.ReadDWord;
|
||||||
|
if ( ProviderType <> prAES )
|
||||||
|
//and( FEncInfo.Header.ProviderType <> prRC4 )
|
||||||
|
then
|
||||||
|
raise Exception.Create('Unknown Provider');
|
||||||
|
|
||||||
|
Reserved1 := EncInfoStream.ReadDWord;
|
||||||
|
Reserved2 := EncInfoStream.ReadDWord;
|
||||||
|
if Reserved2 <> 0 then
|
||||||
|
raise Exception.Create('Reserved2 must equal to 0');
|
||||||
|
|
||||||
|
//CSP_Name := Not needed
|
||||||
|
// CSP: Should be Microsoft Enhanced RSA and AES Cryptographic Provider
|
||||||
|
// or Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)
|
||||||
|
//Skip CSP Name
|
||||||
|
EncInfoStream.Position := Pos + FEncInfo.HeaderSize;
|
||||||
|
end;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ENCRYPTION VERIFIER
|
||||||
|
///
|
||||||
|
with FEncInfo.Verifier do
|
||||||
|
begin
|
||||||
|
SaltSize := EncInfoStream.ReadDWord;
|
||||||
|
if FEncInfo.Verifier.SaltSize <> 16 then
|
||||||
|
raise Exception.Create('Incorrect salt size');
|
||||||
|
|
||||||
|
EncInfoStream.ReadBuffer(Salt[0], SaltSize);
|
||||||
|
EncInfoStream.ReadBuffer(EncryptedVerifier[0], SaltSize);
|
||||||
|
|
||||||
|
VerifierHashSize := EncInfoStream.ReadDWord;
|
||||||
|
|
||||||
|
if FEncInfo.Header.ProviderType = prAES then
|
||||||
|
EncInfoStream.ReadBuffer( EncryptedVerifierHash[0], 32);
|
||||||
|
{ for RC4
|
||||||
|
else if FEncInfo.Header.ProviderType = prRC4 then
|
||||||
|
EncInfoStream.ReadBuffer( EncryptedVerifierHash[0], 20); }
|
||||||
|
end;
|
||||||
|
|
||||||
|
Err := '';
|
||||||
|
except
|
||||||
|
on E: Exception do
|
||||||
|
Err := E.Message;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
if Assigned(OLEStorage) then
|
||||||
|
OLEStorage.Free;
|
||||||
|
|
||||||
|
EncInfoStream.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := Err;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.CheckPasswordInternal(APassword: UnicodeString): Boolean;
|
||||||
|
var
|
||||||
|
AES_Cipher: TDCP_rijndael;
|
||||||
|
|
||||||
|
ConcArr : TBytes;
|
||||||
|
LastHash: TSHA1Digest;
|
||||||
|
|
||||||
|
Iterator, i: DWord;
|
||||||
|
|
||||||
|
X1_Buff: array[0..63] of byte;
|
||||||
|
X2_Buff: array[0..63] of byte;
|
||||||
|
X1_Hash: TSHA1Digest;
|
||||||
|
X2_Hash: TSHA1Digest;
|
||||||
|
|
||||||
|
EncryptionKeySize : Integer;
|
||||||
|
|
||||||
|
Verifier : array[0..15] of Byte;
|
||||||
|
VerifierHash: array[0..31] of Byte;// Needs only 20bytes to hold the SHA1
|
||||||
|
// but needs 32bytes to hold the decrypted hash
|
||||||
|
begin
|
||||||
|
// if no password used, use microsoft default.
|
||||||
|
if APassword = '' then
|
||||||
|
APassword := 'VelvetSweatshop';
|
||||||
|
|
||||||
|
//// [MS-OFFCRYPTO]
|
||||||
|
//// 2.3.4.7 ECMA-376 Document Encryption Key Generation
|
||||||
|
|
||||||
|
// 1.1.Concat Salt and Password
|
||||||
|
// Calculate SHA1(0) = SHA1(salt + password)
|
||||||
|
ConcatToByteArray( ConcArr
|
||||||
|
, FEncInfo.Verifier.Salt
|
||||||
|
, TEncoding.Unicode.GetBytes(APassword));
|
||||||
|
LastHash := SHA1Buffer( ConcArr[0], Length(ConcArr) );
|
||||||
|
|
||||||
|
// 1.2.Calculate SHA1(n) = SHA1(iterator + SHA1(n-1) ) -- iterator is 32bit
|
||||||
|
for Iterator := 0 to 49999 do
|
||||||
|
begin
|
||||||
|
ConcatToByteArray(ConcArr, Iterator, LastHash);
|
||||||
|
LastHash := SHA1Buffer( ConcArr[0], Length(ConcArr) );
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 1.3.Claculate final hash, SHA1(final) = SHA1(H(n) + block) -- block = 0 (32bit)
|
||||||
|
ConcatToByteArray(ConcArr, LastHash, 0);
|
||||||
|
LastHash := SHA1Buffer( ConcArr[0], Length(ConcArr) );
|
||||||
|
|
||||||
|
|
||||||
|
//// 2.Derive the encryption key.
|
||||||
|
// 2.1 cbRequiredKeyLength for AES is 128,192,256bit ?!!! must be < 40bytes
|
||||||
|
// 2.2 cbHash = 20bytes ( 160bit),, length of SHA1 hash
|
||||||
|
// 2.3 + 2.4 Claculate X1 and X2 the SHA of the generated 64bit Arrays.
|
||||||
|
|
||||||
|
// FillByte(X1_Buff[0], 64, $36);
|
||||||
|
// FillByte(X2_Buff[0], 64, $5C);
|
||||||
|
for i := 0 to 19 do
|
||||||
|
begin
|
||||||
|
X1_Buff[i] := LastHash[i] xor $36;
|
||||||
|
X2_Buff[i] := LastHash[i] xor $5C;
|
||||||
|
end;
|
||||||
|
for i := 20 to 63 do
|
||||||
|
begin
|
||||||
|
X1_Buff[i] := $36;
|
||||||
|
X2_Buff[i] := $5C;
|
||||||
|
end;
|
||||||
|
|
||||||
|
X1_Hash := SHA1Buffer( X1_Buff[0], Length(X1_Buff) );
|
||||||
|
X2_Hash := SHA1Buffer( X2_Buff[0], Length(X2_Buff) );
|
||||||
|
|
||||||
|
// 2.5 Concat X1, X2 -> X3 = X1 + X2 (X3 = 40bytes in length)
|
||||||
|
//ConcatToByteArray( ConcArr, X1_Hash, X2_Hash );
|
||||||
|
|
||||||
|
// 2.6 Let keyDerived be equal to the first cbRequiredKeyLength bytes of X3.
|
||||||
|
// We'll fill the Encryption key on the fly, so we won't need X3
|
||||||
|
// This Key (FEncryptionKey) is used for decryption method
|
||||||
|
EncryptionKeySize := FEncInfo.Header.KeySize div 8; // Convert Size from bits to bytes
|
||||||
|
SetLength(FEncryptionKey, EncryptionKeySize);
|
||||||
|
if EncryptionKeySize <= 20 then
|
||||||
|
begin
|
||||||
|
Move(X1_Hash[0], FEncryptionKey[0], EncryptionKeySize);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Move(X1_Hash[0], FEncryptionKey[0], EncryptionKeySize);
|
||||||
|
Move(X2_Hash[0], FEncryptionKey[20], EncryptionKeySize-20);
|
||||||
|
end;
|
||||||
|
|
||||||
|
//// 2.3.4.9 Password Verification
|
||||||
|
// 1. Encryption key is FEncryptionKey
|
||||||
|
|
||||||
|
// 2. Decrypt the EncryptedVerifier
|
||||||
|
AES_Cipher := TDCP_rijndael.Create(nil);
|
||||||
|
AES_Cipher.Init( FEncryptionKey[0], FEncInfo.Header.KeySize, nil );
|
||||||
|
AES_Cipher.DecryptECB(FEncInfo.Verifier.EncryptedVerifier[0] , Verifier[0]);
|
||||||
|
|
||||||
|
// 3. Decrypt the DecryptedVerifierHash
|
||||||
|
AES_Cipher.Burn;
|
||||||
|
AES_Cipher.Init( FEncryptionKey[0], FEncInfo.Header.KeySize, nil );
|
||||||
|
AES_Cipher.DecryptECB(FEncInfo.Verifier.EncryptedVerifierHash[0] , VerifierHash[0]);
|
||||||
|
AES_Cipher.DecryptECB(FEncInfo.Verifier.EncryptedVerifierHash[16], VerifierHash[16]);
|
||||||
|
AES_Cipher.Free;
|
||||||
|
|
||||||
|
// 4. Calculate SHA1(Verifier)
|
||||||
|
LastHash := SHA1Buffer(Verifier[0], Length(Verifier));
|
||||||
|
|
||||||
|
// 5. Compare results
|
||||||
|
Result := (CompareByte( LastHash[0], VerifierHash[0], 20) = 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.Decrypt(inFileName: string; outStream: TStream
|
||||||
|
): string;
|
||||||
|
begin
|
||||||
|
Result := Decrypt(inFileName, outStream, 'VelvetSweatshop' );
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.Decrypt(inFileName: string; outStream: TStream;
|
||||||
|
APassword: UnicodeString): string;
|
||||||
|
Var
|
||||||
|
inStream : TFileStream;
|
||||||
|
begin
|
||||||
|
if not FileExists(inFileName) then
|
||||||
|
Exit( inFileName + ' not found.' );
|
||||||
|
|
||||||
|
try
|
||||||
|
inStream := TFileStream.Create( inFileName, fmOpenRead );
|
||||||
|
|
||||||
|
inStream.Position := 0;
|
||||||
|
Result := Decrypt( inStream, outStream, APassword );
|
||||||
|
finally
|
||||||
|
inStream.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.Decrypt(inStream: TStream; outStream: TStream
|
||||||
|
): string;
|
||||||
|
begin
|
||||||
|
Result := Decrypt(inStream, outStream, 'VelvetSweatshop' );
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.Decrypt(inStream: TStream; outStream: TStream;
|
||||||
|
APassword: UnicodeString): string;
|
||||||
|
var
|
||||||
|
OLEStream: TMemoryStream;
|
||||||
|
OLEStorage: TOLEStorage;
|
||||||
|
OLEDocument: TOLEDocument;
|
||||||
|
|
||||||
|
AES_Cipher : TDCP_rijndael;
|
||||||
|
inData : TBytes;
|
||||||
|
outData : TBytes;
|
||||||
|
StreamSize : QWord;
|
||||||
|
KeySizeByte: Integer;
|
||||||
|
|
||||||
|
Err : string;
|
||||||
|
begin
|
||||||
|
if (not Assigned(inStream)) or (not Assigned(outStream)) then
|
||||||
|
Exit( 'streams must be assigned' );
|
||||||
|
|
||||||
|
Err := InitEncryptionInfo(inStream);
|
||||||
|
if Err <> '' then
|
||||||
|
Exit( 'Error when initializing Encryption Info'#10#13 + Err );
|
||||||
|
|
||||||
|
if not CheckPasswordInternal(APassword) then
|
||||||
|
Exit( 'Wrong password' );
|
||||||
|
|
||||||
|
// read the encoded stream into memory
|
||||||
|
OLEStream := TMemoryStream.Create;
|
||||||
|
try
|
||||||
|
OLEStorage := TOLEStorage.Create;
|
||||||
|
try
|
||||||
|
OLEDocument.Stream := OLEStream;
|
||||||
|
inStream.Position := 0;
|
||||||
|
OLEStorage.ReadOLEStream(inStream, OLEDocument, 'EncryptedPackage');
|
||||||
|
if OLEDocument.Stream.Size = 0 then
|
||||||
|
raise Exception.Create('EncryptedPackage stream not found.');
|
||||||
|
|
||||||
|
// Start decryption
|
||||||
|
OLEStream.Position:=0;
|
||||||
|
outStream.Position:=0;
|
||||||
|
|
||||||
|
StreamSize := OLEStream.ReadQWord;
|
||||||
|
|
||||||
|
KeySizeByte := FEncInfo.Header.KeySize div 8;
|
||||||
|
SetLength(inData, KeySizeByte);
|
||||||
|
SetLength(outData, KeySizeByte);
|
||||||
|
|
||||||
|
AES_Cipher := TDCP_rijndael.Create(nil);
|
||||||
|
AES_Cipher.Init( FEncryptionKey[0], FEncInfo.Header.KeySize, nil );
|
||||||
|
|
||||||
|
While StreamSize > 0 do
|
||||||
|
begin
|
||||||
|
OLEStream.ReadBuffer(inData[0], KeySizeByte);
|
||||||
|
AES_Cipher.DecryptECB(inData[0], outData[0]);
|
||||||
|
|
||||||
|
if StreamSize < KeySizeByte then
|
||||||
|
outStream.WriteBuffer(outData[0], StreamSize) // Last block less then key size
|
||||||
|
else
|
||||||
|
outStream.WriteBuffer(outData[0], KeySizeByte);
|
||||||
|
|
||||||
|
if StreamSize < KeySizeByte then
|
||||||
|
StreamSize := 0
|
||||||
|
else
|
||||||
|
Dec(StreamSize, KeySizeByte);
|
||||||
|
end;
|
||||||
|
|
||||||
|
AES_Cipher.Free;
|
||||||
|
|
||||||
|
/////
|
||||||
|
except
|
||||||
|
Err := 'EncryptedPackage not found';
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
if Assigned(OLEStorage) then
|
||||||
|
OLEStorage.Free;
|
||||||
|
|
||||||
|
OLEStream.Free;
|
||||||
|
end;
|
||||||
|
Exit( Err );
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.isEncryptedAndSupported(AFileName: string
|
||||||
|
): Boolean;
|
||||||
|
var
|
||||||
|
AStream : TStream;
|
||||||
|
begin
|
||||||
|
if not FileExists(AFileName) then
|
||||||
|
Exit( False );
|
||||||
|
|
||||||
|
try
|
||||||
|
AStream := TFileStream.Create( AFileName, fmOpenRead );
|
||||||
|
|
||||||
|
AStream.Position := 0;
|
||||||
|
//FStream.CopyFrom(AStream, AStream.Size);
|
||||||
|
|
||||||
|
Result := isEncryptedAndSupported( AStream );
|
||||||
|
finally
|
||||||
|
AStream.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.isEncryptedAndSupported(AStream: TStream
|
||||||
|
): Boolean;
|
||||||
|
begin
|
||||||
|
if not Assigned(AStream) then
|
||||||
|
Exit( False );
|
||||||
|
|
||||||
|
if InitEncryptionInfo(AStream) <> '' then
|
||||||
|
Exit( False );
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.CheckPassword(AFileName: string;
|
||||||
|
APassword: UnicodeString): Boolean;
|
||||||
|
var
|
||||||
|
AStream : TStream;
|
||||||
|
begin
|
||||||
|
if not FileExists(AFileName) then
|
||||||
|
Exit( False );
|
||||||
|
|
||||||
|
try
|
||||||
|
AStream := TFileStream.Create( AFileName, fmOpenRead );
|
||||||
|
|
||||||
|
AStream.Position := 0;
|
||||||
|
|
||||||
|
Result := CheckPassword( AStream, APassword );
|
||||||
|
finally
|
||||||
|
AStream.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TExcelFileDecryptor.CheckPassword(AStream: TStream;
|
||||||
|
APassword: UnicodeString): Boolean;
|
||||||
|
begin
|
||||||
|
if not Assigned(AStream) then
|
||||||
|
Exit( False );
|
||||||
|
|
||||||
|
AStream.Position := 0;
|
||||||
|
if InitEncryptionInfo(AStream) <> '' then
|
||||||
|
Exit( False );
|
||||||
|
|
||||||
|
Result := CheckPasswordInternal(APassword);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
68
components/fpspreadsheet/source/crypto/xlsxooxml_crypto.pas
Normal file
68
components/fpspreadsheet/source/crypto/xlsxooxml_crypto.pas
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
unit xlsxooxml_crypto;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes,
|
||||||
|
fpstypes, xlsxooxml, xlsxdecrypter;
|
||||||
|
|
||||||
|
type
|
||||||
|
TsSpreadOOXMLReaderCrypto = class(TsSpreadOOXMLReader)
|
||||||
|
public
|
||||||
|
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||||
|
AParams: TsStreamParams = []); override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
sfidOOXML_Crypto: TsSpreadFormatID;
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
fpsReaderWriter;
|
||||||
|
|
||||||
|
procedure TsSpreadOOXMLReaderCrypto.ReadFromStream(AStream: TStream;
|
||||||
|
APassword: String = ''; AParams: TsStreamParams = []);
|
||||||
|
var
|
||||||
|
ExcelDecrypt : TExcelFileDecryptor;
|
||||||
|
DecryptedStream: TStream;
|
||||||
|
begin
|
||||||
|
ExcelDecrypt := TExcelFileDecryptor.Create;
|
||||||
|
try
|
||||||
|
AStream.Position := 0;
|
||||||
|
if ExcelDecrypt.isEncryptedAndSupported(AStream) then
|
||||||
|
begin
|
||||||
|
DecryptedStream := TMemoryStream.Create;
|
||||||
|
try
|
||||||
|
ExcelDecrypt.Decrypt(AStream, DecryptedStream, APassword);
|
||||||
|
// Discard encrypted stream and load decrypted one.
|
||||||
|
AStream.Free;
|
||||||
|
AStream := TMemoryStream.Create;
|
||||||
|
DecryptedStream.Position := 0;
|
||||||
|
AStream.CopyFrom(DecryptedStream, DecryptedStream.Size);
|
||||||
|
AStream.Position := 0;
|
||||||
|
finally
|
||||||
|
DecryptedStream.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
ExcelDecrypt.Free;
|
||||||
|
AStream.Position := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
initialization
|
||||||
|
|
||||||
|
// Registers this reader/writer for fpSpreadsheet
|
||||||
|
sfidOOXML_Crypto := RegisterSpreadFormat(sfUser,
|
||||||
|
TsSpreadOOXMLReaderCrypto, nil,
|
||||||
|
STR_FILEFORMAT_EXCEL_XLSX, 'OOXML', [STR_OOXML_EXCEL_EXTENSION, '.xlsm']
|
||||||
|
);
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
||||||
|
end.
|
45
components/fpspreadsheet/source/laz_fpspreadsheet_crypto.lpk
Normal file
45
components/fpspreadsheet/source/laz_fpspreadsheet_crypto.lpk
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CONFIG>
|
||||||
|
<Package Version="4">
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<Name Value="laz_fpspreadsheet_crypto"/>
|
||||||
|
<Type Value="RunTimeOnly"/>
|
||||||
|
<CompilerOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<SearchPaths>
|
||||||
|
<OtherUnitFiles Value="crypto"/>
|
||||||
|
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)\"/>
|
||||||
|
</SearchPaths>
|
||||||
|
</CompilerOptions>
|
||||||
|
<Description Value="Encryption / decryption support for FPSpreadsheet"/>
|
||||||
|
<Version Major="1" Minor="9"/>
|
||||||
|
<Files Count="2">
|
||||||
|
<Item1>
|
||||||
|
<Filename Value="crypto\xlsxdecrypter.pas"/>
|
||||||
|
<UnitName Value="xlsxdecrypter"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<Filename Value="crypto\xlsxooxml_crypto.pas"/>
|
||||||
|
<UnitName Value="xlsxooxml_crypto"/>
|
||||||
|
</Item2>
|
||||||
|
</Files>
|
||||||
|
<RequiredPkgs Count="3">
|
||||||
|
<Item1>
|
||||||
|
<PackageName Value="dcpcrypt"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<PackageName Value="laz_fpspreadsheet"/>
|
||||||
|
</Item2>
|
||||||
|
<Item3>
|
||||||
|
<PackageName Value="FCL"/>
|
||||||
|
</Item3>
|
||||||
|
</RequiredPkgs>
|
||||||
|
<UsageOptions>
|
||||||
|
<UnitPath Value="$(PkgOutDir)"/>
|
||||||
|
</UsageOptions>
|
||||||
|
<PublishOptions>
|
||||||
|
<Version Value="2"/>
|
||||||
|
</PublishOptions>
|
||||||
|
</Package>
|
||||||
|
</CONFIG>
|
Reference in New Issue
Block a user