CryptProtectDataで暗号化するときに第6パラメータ(dwFlags)にCRYPTPROTECT_LOCAL_MACHINEを含めると同一PC上の全てのユーザで復号化できてしまいますが、含めなければ同一PC上の同一のユーザでのみ復号化できます。また第3パラメータ(pOptionalEntropy)でエントロピを指定して暗号化しておけばCryptUnprotectDataで同じエントロピを指定しないと復号化できないようにすることができます。なおWindows 2000では第2パラメータのszDataDescrを省略する(NULLにする)ことができないので注意が必要です。
まず使用するCryptAPIと構造体を定義します。
uses
Windows;
type
_CRYPTOAPI_BLOB = record
cbData: DWORD;
pbData: PBYTE;
end;
{$EXTERNALSYM _CRYPTOAPI_BLOB}
DATA_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM DATA_BLOB}
PDATA_BLOB = ^DATA_BLOB;
{$EXTERNALSYM PDATA_BLOB}
PCRYPTPROTECT_PROMPTSTRUCT = ^CRYPTPROTECT_PROMPTSTRUCT;
{$EXTERNALSYM PCRYPTPROTECT_PROMPTSTRUCT}
_CRYPTPROTECT_PROMPTSTRUCT = record
cbSize: DWORD;
dwPromptFlags: DWORD;
hwndApp: HWND;
szPrompt: LPCWSTR;
end;
{$EXTERNALSYM _CRYPTPROTECT_PROMPTSTRUCT}
CRYPTPROTECT_PROMPTSTRUCT = _CRYPTPROTECT_PROMPTSTRUCT;
{$EXTERNALSYM CRYPTPROTECT_PROMPTSTRUCT}
const
CRYPTPROTECT_LOCAL_MACHINE = $4;
{$EXTERNALSYM CRYPTPROTECT_LOCAL_MACHINE}
function CryptProtectData(pDataIn: PDATA_BLOB; szDataDescr: PWideChar;
pOptionalEntropy: PDATA_BLOB; pvReserved: Pointer;
pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT;
dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
external 'crypt32.dll' name 'CryptProtectData';
{$EXTERNALSYM CryptProtectData}
function CryptUnprotectData(pDataIn: PDATA_BLOB; var ppszDataDescr: PWideChar;
pOptionalEntropy: PDATA_BLOB; pvReserved: Pointer;
pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT;
dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
external 'crypt32.dll' name 'CryptUnprotectData';
{$EXTERNALSYM CryptUnprotectData}
CryptProtectData/CryptUnprotectDataで扱うデータは基本的にバイナリなので、まずはTStreamを暗号化してみます。uses
Windows, Classes, SysUtils;
procedure ProtectStream(Source: TStream; Dest: TStream;
{$IFDEF Unicode}
const Description: String;
{$ELSE}
const Description: WideString;
{$ENDIF}
Entropy: TStream; LocalMachine: Boolean);
var
DataIn: DATA_BLOB;
OptionalEntropy: DATA_BLOB;
POptionalEntropy: PDATA_BLOB;
DataOut: DATA_BLOB;
Flags: DWORD;
MemorySource: TMemoryStream;
MemoryEntropy: TMemoryStream;
begin
MemorySource := nil;
MemoryEntropy := nil;
try
MemorySource := TMemoryStream.Create;
MemoryEntropy := TMemoryStream.Create;
{ DataIn }
with MemorySource do
begin
Source.Position := 0;
LoadFromStream(Source);
DataIn.cbData := Size;
DataIn.pbData := Memory;
end;
{ OptionalEntropy }
if Entropy = nil then
begin
POptionalEntropy := nil;
end
else
begin
with MemoryEntropy do
begin
Entropy.Position := 0;
LoadFromStream(Entropy);
OptionalEntropy.cbData := Size;
OptionalEntropy.pbData := Memory;
end;
POptionalEntropy := @OptionalEntropy;
end;
{ Flags }
Flags := 0;
if LocalMachine = True then
begin
Flags := CRYPTPROTECT_LOCAL_MACHINE;
end;
{ DataOut }
FillChar(DataOut,SizeOf(DataOut),0);
{ Protect data }
if CryptProtectData(@DataIn,PWideChar(Description),POptionalEntropy,nil,
nil,Flags,@DataOut) = False then
begin
RaiseLastOSError;
end;
{ Result }
Dest.Write(DataOut.pbData^,DataOut.cbData);
{ Free allocated memory }
LocalFree(HLOCAL(DataOut.pbData));
finally
MemorySource.Free;
MemoryEntropy.Free;
end;
end;
対象データと(指定されていれば)エントロピをTMemoryStreamに格納してそのMemory/SizeプロパティをDATA_BLOB構造体(=CRYPT_INTEGER_BLOB構造体)のpbData/cbDataにセットし、CryptProtectDataを呼び出します。DataOutには暗号化されたデータが格納されていますので、これを取り出し、最後に忘れずにLocalFreeでDataOutの内容を解放します。復号化も同様に
function UnprotectStream(Source: TStream; Dest: TStream;
{$IFDEF Unicode}
var Description: String;
{$ELSE}
var Description: WideString;
{$ENDIF}
Entropy: TStream): Boolean;
var
DataIn: DATA_BLOB;
OptionalEntropy: DATA_BLOB;
POptionalEntropy: PDATA_BLOB;
DataOut: DATA_BLOB;
PDescription: PWideChar;
MemorySource: TMemoryStream;
MemoryEntropy: TMemoryStream;
begin
MemorySource := nil;
MemoryEntropy := nil;
try
MemorySource := TMemoryStream.Create;
MemoryEntropy := TMemoryStream.Create;
{ DataIn }
with MemorySource do
begin
Source.Position := 0;
LoadFromStream(Source);
DataIn.cbData := Size;
DataIn.pbData := Memory;
end;
{ OptionalEntropy }
if Entropy = nil then
begin
POptionalEntropy := nil;
end
else
begin
with MemoryEntropy do
begin
Entropy.Position := 0;
LoadFromStream(Entropy);
OptionalEntropy.cbData := Size;
OptionalEntropy.pbData := Memory;
end;
POptionalEntropy := @OptionalEntropy;
end;
{ DataOut }
FillChar(DataOut,SizeOf(DataOut),0);
{ Unprotect data }
if CryptUnprotectData(@DataIn,PDescription,POptionalEntropy,nil,
nil,0,@DataOut) = False then
begin
Result := False;
Exit;
end;
{ Result }
Dest.Write(DataOut.pbData^,DataOut.cbData);
{ Description }
Description := PDescription;
{ Free allocated memory }
LocalFree(HLOCAL(DataOut.pbData));
LocalFree(HLOCAL(PDescription));
Result := True;
finally
MemorySource.Free;
MemoryEntropy.Free;
end;
end;
対象データと(指定されていれば)エントロピをTMemoryStreamに格納してそのMemory/SizeプロパティをDATA_BLOB構造体のpbData/cbDataにセットし、CryptUnprotectDataを呼び出します。DataOutとPDescriptionには復号化されたデータと暗号化のときに指定したDescriptionが格納されていますので、これらを取り出し、最後に忘れずにLocalFreeでDataOutとPDescriptionの内容を解放します。もし復号化に失敗した場合は戻値がFalseになります。長くなったので続きます。
元ねたはNyaRuRuさんのローカルストレージに保存するデータの暗号化 ― Windows の場合 - NyaRuRuの日記とEternalWindowsさんのDPAPIによる暗号化。
0 件のコメント:
コメントを投稿