Friday, January 13, 2017

Store encrypted passwords in a text file

I've tried numerous times to get a service account password into an encrypted file and then recall it.  Sometimes it's worked, sometimes not.

I hit on a few posts that dialed me into a process that seems to work reliably.  The result is below.  Its a standalone test script but can be easily imported into any script.

What I do is use an XML file as a config file and read it when a script starts.  The encoded string this script creates is suitable for storage this way and recalls cleanly.  I also include the encrypted AES key in the XML file so both can be read and the internals of the calling script can remain generic.   This recovered password can be de-crypted by anyone not just the user who encrypted it so it works fine for service accounts running automated scripts.

The script stays in whatever folder you put it in so it's best to put it in a new empty folder for testing.

Now for the usual disclaimer... This is only nominally secure.  Anyone who knows what they are doing can de-crypt the password since BOTH the encryption key and password locations are available.  This is just to prevent the password from being stored as plain text.


Clear-Host

Function RandomKey {   #-[ function creates a random byte string of specified length ]--
      $Length = 32 #16,24, or 32
       $Script:RKey = @()
     For ($i=1; $i -le $Length; $i++) {
     [Byte]$RByte = Get-Random -Minimum 0 -Maximum 256
     $Script:RKey += $RByte
     }
      Return $Script:RKey
}

$ByteArray = RandomKey

$Password_IN = 'MyP@ssw0rd'
$User = "domain\serviceaccount"

write-host "Input password                 :"$Password_IN -ForegroundColor Red
write-host "byte array                     :"$Script:ByteArray -ForegroundColor white

#----------[ Creating AES key with random data and export to file ]-------------
$KeyFile = "$PSScriptRoot\AESkey.txt"
#$RndKey = New-Object Byte[] 16   # You can use 16, 24, or 32 for AES
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($ByteArray)
$Base64String = [System.Convert]::ToBase64String($ByteArray);
$Base64String | out-file $KeyFile
Write-Host "Byte array as base64 string    :"$Base64String -ForegroundColor Cyan

#-------------[ Creating SecureString object ]----------------------------------
$PasswordFile = "$PSScriptRoot\AESPassword.txt"
$KeyFile = "$PSSCriptRoot\AESkey.txt"
$Base64String = (Get-Content $KeyFile)
$ByteArray = [System.Convert]::FromBase64String($Base64String);
$Password = $Password_IN | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString -key $ByteArray | Out-File $PasswordFile

Write-Host "encrypted pwd                  :"(Get-Content $PasswordFile) -ForegroundColor Green

# Creating PSCredential object
$PasswordFile = "$PSScriptRoot\AESPassword.txt"
$KeyFile = "$PSSCriptRoot\AESkey.txt"
$Base64String = (Get-Content $KeyFile)
$ByteArray = [System.Convert]::FromBase64String($Base64String);
write-host "decrypted byte array           :"$ByteArray
 
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, (Get-Content $PasswordFile | ConvertTo-SecureString -Key $ByteArray)

#----------------------[ Useable result ]---------------------------------------
$Password = $Credential.GetNetworkCredential().Password
$UserID = $Credential.GetNetworkCredential().Username
write-host "decrypted password             :"$Password -ForegroundColor yellow