CredLocker – Storing PSCredentials to File
Summary
CredLocker is tool to securely store PSCredentials to a file such that the file can be copied to multiple hosts and the PSCredentials can be retrieved from that file via processes running on those hosts.
The function + the data store file together provide functionality similar to PasswordSafe: a password-secured data store for user credentials. However, instead of storing the credentials as discrete strings for Username and Password, CredLocker stores and retrieves the credentials as [PSCredential] objects, making it suitable for automation.
Overview
CredLocker uses the globally-scoped [PSCredentail] variable CredLockerCredential to secure the stored credentials. Specifically, it uses [SHA256] ComputeHash() method against CredLockerCredential’s [SecureString] Password property to generate a 32-byte array.
CredLocker then uses this 32-byte array as the value to ConvertTo-SecureString and ConvertFrom-SecureString’s -Key parameter. Because ComputeHash() is deterministic, it will return the same 32-byte array for the same input data on all hosts. A [SecureString] value encrypted to string via ConvertFrom-SecureString can be decrypted via ConvertTo-SecureString on a different host provided both cmdlet calls are made with the same -Key parameter value.
Workflow
1. User calls Get-CredLocker and is prompted for globally-scoped [PSCredential] $CredLockerCredential if it does not already exist in the user’s environment.
2. $home\CredLocker.clixml file is created or read.
3. Globally-scoped [PSObject] $ CredLocker is populated with contents of $home\CredLocker.clixml.
4. User creates [PSCredential] $cred1 via Get-Credential.
5. User stores [PSCredential] $cred1 via $CredLocker.Set($cred1). This:
- Generates a temporary [String]$EncryptedUserName by
o Converting $cred1’s UserName property to a [SecureString] via ConvertTo-SecureString.
o Encrypting the [SecureString] to [String] via ConvertFrom-SecureString using the 32-byte array.
- Updates [Hashtable] $CredLocker.Data with entry of Name $EncryptedUserName and of Value ($cred1’s Password property encrypted to [String] via ConvertFrom-SecureString using the 32-byte array.)
- Updates $home\CredLocker.clixml.
6. User copies $home\CredLocker.clixml to different host.
7. On other host, user calls Get-CredLocker and is prompted for $CredLockerCredential.
8. $CredLocker is populated with $home\CredLocker
9. User retrieves [PSCredential] $cred1 = $CredLocker.Get($UserName) where $UserName is the username provided to the [PSCredential] in step 4, above. This:
- Generates a temporary two-way [Hashtable] $keys containing all the encrypted and decrypted usernames.
- Looks up the specified $UserName against $keys
- If found, retrieves the [String] containing the encrypted [SecureString] from [Hashtable] $CredLocker.Data.
- Creates [PSCredential] by
o Creating a [SecureString] $SecureString from the encrypted [SecureString] via ConvertTo-SecureString using the 32-byte array.
o Creating a transient password [String] from the [SecureString] via [System.Runtime.InteropServices.marshal]::PtrToStringAuto( [System.Runtime.InteropServices.marshal]::SecureStringToBSTR( $SecureString ))
o Calling New-Object System.Management.Automation.PSCredential with the specified $UserName and the decrypted transient password [String].
o Nulling out $password, then removing the variable.
Security Considerations
CredLocker is designed with security in mind. It does not attempt to create its own ciphers. Instead, it relies on PowerShell cmdlets specifically created for manipulating [SecureString] values.
- Uses Get-Credential to prompt user for the master CredLockerCredential password, so it is never displayed. It is immediately stored as a [SecureString] object.
- Uses [SHA256]::ComputeHash() against the master CredLockerCredential password to generate the 32-byte array used as the -Key parameter value.
- Obfuscates the usernames of the stored credentials.
- Uses ConvertFrom-SecureString and ConvertTo-SecureString to store and retrieve values to data store file.
- Exports credentials as a [PSCredential] object, thus avoiding exposing the password as plaintext.
- Converts [SecureString] values to plaintext password in-stream, so it is never explicitly stored to a variable.
- Overwrites, then removes temporary variables for decrypting [PSCredential] usernames in the CredLocker.Data [Hashtable].
Edit: 2014-05-21: added history feature.
Here’s the code:
function Get-CredLocker
{
#region header
####################################################################################################