In continuing with my previous post, Secure Password with PowerShell: Encrypting Credentials - Part 1, I'd like to talk about sharing credentials between different machines/users/etc.
To recap my last blog, part 1 of Encrypting Credentials, when you use ConvertTo-SecureString and ConvertFrom-SecureString without a Key or SecureKey, Powershell will use Windows Data Protection API (DPAPI) to encrypt/decrypt your strings. This means that it will only work for the same user on the same computer.
When you use a Key/SecureKey, the Advanced Encryption Standard (AES) encryption algorithm is used. You are able to use the stored credential from any machine with any user so long as you know the AES Key that was used.
Here's a small snippet from my last blog post for easy reference for the two cmdlets:
ConvertTo-SecureString and ConvertFrom-SecureString
Syntax
ConvertTo-SecureString [-String] SomeString
ConvertTo-SecureString [-String] SomeString [-SecureKey SecureString]
ConvertTo-SecureString [-String] SomeString [-Key Byte[]]
ConvertTo-SecureString [-String] SomeString [-AsPlainText] [-Force]
ConvertFrom-SecureString [-SecureString] SecureString [-SecureKey SecureString]
ConvertFrom-SecureString [-SecureString] SecureString [-Key Byte[]]
–String String
The string to convert to a SecureString
–SecureKey SecureString
Encryption key as a SecureString.
–Key Byte[]
Encryption key as a byte array.
Using no Key/SecureKey
First, let’s show an example of what you will see if you try to create a credential from one machine (Machine 1) and then access it from another machine (Machine 2) without providing a key.
Encrypting a password without a key and saving it to file from Machine 1
This will create the file Password.txt with an encrypted standard string representing the password. It will use DPAPI.
$File = "\\Machine1\SharedPath\Password.txt"
$Password = "P@ssword1" | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString | Out-File $File
Accessing the encrypted password file from Machine 1
This will successfully get the encrypted standard string using Get-Content, and convert it to a SecureString.
$File = "\\Machine1\SharedPath\Password.txt"
Get-Content $File | ConvertTo-SecureString
Accessing the encrypted password file from Machine 2
This will fail with an error indicating that the key is invalid. Example below:
$File = "\\Machine1\SharedPath\Password.txt"
Get-Content $File | ConvertTo-SecureString
Using Key/SecureKey
Now, let's show a simple example of creating an encrypted standard string with the use of a key. AES encryption only supports 128-bit (16 bytes), 192-bit (24 bytes) or 256-bit key (32 bytes) lengths, so we'll need to create or generate an appropriate key. For simplicity, let's create a simple byte array of ascending numbers. We'll use a 128-bit key, so we'll need a 16-byte array.
Long way
[Byte[]] $key = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
Short way
[Byte[]] $key = (1..16)
Encrypting a password and saving it to file from Machine 1
This will create the file (with Out-File) Password.txt with an encrypted standard string representing the password. It will use AES.
$File = "\\Machine1\SharedPath\Password.txt"
[Byte[]] $key = (1..16)
$Password = "P@ssword1" | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString -key $key | Out-File $File
Accessing the encrypted password file from Machine 1
This will successfully get the encrypted standard string and convert it to a SecureString. It will use the AES key that we provided earlier.
$File = "\\Machine1\SharedPath\Password.txt"
[Byte[]] $key = (1..16)
Get-Content $File | ConvertTo-SecureString -Key $key
Accessing the encrypted password file from Machine 2
This will successfully get the encrypted standard string and convert it to a SecureString by using the AES key.
$File = "\\Machine1\SharedPath\Password.txt"
[Byte[]] $key = (1..16)
Get-Content $File | ConvertTo-SecureString -Key $key
Putting it all together
Now that you know how to use an AES key to make SecureStrings created by different user accounts and workstations, you have to protect that key as best as you can since anybody who has that AES key can now decrypt the data protected.
In my previous example, I used a very simple 16-byte array that is stored in the body of the script itself. This is not a good practice and is essentially the same as if you were to write the password in clear text. Alternatively, you could create/generate a key beforehand in a separate script.
As an example, I have built a small script to generate a random 16-byte array. I have used the System.Security.Cryptography.RNGCryptoServiceProvider class to fill a byte array with randomly-generated data.
Creating AES key with random data and export to file
$KeyFile = "\\Machine1\SharedPath\AES.key"
$Key = New-Object Byte[] 16 # You can use 16, 24, or 32 for AES
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
$Key | out-file $KeyFile
Creating SecureString object
$PasswordFile = "\\Machine1\SharedPath\Password.txt"
$KeyFile = "\\Machine1\SharedPath\AES.key"
$Key = Get-Content $KeyFile
$Password = "P@ssword1" | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString -key $Key | Out-File $PasswordFile
Creating PSCredential object
$User = "MyUserName"
$PasswordFile = "\\Machine1\SharedPath\Password.txt"
$KeyFile = "\\Machine1\SharedPath\AES.key"
$key = Get-Content $KeyFile
$MyCredential = New-Object -TypeName System.Management.Automation.PSCredential `
-ArgumentList $User, (Get-Content $PasswordFile | ConvertTo-SecureString -Key $key)
Final Notes
That’s it! You will be able to use the key file to decrypt the password file from any machine with any user.
Be sure to protect that AES key as if it were your password. Anybody who can read the AES key can decrypt anything that was encrypted with it.
At the very least, you should implement strict NTFS access controls for both your password and key files. This should keep out most prying eyes. Additionally, you may consider storing the data in a database with strict access controls or even implementing public-key (asymmetric) cryptography for additional control.
In any case, you should be good to use AES keys with your SecureStrings now.
Did you know that PDQ Deploy has a PowerShell step you can use to deploy your scripts?