ToddBertschi
Contributor
Contributor

Secure Credentials with AES Keys

So I have a problem with a bit of a twist.

I maintain about 30 vCenters in my environments. This multitude of systems (I'm not even counting hosts) in addition to various automated tasks that I run means that I quickly fell in love with the New-VICredentialStoreItem command to create XML files of all of my credentials to automate logging into everything but I have a problem with it. Sometimes the XML files won't open. It's not consistent but I know why. My work system is pulled from a pool of VM's and my files are all kept on essentially a roaming profile. The pool is fairly small so I usually get the same system but its not a guarantee. When this happens, all my XML files break since it relies on the same user on the same machine to decrypt the files.What I would like to do is to move to an AES key. My key is in a location that's about as secure as it's going to be but I'm having problems with the new code and I can't seem to wrap my head around it so I need a new set of eyes. I think I'm running in circles. I'm posting the functions as I have them being called by a couple menu programs as well as in my PS Profile.

This is my original function to create a secure credential (and yes I should use Get-Credential but I was young and starry eyed when I first wrote it. The main thing is it worked.)

 

function CreateCreds
{
   $file = Read-Host "Enter the file name"
   $FQDN = Read-Host "Enter the IP or FQDN of the vCenter server"
   $user = Read-Host "Enter the user as you would in vCenter"
   $pass = Read-Host "Enter the password"

# Create VI Credential File
   New-VICredentialStoreItem -Host $FQDN -File "C:\credentials\$file.xml" -User $user -Password $pass
   Write-host "Secure file created"
}

 

 And this is the two functions I use to generate a new AES key and create a new credential file.

 

$AESKeyFilePath = "C:\Credentials\aes.key"
$SecureCredsPath = "C:\Credentials\"

function CreateKey
{
# Need to add a warning. Re-generating the key will invalidate all existing files
    # Generate a random AES Encryption Key.
    $AESKey = New-Object Byte[] 32
    [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)

    # Store the AESKey into a file. 
    Set-Content $AESKeyFilePath $AESKey 
Write-host "Key file created"
}

function CreateCreds
{
$AESKey = Get-Content -Path $AESKeyFilePath # Get contents of AES Key file
$file = Read-Host "Enter the file name"
$FQDN = Read-Host "Enter the IP or FQDN of the vCenter server"

# Prompt you to enter the username and password
            $credObject = Get-Credential
    $passwordSecureString = $credObject.password
    # Need file check. Flag if file exists and overwrite if told
    New-VICredentialStoreItem -Host $FQDN -File $SecureCredsPath$file.xml -User $credObject.UserName -Password $passwordSecureString
    Write-host "Secure file created"
}

 

Now on to using the new files....

 

function Connect-vCenter
{
    Param (
    [Parameter(Mandatory = $True)]
    $file 
    )

    if ($file)
    {
        $AESKeyFilePath = "c:\credentials\aes.key"
        $AESKey = Get-Content -Path $AESKeyFilePath # Get contents of AES Key file  

        $creds = Get-VICredentialStoreItem -File "c:\credentials\$file.xml"
        $vCenter = $creds.host
        $userUPN = $creds.user
        $securePass = $creds.Password | ConvertTo-SecureString -Key $AESKey  
        $adminCreds = New-Object System.Management.Automation.PSCredential($userUPN, $securePass)
        Connect-VIServer -Server $vCenter -Credential $adminCreds
    }
    else
    {
        Write-Host "Error."
    }
}

 

 

This just bombs out every time. The code might look familiar as I've scrapped everything I had and tried to follow posts on this board but something is wrong and I can't tell what. Does anyone have any ideas? I'd be very appreciative and you would make my life easier.

--T

Peace

 

 

 

Labels (2)
0 Kudos
3 Replies
LucD
Leadership
Leadership

I'm not sure I understand where the AES key is actually used.
Shouldn't the key be used in the CreateCreds function?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
ToddBertschi
Contributor
Contributor

You're right. I don't seem to be calling the AES key correctly when decrypting it. I've been working on this for a couple months as I have time so I have multiple copies all over (I need to be better about version control and projects). And staring at it means I don't see those little pieces get missed. I'm going to use this as my start code and work it out from there. If I get it working (or continue to have issues) I'll post the code back up for everyone.

Thanks again for the quick once over.

0 Kudos
ToddBertschi
Contributor
Contributor

Well I think I figured it out so to give back, here's what I found. I think the issue is the New-VICredentialStoreItem and the XML file it creates. Once I dropped that and moved to creating my own files, I got it working. I don't know what the issue is whether it's in encrypting or decrypting the secure password but it wasn't working. I went with INI files because I can easily query an INI file with another function I have in my profile. I'll probably re-write it as some point to use XML files again and I'll probably remove the hard links for the files as well. Below is a complete set of files. CreateSecureCreds will generate an AES key file and will create credential files for any vCenter or Host you need to connect to. It's menu driven too for your convenience. At the end I've included the Get-INI function that I did not write but at some point got scrubbed and I lost the author's name. I think it's fairly popular and easy to find. Lastly, I included the ConnectTo-VIServer function.

The script can always be improved but hopefully this helps out someone else.

 

 

<#
.SYNOPSIS
  Version: 3.0
  Name: CreateSecureCreds.ps1
  The purpose of this script is to create a series of hashed (secure) files
  with connection information to connect to vCenter instances.
  Used in conjunction with the ConnectTo-VIServer function

.DESCRIPTION
  This script creates a series of files for the ConnectTo-VIServer function to use
#>

Write-Host ""
Write-Host "GREETINGS PROGRAM"
Write-Host ""
Write-Host -fore Yellow "This script will create the password files needed"
Write-Host -fore Yellow "to securely log into various vCenters"

# Define a location to store the AESKey
$AESKeyFilePath = "X:\SecureCreds\aes.key"

### DO NOT MODIFY ANYTHING BELOW THIS LINE ###

function CreateCreds
{
$AESKey = Get-Content -Path $AESKeyFilePath # Get contents of AES Key file
$file = Read-Host "Enter the file name you want to use"
$FQDN = Read-Host "Enter the IP or FQDN of the vCenter server"
$user = Read-Host "Enter the user as you would in vCenter"
$pass = Read-Host "Enter the password" -AsSecureString
    
    $securepass = ConvertFrom-SecureString -SecureString $pass -Key $AESKey

#Create INI file
$CreateINI = @"
[Creds]
username=$user
credential=$securepass
[Host]
FQDN=$FQDN
"@

set-content "X:\SecureCreds\$file.ini" $CreateINI
Write-host "Secure file created"
}


function CreateKey
{
# Need to add a warning. Re-generating the key will invalidate all existing files
# Possibly erase all other connection files in the folder

    # Generate a random AES Encryption Key.
    $AESKey = New-Object Byte[] 32
    [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)

    # Store the AESKey into a file. This file should be protected! (e.g. ACL on the file to allow only select people to read)
    # Need check for existing file
    Set-Content $AESKeyFilePath $AESKey 
Write-host "Key file created"
}

#----------------[ Menu ]----------------------------------------------------
function Show-Menu
{
    param (
        [string]$Title = 'Create Credentials'
    )
    #Clear-Host
    Write-Host "================ $Title ================"
    
    Write-Host "     Press '1' to create a new credential file"
    Write-Host "     Press '2' to generate a new AES key file"
    Write-Host "       Q: Press 'Q' to quit."
}

Function SelectMenu {
do
 {
     Show-Menu
     $selection = Read-Host "Please make a selection"
     switch ($selection)
     {
         '1' {
             CreateCreds
         }
         '2' {
             CreateKey
         }  
     }
     #pause
 }
 until ($selection -eq 'q')
 }
#----------------[ Main Execution ]----------------------------------------------------
SelectMenu
Write-Host ""

#### Addtional Functions ####

# Read in INI files
<#
Function Get-IniContent {  
      
    [CmdletBinding()]  
    Param(  
        [ValidateNotNullOrEmpty()]  
        [ValidateScript({(Test-Path $_) -and ((Get-Item $_).Extension -eq ".ini")})]  
        [Parameter(ValueFromPipeline=$True,Mandatory=$True)]  
        [string]$FilePath  
    )  
      
    Begin  
        {Write-Verbose "$($MyInvocation.MyCommand.Name):: Function started"}  
          
    Process  
    {  
        Write-Verbose "$($MyInvocation.MyCommand.Name):: Processing file: $Filepath"  
              
        $ini = @{}  
        switch -regex -file $FilePath  
        {  
            "^\[(.+)\]$" # Section  
            {  
                $section = $matches[1]  
                $ini[$section] = @{}  
                $CommentCount = 0  
            }  
            "^(;.*)$" # Comment  
            {  
                if (!($section))  
                {  
                    $section = "No-Section"  
                    $ini[$section] = @{}  
                }  
                $value = $matches[1]  
                $CommentCount = $CommentCount + 1  
                $name = "Comment" + $CommentCount  
                $ini[$section][$name] = $value  
            }   
            "(.+?)\s*=\s*(.*)" # Key  
            {  
                if (!($section))  
                {  
                    $section = "No-Section"  
                    $ini[$section] = @{}  
                }  
                $name,$value = $matches[1..2]  
                $ini[$section][$name] = $value  
            }  
        }  
        Write-Verbose "$($MyInvocation.MyCommand.Name):: Finished Processing file: $FilePath"  
        Return $ini  
    }  
          
    End  
        {Write-Verbose "$($MyInvocation.MyCommand.Name):: Function ended"}  
} 
#>

# Connection function
<#
function ConnectTo-VIServer
{
    Param (
    [Parameter(Mandatory = $True)]
    $file 
    )
    $filecheck = "X:\SecureCreds\$file.ini"
    if (Test-Path $filecheck -pathType Leaf)
    {
 
        $AESKeyFilePath = "X:\SecureCreds\aes.key"
        $AESKey = Get-Content -Path $AESKeyFilePath # Get contents of AES Key file  

        $creds = Get-IniContent X:\SecureCreds\$file.ini"

        $vCenter = $creds.host.FQDN
        $userUPN = $creds.creds.username
        $securePass = $creds.creds.credential | ConvertTo-SecureString -Key $AESKey

        #Write-host "VCSA:" $vCenter
        #Write-host "User:" $userUPN
        #Write-host "Pass:" $securepass
        
        $adminCreds = New-Object System.Management.Automation.PSCredential($userUPN, $securePass)

        Connect-VIServer -Server $vCenter -Credential $adminCreds
        
    }
    else
    {
        Write-Host "Connection file does not exist"
    }
}

#>

 

 

0 Kudos