問題が単にユーザーが毎回パスワードを入力する必要がないようにすることである場合は、Windowsにすでにパスワードストレージシステムがあることを知っておく必要があります。
[コントロールパネル] ->[資格情報マネージャー]に移動した場合。そこから、 Windowsクレデンシャル->汎用クレデンシャルを探しています。
そこから、リモートデスクトップのパスワードなどが保存されている場所と同じ場所であることがわかります。
この機能を公開するAPIは、、、CredRead
およびCredWrite
ですCredDelete
。
これらを3つの機能にまとめました。
function CredReadGenericCredentials(const Target: UnicodeString; var Username, Password: UnicodeString): Boolean;
function CredWriteGenericCredentials(const Target, Username, Password: UnicodeString): Boolean;
function CredDeleteGenericCredentials(const Target: UnicodeString): Boolean;
ターゲットは、credentailsを識別することです。私は通常、アプリケーション名を使用します。
String target = ExtractFilename(ParamStr(0)); //e.g. 'Contoso.exe'
だからそれは単純です:
CredWriteGenericCredentials(ExtractFilename(ParamStr(0)), username, password);
その後、資格情報マネージャーでそれらを確認できます。
それらを読み返したいとき:
CredReadGenericCredentials(ExtractFilename(ParamStr(0)), {var}username, {var}password);
あなたがしなければならないUI作業の余分な部分があります:
- 保存されたクレデンシャルがないことを検出し、ユーザーにクレデンシャルの入力を求める
- 保存したユーザー名/パスワードが機能しなかったことを検出し、新しい/正しいクレデンシャルの入力を求め、接続してみて、新しい正しいクレデンシャルを保存します
保存された資格情報の読み取り:
function CredReadGenericCredentials(const Target: UnicodeString; var Username, Password: UnicodeString): Boolean;
var
credential: PCREDENTIALW;
le: DWORD;
s: string;
begin
Result := False;
credential := nil;
if not CredReadW(Target, CRED_TYPE_GENERIC, 0, {var}credential) then
begin
le := GetLastError;
s := 'Could not get "'+Target+'" generic credentials: '+SysErrorMessage(le)+' '+IntToStr(le);
OutputDebugString(PChar(s));
Exit;
end;
try
username := Credential.UserName;
password := WideCharToWideString(PWideChar(Credential.CredentialBlob), Credential.CredentialBlobSize div 2); //By convention blobs that contain strings do not have a trailing NULL.
finally
CredFree(Credential);
end;
Result := True;
end;
保存されたクレデンシャルの書き込み:
function CredWriteGenericCredentials(const Target, Username, Password: UnicodeString): Boolean;
var
persistType: DWORD;
Credentials: CREDENTIALW;
le: DWORD;
s: string;
begin
ZeroMemory(@Credentials, SizeOf(Credentials));
Credentials.TargetName := PWideChar(Target); //cannot be longer than CRED_MAX_GENERIC_TARGET_NAME_LENGTH (32767) characters. Recommended format "Company_Target"
Credentials.Type_ := CRED_TYPE_GENERIC;
Credentials.UserName := PWideChar(Username);
Credentials.Persist := CRED_PERSIST_LOCAL_MACHINE;
Credentials.CredentialBlob := PByte(Password);
Credentials.CredentialBlobSize := 2*(Length(Password)); //By convention no trailing null. Cannot be longer than CRED_MAX_CREDENTIAL_BLOB_SIZE (512) bytes
Credentials.UserName := PWideChar(Username);
Result := CredWriteW(Credentials, 0);
end;
end;
そして、削除します:
function CredDeleteGenericCredentials(const Target: UnicodeString): Boolean;
begin
Result := CredDelete(Target, CRED_TYPE_GENERIC);
end;
CredReadは、CryptProtectDataのラッパーです。
CredWrite/CredReadは内部的にを使用することに注意してくださいCryptProtectData
。
- また、クレデンシャルをどこかに保存することも選択します
- また、ユーザーが保存されたクレデンシャルを表示、管理、さらには手動で入力および変更するためのUIを提供します
自分自身を使用することによる違いCryptProtectData
は、ブロブしか与えられないということです。それをどこかに保存し、後で取得するのはあなた次第です。
パスワードを保存するときのラッパーは次のとおりCryptProtectData
です。CryptUnprotectData
function EncryptString(const Plaintext: UnicodeString; const AdditionalEntropy: UnicodeString): TBytes;
function DecryptString(const Blob: TBytes; const AdditionalEntropy: UnicodeString): UnicodeString;
これは使いやすいです:
procedure TForm1.TestStringEncryption;
var
encryptedBlob: TBytes;
plainText: UnicodeString;
const
Salt = 'Salt doesn''t have to be secret; just different from the next application';
begin
encryptedBlob := EncryptString('correct battery horse staple', Salt);
plainText := DecryptString(encryptedBlob, salt);
if plainText <> 'correct battery horse staple' then
raise Exception.Create('String encryption self-test failed');
end;
実際の内臓は次のとおりです。
type
DATA_BLOB = record
cbData: DWORD;
pbData: PByte;
end;
PDATA_BLOB = ^DATA_BLOB;
const
CRYPTPROTECT_UI_FORBIDDEN = $1;
function CryptProtectData(const DataIn: DATA_BLOB; szDataDescr: PWideChar; OptionalEntropy: PDATA_BLOB; Reserved: Pointer; PromptStruct: Pointer{PCRYPTPROTECT_PROMPTSTRUCT}; dwFlags: DWORD; var DataOut: DATA_BLOB): BOOL; stdcall; external 'Crypt32.dll' name 'CryptProtectData';
function CryptUnprotectData(const DataIn: DATA_BLOB; szDataDescr: PPWideChar; OptionalEntropy: PDATA_BLOB; Reserved: Pointer; PromptStruct: Pointer{PCRYPTPROTECT_PROMPTSTRUCT}; dwFlags: DWORD; var DataOut: DATA_BLOB): Bool; stdcall; external 'Crypt32.dll' name 'CryptUnprotectData';
function EncryptString(const Plaintext: UnicodeString; const AdditionalEntropy: UnicodeString): TBytes;
var
blobIn: DATA_BLOB;
blobOut: DATA_BLOB;
entropyBlob: DATA_BLOB;
pEntropy: Pointer;
bRes: Boolean;
begin
blobIn.pbData := Pointer(PlainText);
blobIn.cbData := Length(PlainText)*SizeOf(WideChar);
if AdditionalEntropy <> '' then
begin
entropyBlob.pbData := Pointer(AdditionalEntropy);
entropyBlob.cbData := Length(AdditionalEntropy)*SizeOf(WideChar);
pEntropy := @entropyBlob;
end
else
pEntropy := nil;
bRes := CryptProtectData(
blobIn,
nil, //data description (PWideChar)
pentropy, //optional entropy (PDATA_BLOB)
nil, //reserved
nil, //prompt struct
CRYPTPROTECT_UI_FORBIDDEN, //flags
{var}blobOut);
if not bRes then
RaiseLastOSError;
//Move output blob into resulting TBytes
SetLength(Result, blobOut.cbData);
Move(blobOut.pbData^, Result[0], blobOut.cbData);
// When you have finished using the DATA_BLOB structure, free its pbData member by calling the LocalFree function
LocalFree(HLOCAL(blobOut.pbData));
end;
そして復号化:
function DecryptString(const blob: TBytes; const AdditionalEntropy: UnicodeString): UnicodeString;
var
dataIn: DATA_BLOB;
entropyBlob: DATA_BLOB;
pentropy: PDATA_BLOB;
dataOut: DATA_BLOB;
bRes: BOOL;
begin
dataIn.pbData := Pointer(blob);
dataIn.cbData := Length(blob);
if AdditionalEntropy <> '' then
begin
entropyBlob.pbData := Pointer(AdditionalEntropy);
entropyBlob.cbData := Length(AdditionalEntropy)*SizeOf(WideChar);
pentropy := @entropyBlob;
end
else
pentropy := nil;
bRes := CryptUnprotectData(
DataIn,
nil, //data description (PWideChar)
pentropy, //optional entropy (PDATA_BLOB)
nil, //reserved
nil, //prompt struct
CRYPTPROTECT_UI_FORBIDDEN,
{var}dataOut);
if not bRes then
RaiseLastOSError;
SetLength(Result, dataOut.cbData div 2);
Move(dataOut.pbData^, Result[1], dataOut.cbData);
LocalFree(HLOCAL(DataOut.pbData));
end;