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;
|
||||
public
|
||||
constructor Create(AWorkbook: TsWorkbook); override;
|
||||
procedure ReadFromFile(AFileName: String; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromFile(AFileName: String; APassword: String = '';
|
||||
AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
||||
end;
|
||||
|
||||
@ -173,14 +175,14 @@ begin
|
||||
Unused(AStream);
|
||||
end;
|
||||
|
||||
procedure TsCSVReader.ReadFromFile(AFileName: String;
|
||||
procedure TsCSVReader.ReadFromFile(AFileName: String; APassword: String = '';
|
||||
AParams: TsStreamParams = []);
|
||||
begin
|
||||
FWorksheetName := ChangeFileExt(ExtractFileName(AFileName), '');
|
||||
inherited ReadFromFile(AFilename, AParams);
|
||||
inherited ReadFromFile(AFilename, APassword, AParams);
|
||||
end;
|
||||
|
||||
procedure TsCSVReader.ReadFromStream(AStream: TStream;
|
||||
procedure TsCSVReader.ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []);
|
||||
var
|
||||
Parser: TCSVParser;
|
||||
@ -227,7 +229,7 @@ var
|
||||
begin
|
||||
Stream := TStringStream.Create(AStrings.Text);
|
||||
try
|
||||
ReadFromStream(Stream, AParams);
|
||||
ReadFromStream(Stream, '', AParams);
|
||||
finally
|
||||
Stream.Free;
|
||||
end;
|
||||
|
@ -57,7 +57,8 @@ type
|
||||
public
|
||||
constructor Create(AWorkbook: TsWorkbook); 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;
|
||||
end;
|
||||
|
||||
@ -1045,11 +1046,12 @@ begin
|
||||
SetLength(FCurrRichTextParams, 0);
|
||||
end;
|
||||
|
||||
procedure TsHTMLReader.ReadFromStream(AStream: TStream;
|
||||
procedure TsHTMLReader.ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []);
|
||||
var
|
||||
list: TStringList;
|
||||
begin
|
||||
Unused(APassword);
|
||||
list := TStringList.Create;
|
||||
try
|
||||
list.LoadFromStream(AStream);
|
||||
|
@ -165,7 +165,8 @@ type
|
||||
destructor Destroy; override;
|
||||
|
||||
{ General reading methods }
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream;
|
||||
APassword: String = ''; AParams: TsStreamParams = []); override;
|
||||
end;
|
||||
|
||||
{ TsSpreadOpenDocWriter }
|
||||
@ -2422,7 +2423,7 @@ begin
|
||||
end;
|
||||
|
||||
procedure TsSpreadOpenDocReader.ReadFromStream(AStream: TStream;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
Doc : TXMLDocument;
|
||||
BodyNode, SpreadSheetNode, TableNode: TDOMNode;
|
||||
@ -2445,7 +2446,7 @@ var
|
||||
end;
|
||||
|
||||
begin
|
||||
Unused(AParams);
|
||||
Unused(APassword, AParams);
|
||||
|
||||
Doc := nil;
|
||||
try
|
||||
|
@ -756,15 +756,15 @@ type
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure ReadFromFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
||||
AParams: TsStreamParams = []); overload;
|
||||
APassword: String = ''; AParams: TsStreamParams = []); overload;
|
||||
procedure ReadFromFile(AFileName: string; AFormat: TsSpreadsheetFormat;
|
||||
AParams: TsStreamParams = []); overload;
|
||||
procedure ReadFromFile(AFileName: string;
|
||||
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
||||
AParams: TsStreamParams = []); overload;
|
||||
procedure ReadFromFileIgnoringExtension(AFileName: string;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
procedure ReadFromStream(AStream: TStream; AFormatID: TsSpreadFormatID;
|
||||
AParams: TsStreamParams = []); overload;
|
||||
APassword: String = ''; AParams: TsStreamParams = []); overload;
|
||||
procedure ReadFromStream(AStream: TStream; AFormat: TsSpreadsheetFormat;
|
||||
AParams: TsStreamParams = []); overload;
|
||||
|
||||
@ -8199,7 +8199,7 @@ procedure TsWorkbook.ReadFromFile(AFileName: string;
|
||||
begin
|
||||
if AFormat = sfUser then
|
||||
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;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@ -8209,8 +8209,8 @@ end;
|
||||
@param AFileName Name of the file to be read
|
||||
@param AFormatID Identifier of the file format assumed
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbook.ReadFromFile(AFileName: string;
|
||||
AFormatID: TsSpreadFormatID; AParams: TsStreamParams = []);
|
||||
procedure TsWorkbook.ReadFromFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
AReader: TsBasicSpreadReader;
|
||||
ok: Boolean;
|
||||
@ -8219,7 +8219,7 @@ begin
|
||||
raise Exception.CreateFmt(rsFileNotFound, [AFileName]);
|
||||
|
||||
if AFormatID = sfIDUnknown then begin
|
||||
ReadFromFile(AFileName, AParams);
|
||||
ReadFromFile(AFileName, APassword, AParams);
|
||||
exit;
|
||||
end;
|
||||
|
||||
@ -8231,7 +8231,7 @@ begin
|
||||
FReadWriteFlag := rwfRead;
|
||||
inc(FLockCount); // This locks various notifications from being sent
|
||||
try
|
||||
AReader.ReadFromFile(AFileName, AParams);
|
||||
AReader.ReadFromFile(AFileName, APassword, AParams);
|
||||
ok := true;
|
||||
UpdateCaches;
|
||||
if (boAutoCalc in Options) then
|
||||
@ -8253,7 +8253,8 @@ end;
|
||||
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.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbook.ReadFromFile(AFileName: string; AParams: TsStreamParams = []);
|
||||
procedure TsWorkbook.ReadFromFile(AFileName: string; APassword: String = '';
|
||||
AParams: TsStreamParams = []);
|
||||
var
|
||||
formatID: TsSpreadFormatID;
|
||||
canLoad, success: Boolean;
|
||||
@ -8293,7 +8294,7 @@ begin
|
||||
success := false;
|
||||
for i:=0 to High(fileformats) do begin
|
||||
try
|
||||
ReadFromFile(AFileName, fileformats[i], AParams);
|
||||
ReadFromFile(AFileName, fileformats[i], APassword, AParams);
|
||||
success := true;
|
||||
break; // Exit the loop if we reach this point successfully.
|
||||
except
|
||||
@ -8310,7 +8311,7 @@ end;
|
||||
Reads the document from a file, but ignores the extension.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbook.ReadFromFileIgnoringExtension(AFileName: string;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
formatID: TsSpreadFormatID;
|
||||
fileformats: TsSpreadFormatIDArray;
|
||||
@ -8319,7 +8320,7 @@ begin
|
||||
fileformats := GetSpreadFormats(faRead, [ord(sfOOXML), ord(sfOpenDocument), ord(sfExcel8)]);
|
||||
for formatID in fileformats do begin
|
||||
try
|
||||
ReadFromFile(AFileName, formatID, AParams);
|
||||
ReadFromFile(AFileName, formatID, APassword, AParams);
|
||||
success := true;
|
||||
break;
|
||||
except
|
||||
@ -8342,7 +8343,7 @@ procedure TsWorkbook.ReadFromStream(AStream: TStream;
|
||||
begin
|
||||
if AFormat = sfUser then
|
||||
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;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@ -8352,8 +8353,8 @@ end;
|
||||
@param AFormatID Identifier of the file format assumed.
|
||||
@param AParams Optional parameters to control stream access.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbook.ReadFromStream(AStream: TStream;
|
||||
AFormatID: TsSpreadFormatID; AParams: TsStreamParams = []);
|
||||
procedure TsWorkbook.ReadFromStream(AStream: TStream; AFormatID: TsSpreadFormatID;
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
AReader: TsBasicSpreadReader;
|
||||
ok: Boolean;
|
||||
@ -8366,7 +8367,7 @@ begin
|
||||
inc(FLockCount);
|
||||
try
|
||||
AStream.Position := 0;
|
||||
AReader.ReadFromStream(AStream, AParams);
|
||||
AReader.ReadFromStream(AStream, APassword, AParams);
|
||||
ok := true;
|
||||
UpdateCaches;
|
||||
if (boAutoCalc in Options) then
|
||||
|
@ -47,8 +47,10 @@ type
|
||||
TsBasicSpreadReader = class(TsBasicSpreadReaderWriter)
|
||||
public
|
||||
{ General writing methods }
|
||||
procedure ReadFromFile(AFileName: string; AParams: TsStreamParams = []); virtual; abstract;
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); virtual; abstract;
|
||||
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
||||
AParams: TsStreamParams = []); virtual; abstract;
|
||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []); virtual; abstract;
|
||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
|
||||
end;
|
||||
|
||||
@ -112,8 +114,10 @@ type
|
||||
destructor Destroy; override;
|
||||
|
||||
{ General writing methods }
|
||||
procedure ReadFromFile(AFileName: string; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromFile(AFileName: string; APassword: String = '';
|
||||
AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); override;
|
||||
|
||||
{@@ List of number formats found in the workbook. }
|
||||
@ -460,7 +464,7 @@ end;
|
||||
@see TsWorkbook
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsCustomSpreadReader.ReadFromFile(AFileName: string;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
stream, fs: TStream;
|
||||
begin
|
||||
@ -482,7 +486,7 @@ begin
|
||||
end;
|
||||
|
||||
try
|
||||
ReadFromStream(stream, AParams);
|
||||
ReadFromStream(stream, APassword, AParams);
|
||||
finally
|
||||
stream.Free;
|
||||
end;
|
||||
@ -501,11 +505,12 @@ end;
|
||||
@param AData Workbook which is filled by the data from the stream.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsCustomSpreadReader.ReadFromStream(AStream: TStream;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String; AParams: TsStreamParams = []);
|
||||
var
|
||||
AStringStream: TStringStream;
|
||||
AStrings: TStringList;
|
||||
begin
|
||||
Unused(APassword);
|
||||
AStringStream := TStringStream.Create('');
|
||||
AStrings := TStringList.Create;
|
||||
try
|
||||
|
@ -77,7 +77,8 @@ type
|
||||
public
|
||||
constructor Create(AWorkbook: TsWorkbook); override;
|
||||
{ General reading methods }
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []); override;
|
||||
end;
|
||||
|
||||
|
||||
@ -576,14 +577,14 @@ end;
|
||||
|
||||
|
||||
procedure TsSpreadBIFF2Reader.ReadFromStream(AStream: TStream;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
BIFF2EOF: Boolean;
|
||||
RecordType: Word;
|
||||
CurStreamPos: Int64;
|
||||
BOFFound: Boolean;
|
||||
begin
|
||||
Unused(AParams);
|
||||
Unused(APassword, AParams);
|
||||
BIFF2EOF := False;
|
||||
|
||||
{ In BIFF2 files there is only one worksheet, let's create it }
|
||||
|
@ -89,7 +89,8 @@ type
|
||||
procedure ReadXF(AStream: TStream);
|
||||
public
|
||||
{ General reading methods }
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []); override;
|
||||
end;
|
||||
|
||||
{ TsSpreadBIFF5Writer }
|
||||
@ -925,13 +926,13 @@ begin
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
OLEStream: TMemoryStream;
|
||||
OLEStorage: TOLEStorage;
|
||||
OLEDocument: TOLEDocument;
|
||||
begin
|
||||
Unused(AParams);
|
||||
Unused(APassword, AParams);
|
||||
|
||||
OLEStream := TMemoryStream.Create;
|
||||
try
|
||||
|
@ -126,7 +126,8 @@ type
|
||||
procedure ReadXF(const AStream: TStream);
|
||||
public
|
||||
destructor Destroy; override;
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream;
|
||||
APassword: String = ''; AParams: TsStreamParams = []); override;
|
||||
end;
|
||||
|
||||
{ TsSpreadBIFF8Writer }
|
||||
@ -958,13 +959,13 @@ begin
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF8Reader.ReadFromStream(AStream: TStream;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
OLEStream: TMemoryStream;
|
||||
OLEStorage: TOLEStorage;
|
||||
OLEDocument: TOLEDocument;
|
||||
begin
|
||||
Unused(AParams);
|
||||
Unused(APassword, AParams);
|
||||
OLEStream := TMemoryStream.Create;
|
||||
try
|
||||
// Only one stream is necessary for any number of worksheets
|
||||
|
@ -44,7 +44,7 @@ uses
|
||||
fpszipper,
|
||||
{$ENDIF}
|
||||
fpsTypes, fpSpreadsheet, fpsUtils, fpsReaderWriter, fpsNumFormat, fpsPalette,
|
||||
fpsxmlcommon, xlsCommon;
|
||||
fpsxmlcommon, xlsCommon; //, xlsxdecrypter;
|
||||
|
||||
type
|
||||
|
||||
@ -105,7 +105,8 @@ type
|
||||
public
|
||||
constructor Create(AWorkbook: TsWorkbook); override;
|
||||
destructor Destroy; override;
|
||||
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); override;
|
||||
procedure ReadFromStream(AStream: TStream; APassword: String = '';
|
||||
AParams: TsStreamParams = []); override;
|
||||
end;
|
||||
|
||||
{ TsSpreadOOXMLWriter }
|
||||
@ -2352,7 +2353,7 @@ begin
|
||||
end;
|
||||
|
||||
procedure TsSpreadOOXMLReader.ReadFromStream(AStream: TStream;
|
||||
AParams: TsStreamParams = []);
|
||||
APassword: String = ''; AParams: TsStreamParams = []);
|
||||
var
|
||||
Doc : TXMLDocument;
|
||||
RelsNode: TDOMNode;
|
||||
@ -2374,7 +2375,7 @@ var
|
||||
end;
|
||||
|
||||
begin
|
||||
Unused(AParams);
|
||||
Unused(APassword, AParams);
|
||||
Doc := nil;
|
||||
|
||||
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