Я хотел бы предложить повернуть проблему на голову. Ваша учетная запись Windows уже защищена паролем. API Win32 предоставляет механизм, в котором вы можете зашифровать данные Windows с помощью пароля Windows.
Это значит, что эти данные надежно защищены как ваш пароль для Windows; и вам не нужно запоминать второй пароль.
Функция Windows CredWrite
и CredRead
позволяют хранить и сохранять учетные данные; из которых я просто посчастливилось иметь удобную функцию обертки уже для хранения учетных данных:
function CredWriteGenericCredentials(const Target, Username, Password: WideString): Boolean;
var
PersistType: DWORD;
Credentials: CREDENTIALW;
hr: DWORD;
s: string;
begin
if not CredGetMaxPersistType(CRED_TYPE_GENERIC, {var}PersistType) then
begin
Result := False;
Exit;
end;
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 := PersistType; //CRED_PERSIST_ENTERPRISE; //local machine and roaming
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);
if not Result then
begin
hr := GetLastError;
case hr of
CredUI.ERROR_NO_SUCH_LOGON_SESSION: s := 'The logon session does not exist or there is no credential set associated with this logon session. Network logon sessions do not have an associated credential set. (ERROR_NO_SUCH_LOGON_SESSION)';
CredUI.ERROR_INVALID_PARAMETER: s := 'Certain fields cannot be changed in an existing credential. This error is returned if a field does not match the value in a protected field of the existing credential. (ERROR_INVALID_PARAMETER)';
CredUI.ERROR_INVALID_FLAGS: s := 'A value that is not valid was specified for the Flags parameter. (ERROR_INVALID_FLAGS)';
ERROR_BAD_USERNAME: s := 'The UserName member of the passed in Credential structure is not valid. For a description of valid user name syntax, see the definition of that member. (ERROR_BAD_USERNAME)';
ERROR_NOT_FOUND: s := 'CRED_PRESERVE_CREDENTIAL_BLOB was specified and there is no existing credential by the same TargetName and Type. (ERROR_NOT_FOUND)';
// SCARD_E_NO_READERS_AVAILABLE: raise Exception.Create('The CRED_TYPE_CERTIFICATE credential being written requires the smart card reader to be available. (SCARD_E_NO_READERS_AVAILABLE)');
// SCARD_E_NO_SMARTCARD: raise Exception.Create('A CRED_TYPE_CERTIFICATE credential being written requires the smart card to be inserted. (SCARD_E_NO_SMARTCARD)');
// SCARD_W_REMOVED_CARD: raise Exception.Create('A CRED_TYPE_CERTIFICATE credential being written requires the smart card to be inserted. (SCARD_W_REMOVED_CARD)');
// SCARD_W_WRONG_CHV: raise Exception.Create('The wrong PIN was supplied for the CRED_TYPE_CERTIFICATE credential being written. (SCARD_W_WRONG_CHV)');
else
s := SysErrorMessage(hr)+' (0x'+IntToHex(hr, 8)+')';
end;
OutputDebugString(PChar(s));
end;
end;
и функция-обертки для чтения учетных данных
function CredReadGenericCredentials(const Target: WideString; var Username, Password: WideString): Boolean;
var
Credential: PCREDENTIALW;
begin
Credential := nil;
if CredReadW(Target, CRED_TYPE_GENERIC, 0, Credential) then
begin
try
username := Credential.UserName;
password := WideCharToWideString(PWideChar(Credential.CredentialBlob), Credential.CredentialBlobSize); //By convention blobs that contain strings do not have a trailing NULL.
finally
CredFree(Credential);
end;
Result := True;
end
else
Result := False;
end;
Следует отметить, что CredRead
и CredWrite
сами являются функциями, развернитесь и используйте CryptProtectData
и CryptUnprotectData
.
Эти функции позволяют взять некоторую произвольную блоб и зашифровать его с помощью пароля учетной записи пользователя , а затем передать вам обратно зашифрованного блоб.Затем вы можете сохранить это blob где угодно (например, реестр или файл).
Позже вы можете дешифровать blob и могут быть дешифрованы только пользователем, который изначально зашифровал его.
Это позволяет вам мечтать о том, чтобы заставить вас иметь дело с другим паролем, но использует Windows для его защиты.
"MyPassword04" --> CryptProtectData() --> "TXlQYXNzd29yZDA0"
Вы можете хранить свой зашифрованный пароль везде, где захотите. Затем позже:
"TXlQYXNzd29yZDA0" --> CryptUnprotectData() --> "MyPassword04"
Предложение, которое я делаю, - это возможность отказаться от паролей; используя безопасность вашей собственной учетной записи.
Просто предложение; вы можете рассмотреть и отвергнуть это.
Update
Дополнительные вспомогательные функции.
Преобразование PWideChar
в WideString
(если есть встроенный (Delphi 5) функция для этого, я никогда не нашел):
function WideCharToWideString(Source: PWideChar; SourceLen: Integer): WideString;
begin
if (SourceLen <= 0) then
begin
Result := '';
Exit;
end;
SetLength(Result, SourceLen div 2);
Move(Source^, Result[1], SourceLen);
end;
Есть разные «прицелы», что you'r разрешено хранить кредитные лимиты в:
CRED_PERSIST_NONE
: Никакие учетные данные не могут быть сохранены. Это значение будет возвращено, если тип учетных данных не поддерживается или был отключен политикой.
CRED_PERSIST_SESSION
: Только учетные данные, относящиеся к сеансу, могут быть сохранены.
CRED_PERSIST_LOCAL_MACHINE
: Учетные данные для конкретного сеанса и компьютера могут быть сохранены. Windows XP: Эти учетные данные не могут быть сохранены для сеансов, в которых профиль не загружен.
CRED_PERSIST_ENTERPRISE
: Любые учетные данные могут быть сохранены. Windows XP: Эти учетные данные не могут быть сохранены для сеансов, в которых профиль не загружен.
Эта функция возвращает самый высокий поддерживаемый тип сохраняемости для заданного типа учетных данных (например, «общие» credentails). Это необходимо, когда вы звоните CredWrite
, что вы не пытаетесь упорствовать его в месте, которое не поддерживается (то есть в области, когда нет домена):
type
TCredGetSessionTypes = function(MaximumPersistCount: DWORD; MaximumPersist: LPDWORD): BOOL; stdcall;
function CredGetMaxPersistType(CredType: DWORD; var MaxCredPersistType: DWORD): Boolean;
const
CRED_TYPE_MAXIMUM = 5;
var
_CredGetSessionTypes: TCredGetSessionTypes;
MaximumPersist: array[0..CRED_TYPE_MAXIMUM-1] of DWORD;
begin
_CredGetSessionTypes := GetProcedureAddress(advapi32, 'CredGetSessionTypes');
if Assigned(_CredGetSessionTypes) then
begin
Result := _CredGetSessionTypes(CRED_TYPE_MAXIMUM, PDWORD(@MaximumPersist[0]));
if Result then
MaxCredPersistType := MaximumPersist[CredType]
else
MaxCredPersistType := 0;
end
else
begin
SetLastError(ERROR_INVALID_FUNCTION);
Result := False;
MaxCredPersistType := 0;
end;
end;
Примечание: Любой код выпущен в общественное достояние. Никакой атрибуции не требуется.
Это хорошая идея, но я должен спрашивать об этом каждый раз, когда пользователь что-то изменяет в хранящихся данных. Думаю, это утомительно! – djiga4me
hm Я действительно не понимаю ваш случай использования. * Пользователи * изменяют свой пароль ... вы хотите защитить файл паролей. Итак, где бы вы хотели получить главный пароль? – Nicolas78
Я использую KeePass, и это схема, которую она использует. Я запускаю программу, открываю файл с кодировкой базы данных, запрашивает мой основной пароль, использует его для дешифрования файла, а затем запоминает этот пароль, пока база данных не будет закрыта. Я могу редактировать базу данных по своему усмотрению, даже изменить основной пароль. Файл базы данных повторно зашифровывается с текущим паролем всякий раз, когда он сохраняется в файл. Пароль шифрует сам файл базы данных, а не сохраненные пароли пользователей. Нет необходимости шифровать их отдельно, когда весь файл зашифрован. –