This has been a long-standing item on my todo list, and since it seems that nobody else has piped up about it in the past ten years here we go:
Windows has a built-in credential manager (check your start menu for "Credential Manager") that stores secrets encrypted by your user profile. They cannot be decrypted without your user account credentials (and will be lost if you force an account password reset with something like a recovery boot device). They will be decrypted automatically by Windows when asked for by a program/script running under your user account, so you won't be prompted for a password to decrypt them beyond the windows logon screen you used to log in in the first place.
You can access this credential store to put sensitive information in that you don't want other users to access, or someone who steals your laptop to access. Do note that if your laptop does not have a proper password set on it, it won't be properly encrypted against them just signing in as you and pulling all the passwords. Be sure to choose a unique name, as any program/script running as you shares this store and can access passwords stored in it by that name.
You can put passwords into the store either using AHK, or by opening the credential manager, going to "Windows Credentials", and clicking "Add a generic credential".
Bonus points for those of you in a corporate domain, these passwords will follow your domain profile around to any other computer you sign in to. (if you don't want that, change PERSIST to 2).
Note: this probably won't work for ANSI AHK, but that's just because the code changes to make it support both are painful to write. Someone could port it if they wanted.
Code: Select all
if !CredWrite("AHK_CredForScript1", "SomeUsername", "SomePassword")
MsgBox failed to write cred
if (cred := CredRead("AHK_CredForScript1"))
MsgBox % cred.name "," cred.username "," cred.password
else
MsgBox Cred not found
if !CredDelete("AHK_CredForScript1")
MsgBox Failed to delete cred
if (cred := CredRead("AHK_CredForScript1"))
MsgBox % cred.name "," cred.username "," cred.password
else
MsgBox Cred not found
CredWrite(name, username, password)
{
VarSetCapacity(cred, 24 + A_PtrSize * 7, 0)
cbPassword := StrLen(password)*2
NumPut(1 , cred, 4+A_PtrSize*0, "UInt") ; Type = CRED_TYPE_GENERIC
NumPut(&name , cred, 8+A_PtrSize*0, "Ptr") ; TargetName = name
NumPut(cbPassword, cred, 16+A_PtrSize*2, "UInt") ; CredentialBlobSize
NumPut(&password , cred, 16+A_PtrSize*3, "UInt") ; CredentialBlob
NumPut(3 , cred, 16+A_PtrSize*4, "UInt") ; Persist = CRED_PERSIST_ENTERPRISE (roam across domain)
NumPut(&username , cred, 24+A_PtrSize*6, "Ptr") ; UserName
return DllCall("Advapi32.dll\CredWriteW"
, "Ptr", &cred ; [in] PCREDENTIALW Credential
, "UInt", 0 ; [in] DWORD Flags
, "UInt") ; BOOL
}
CredDelete(name)
{
return DllCall("Advapi32.dll\CredDeleteW"
, "WStr", name ; [in] LPCWSTR TargetName
, "UInt", 1 ; [in] DWORD Type,
, "UInt", 0 ; [in] DWORD Flags
, "UInt") ; BOOL
}
CredRead(name)
{
DllCall("Advapi32.dll\CredReadW"
, "Str", name ; [in] LPCWSTR TargetName
, "UInt", 1 ; [in] DWORD Type = CRED_TYPE_GENERIC (https://learn.microsoft.com/en-us/windows/win32/api/wincred/ns-wincred-credentiala)
, "UInt", 0 ; [in] DWORD Flags
, "Ptr*", pCred ; [out] PCREDENTIALW *Credential
, "UInt") ; BOOL
if !pCred
return
name := StrGet(NumGet(pCred + 8 + A_PtrSize * 0, "UPtr"), 256, "UTF-16")
username := StrGet(NumGet(pCred + 24 + A_PtrSize * 6, "UPtr"), 256, "UTF-16")
len := NumGet(pCred + 16 + A_PtrSize * 2, "UInt")
password := StrGet(NumGet(pCred + 16 + A_PtrSize * 3, "UPtr"), len/2, "UTF-16")
DllCall("Advapi32.dll\CredFree", "Ptr", pCred)
return {"name": name, "username": username, "password": password}
}