SecureString encryption

This topic contains 4 replies, has 3 voices, and was last updated by Profile photo of Thomas Franke Thomas Franke 3 years, 7 months ago.

  • Author
    Posts
  • #9891
    Profile photo of Thomas Franke
    Thomas Franke
    Participant

    From the book "Powershell in depth" (p.177): "The password can only be decrypted using the private key, which exists only on the computer where the credential was created."

    Several other ressources reports that the encryption is done with the private key of the user who created the credential / encrypted string.

    When running tests with stored credentials (without using the -key Parameter), I couldn't decrypt the password neither with an other user on the same machine nor with the same domain user on another machine.

    So is the encryption done with both keys – user and machine? Or did I mess up something during my tests?

  • #9900
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    SecureStrings are encrypted using the Data Protection API (DPAPI). If you really want to get into the gory details of how that works, start here: http://msdn.microsoft.com/en-us/library/ms995355.aspx.

    The short answer is that, yes, the keys are specific to both the user and machine where the encryption took place, by default.

  • #10269
    Profile photo of John Mello
    John Mello
    Participant

    I just ran across this very issue trying to load balance a PowerShell script that syncs Exchange Calendars via EWS using impersonation. I saved the credentials for the impersonation account in an XML file and realized after testing it on separate machines that this wasn't going to work due to the fact that the Data Protection API (DPAPI) uses both the user and machine to create the key. Does anyone have an alternate solution I can employ without hardcoding the password in the script? My fall back is to create an impersonation task account and have the script run under that user.

  • #10272
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    That depends mainly on where you want to fall on the Security < —> Usability scale. There's really no 100% secure way to store secret data with just software, but DPAPI is about as good as it gets. With that in mind, I'd probably take the approach of encrypting the password as a SecureString on each machine that needs to run the script. You could even do it remotely with Invoke-Command to keep the manual effort down. Something like this:

    $cred = Get-Credential # The account that will be running the scheduled task on the remote machines
    $remoteComputers = @('Computer1','Computer2','Computer3')
    $savedPassword = 'SomePasswordIWantToEncrypt'
    
    $scriptBlock = {
        $args[0] |
        ConvertTo-SecureString -AsPlainText -Force |
        ConvertFrom-SecureString |
        Set-Content $home\Documents\EncryptedPassword.txt
    }
    
    # The -UseSSL switch is here to avoid sending the password over the network in plain text, but you'd have to have configured HTTPS Listeners on the remote computers.
    Invoke-Command -ComputerName $remoteComputers -Credential $cred -ScriptBlock $scriptBlock -ArgumentList $savedPassword -UseSSL
    
  • #10301
    Profile photo of Thomas Franke
    Thomas Franke
    Participant

    If you're using PowerShell 3.0 you can directly output a PSCredential object to disk:

    Get-Credential | Export-Clixml $path

    And read it back in:

    $cred = Import-Clixml $path

    The conversion is done automatically, which is a nice feature. Be sure to restrict your script to PS 3.0 since the password will not be saved to file with earlier versions.

    Read more about PowerShell version issues here: http://thomas-franke.net/2013/09/19/managing-powershell-version-issues/

You must be logged in to reply to this topic.