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 件のコメント:
コメントを投稿