What are we doing here??

The purpose of this post is to demonstrate how you can write a function to both store credentials (passwords) to a disk using Encryption and then load them up as a secure credential object.

Often when creating scripts that must log in to other services you will need some method of saving credentials if the scripts are not interactive or are run with some sort of automation. When you do this the first thing that people often do is rely on the Windows Data Protection API to handle the access to the credentials. The problem with this is that those credentials can then only be access by that user account on that machine. Which creates issues when deploying to downstream environments.

The method demonstrated here will be to use the -Key parameter to create an encryption key that can then be used to decrypt the credentials on another machine.

References: ConvertTo-SecureString, ConvertFrom-SecureString

Core elements – Creating the key and secure string

Creating an encryption key: Use the Cryptography service to generate a 256 bit encryption key:

#Identify the path that the key will be saved to
$KeyPath = "C:\Temp\YourKey.key"

#Create the key object and define it's type
$Key = New-Object Byte[] 32

#populate the Key object with some random characters to generate the key and then export it to the KeyPath as defined above.
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
$Key | out-file $KeyPath

Once you have generated your Encryption key, it is time to generate a secure string that we will use as the password to be encrypted:

#Populate your password variable as a generic string
$Password = "ASuperSecretPassword"

#Create your secure string by converting the password variable
$SecureString = ConvertTo-SecureString $Password -AsPlainText -Force

Now with our Encryption Key and our Secure string variable we can encrypt the information to disk as a txt file.

#Identify the file that you will be writing the encrypted credentials to
$CredPath = "C:\Temp\YourPassword.txt"

#Convert your secure string to an encrypted file using the encryption key you generated above
$EncryptedString = ConvertFrom-SecureString $SecureString -Key $key

#Write the encrypted secure string to the path as a text file
Set-Content $CredPath -Value $EncryptedString

After this is completed, you should have 2 files written to disk: YourKey.key and YourPassword.txt.

Opening YourPassword.txt you will see that it is in an encrypted format:

Encrypted Password file

Core elements – Decrypting and using the password file via the Encryption Key

Now that you have written both the YourKey.key file and the YourPassword.txt file you can copy them to another server to use. In order to use them you will first need to import them both as variables into the system.

#Define the paths for both the KeyFile and CredentialFile
$KeyFile = "C:\Temp\YourKey.key"
$CredentialFile = "C:\Temp\YourPassword.txt"

#Import the Key and Cred files
$Key = Get-Content $KeyFile
$Creds = Get-Content $CredentialFile

Convert the cred object into a secure string decrypting it with the Key

$Password = ConvertTo-SecureString $Creds -Key $Key

If you want you can view the $Password variable and see it is a secure string:

You can use this secure string in your script as a password by adding it into a credential object as shown below:

#Define a username to be added to the credential object if you wish

$UserName = "YourFancyUserName"
$Creds = New-Object System.Management.Automation.PSCredential ($Username, $Password)

Example output as shown:

Putting it all together – Building 2 functions to create credential files and get existing credential files

Creating a function to make a new credential file is fairly straightforward. Make a simple function with a few parameters that identify the path the files will be saved to, the password that will be encrypted and the name of the password (for naming the files).

Function New-SecureCredentialsFile
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Password,
        [Parameter(Mandatory)]
        [string]
        $Path,
        [Parameter(Mandatory)]
        [string]
        $CredentialName
    )

#Generate the filenames and paths for the AES key and the password file
$CredPath = $Path + $CredentialName + ".txt"
$KeyPath = $Path + $CredentialName + ".key"

#Generate a 256 bit AES encryption key and output it to the keypath
$Key = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
$Key | out-file $KeyPath

#Take the input passwordstring and convert it to a secure string
$SecureString = ConvertTo-SecureString $Password -AsPlainText -Force

$EncryptedString = ConvertFrom-SecureString $SecureString -Key $key

#Write the encrypted secure string to the path as a text file
Set-Content $CredPath -Value $EncryptedString

}

Creating a function to get the credential file is similarly straightforward. Just make a basic function with a parameter for the path to the keyfile and credential file as well as adding in an optional UserName parameter to create full credential objects if you want.

Function Get-SecureCredentialsFile
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [String]
        $KeyFile,
        [Parameter(Mandatory)]
        [String]
        $CredentialFile,
        [Parameter()]
        $Username
    )

#Load the keyfile and credentials file using the parameter paths defined when the function is run
$Key = Get-Content $KeyFile

$Creds = Get-Content $CredentialFile

#If the username parameter is set, it will output a credential object complete with username and password.  If it is not set, it will just output the password as a secure string
If ($PSBoundParameters.ContainsKey('Username'))
    {
    $Password = ConvertTo-SecureString $Creds -Key $Key

    $Creds = New-Object System.Management.Automation.PSCredential ($Username, $Password)

    Return $Creds
        
    }

Else{
    $Password = ConvertTo-SecureString $Creds -Key $Key

    Return $Password
    }

}

And there you have it!

Using these functions you can create a new encrypted password file and key that can be transported or imported into any server. You will want to secure both the encryption key and the password file using file system access control lists or permissions to ensure that unauthorized accounts are not able to view the files but this can be simply handled by providing the service accounts or user accounts that will run the script permissions on the files.


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *