What protection scheme for my passwords?
我正在用Delphi开发一个软件(供个人使用)。
但我有个问题,就是这样:
->有一个主密码可以访问其他密码文件。
->在存储这些密码时,我使用主密码作为密钥。我想,没关系。->但是如何保护主密码,并允许修改它呢????
如果我使用一个常量键(以二进制形式存储在代码中),它就可以被反汇编!
所以,我疯了,或者有一种方法可以做到这一点:保护主密码和派生密码。
(主密码(按用户选择)->加密用户数据时将其用作密钥(与其他密码和用户名相关)。
谢谢你的帮助。请原谅我的英语不好。
我建议把这个问题彻底解决。您的Windows帐户已被密码保护。win32 api提供了一种机制,您可以使用Windows密码让Windows加密数据。
这意味着你的数据和你的Windows密码一样安全;你不需要记住第二个密码。
windows函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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; |
以及用于读取凭证的包装函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; |
号
需要注意的是,
这些函数允许您获取一些任意的blob,并使用用户帐户的密码1对其进行加密,然后将加密的blob返回给您。然后,您可以将该blob存储在任何您喜欢的地方(例如注册表或文件)。
稍后,您可以对blob进行解密,并且只能由最初对其进行加密的用户进行解密。
这让你有了强迫你处理另一个密码的梦想,但是使用Windows来保护它。
1 | "MyPassword04" --> CryptProtectData() -->"TXlQYXNzd29yZDA0" |
你可以在任何你喜欢的地方存储你的加密密码。稍后:
1 | "TXlQYXNzd29yZDA0" --> CryptUnprotectData() -->"MyPassword04" |
。
我的建议是能够放弃密码;利用您自己帐户的安全性。
只是一个建议;你可以自由地考虑和拒绝它。
更新
其他助手函数。
将
1 2 3 4 5 6 7 8 9 10 11 | 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; |
您可以将不同的"范围"存储在以下位置:
CRED_PERSIST_NONE :不能存储凭证。如果凭证类型不受支持或已被策略禁用,则返回此值。CRED_PERSIST_SESSION :只能存储会话特定的凭证。CRED_PERSIST_LOCAL_MACHINE :可以存储特定于会话和特定于计算机的凭证。Windows XP:无法为未加载配置文件的会话存储此凭据。CRED_PERSIST_ENTERPRISE :可以存储任何凭证。Windows XP:无法为未加载配置文件的会话存储此凭据。
此函数返回给定凭证类型(例如"generic"credentives)支持的最高持久性类型。当您调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; |
。
Note: Any code is released into the public domain. No attribution required.
号
用主密码对密码文件进行编码。不要将密码存储在任何地方;只需在解密密码文件之前查询它。如果有人输入了错误的密码,密码文件将被置乱。
您可能对我们新推出的smartutils密码sdk感兴趣:http://sutils.com/index.php/smartutils-password-sdk。它允许在aes-256加密数据库文件中存储包含URL、用户名等相关信息的密码。主密码可以在一行代码中使用dpapi加密。
查看密码安全http://password safe.sourceforge.net/如何解决问题。
最终文件应具有以下布局,省略格式[salt][password verifier][encrypted encryption key][encrypted hmac key][encrypted password list][hmac value]
您可能要做的是对所有密码使用单向哈希,而根本不需要主密码。
散列的好处在于,每个人都可以阅读它,但它们并不是最聪明的,因为破解散列密码的唯一方法是暴力攻击。散列是"大"的,这会花费更多的时间。
当然,如果存储的密码很容易被字典攻击发现,这就不成立了,但是您的主密码是安全的吗?