Encrypting and decrypting passwords in your PowerShell scripts can be a challenge, especially if you have no experience with encryption. However, by using PowerShell, we can build tools to handle passwords, API keys, and any other sensitive information securely when using such data in our scripts.

There are many different ways to handle passwords in your PowerShell scripts. The main point is not to store them in plain text! Please don't. Just don't. It only takes a few more lines of code to decrypt encrypted strings and use them in PowerShell, so let's do that.

One of the easiest ways to encrypt and decrypt passwords in your PowerShell scripts is to use secure strings. Secure strings are an easy and built-in way to manage sensitive information in PowerShell. Secure strings are easy to create using the ConvertTo-SecureString cmdlet.

Let's say you need to prompt a user for a password in your script. One way to do this is to use Read-Host. The Read-Host command prompts a user for input and then returns that input. The Read-Host command AsSecureString parameter not only obfuscates what you type in but also saves it as a secure string.

You can see below that when I use the AsSecureString parameter, I can save my password as an encrypted secure string.

Read Host AsSecureString

Read Host AsSecureString

Unless you get lucky and the service you're working with accepts secure strings, chances are you're going to have to decrypt it back again before passing the password to something else. Not a problem. We can do that too. At first glance, you may think ConvertFrom-SecureString would do this, but you'd be mistaken.

PS> $mypassword = $pw | ConvertFrom-SecureString
PS> $mypassword
01000000d08c9ddf0115d1118c7a00c04fc297eb010000008c9435141716494d9426986c90f68166000000000200000000001066000000010000200000001193597f27759d6c27c14c833f7fc977364733368224a4a624f3e7dee1cd122a000000000e80000000020000200000005bbaa1f752f376e9ebbc00b9496d54600ab5bda3836f5e27e71f806d6709bb85600000004607d3b1f2556528fe04d710065635db025ea96b61f6aa2c158a3244ccc61240a4865b1be3dc7fad972735fe1ddfd73f34b6693bb1ab504124f59fe66195e0d02288074d420ef2a089cb5f1ade7299514d6bde6297f1d885565075dee93c179d40000000f45aff3e0659543639dcb16243c0609699ec7fcc6c5c4ef4e4c383f5c567a236bb5ce30e8e6c3345edc1628810c435904eb13eb5570fe2742101e342d51bd887

That doesn't look right. This is because converting it from a secure string back to a string doesn't decrypt it. To decrypt it, we're going to have to use some .NET juju and use the secure string.

PS> [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pw))
This is my secret password you can't see now.

Now that you know how to encrypt and decrypt a string, it's not much good if you can't reuse that password across different scripts. As it is, you'll have to be running it in the same session. Let's save the encrypted password to a file and then recall it again from disk.

To save our encrypted secure string to a file, we can export the entire object with Export-CliXml. You can see that when we do this, it remains encrypted while on disk.

PS> $pw | Export-Clixml -Path 'C:\MyPassword.xml'
PS> Get-Content C:\MyPassword.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <SS>01000000d08c9ddf0115d1118c7a00c04fc297eb010000008c9435141716494d9426986c90f6816600000000020000000000106600000001000020000000cda15d7d3aad8a5134d2875256ce01b71c6f8c8887c441ae39b433c7cd55104b000000000e8000000002000020000000198b64b12916864f2a536e8d17617e862290aa4592afe2ec605a9084f7afbbf660000000b4edfef5bbe12cbafdc3c87780ad23d530e4a70f40d2dcccb85636c154c6f07d803b0ad3231e31e470362d6df20fd13dc1a4e7d50aec0042d09d6b80e9da1e27f2d60f4adce58ab6d8d47ea5dea10bcdf4a27c74c7f9db4be4e74f1bc0e8137d400000003fec35f08b9c7b207afadcf0bbc5b5b764cf98725510675e3fbfb6af424d2a0ed12d0d5a3e27d5b917ae9565c7d4bac6d1677604dfa42d997c4e12ba34096f27</SS>
</Objs>

Now let's say we've got another PowerShell script that needs to reference this password. In another PowerShell script, I can import that PowerShell object from the file with Import-CliXml, which will turn it into a secure string object I can work with.

PS> $pw = Import-CliXml -Path 'C:\MyPassword.xml'

Once I have the password in a secure string object, I can then decrypt it again with our .NET juju.

Subscribe to 4sysops newsletter!

PS> $plaintextpassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pw))
PS> $plaintextpassword
This is my secret password you can't see now.

Using secure strings in PowerShell is just one way to encrypt and decrypt strings as passwords in PowerShell. If you'd like an in-depth look on other ways to make this happen, including encrypting with AES keys and user certificates, I encourage you to check out my Pluralsight course Building Security Tools in a Windows Environment.

avataravatar
10 Comments
  1. Jkdba 3 years ago

    Nice article security is super important but sometimes a pain, so this automation is great! For something a more centralized and robust consider the keepass powershell module PoShKeepass.

    avataravataravatar
  2. Joshua 3 years ago

    Does this work with Powershell Core on Linux?

  3. I think its important to mention that the password can be decrypted only on the same computer and user where it was created.

    Same result can be also achieved a bit easier if the $pw variable is created by Get-credentials cmdlet and then $pw.GetNetworkCredential().password gets the password usable in different applications. 

    avataravatar
    • Just to simplify further we can use the $pw securestring to create credentials object. and then use Credentials.GetNetworkCredential().Password to get the password in plain text

      $Credentials = New-Object System.Management.Automation.PSCredential("UserName", $pw)
      $plainpassword = $Credentials.GetNetworkCredential().Password

  4. Wena 3 years ago

    Please use ConvertTo-SecureString instead of 

    $plaintextpassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pw))

    You have error handling so you dont need to try catch that and everyone know what you are doing. This C# approach sound nice, but who can correct or read script after you.

    • @Wena 

      you comment is a bit weird. The object generated with Read-host -AsSecureString is already a secure.string object, there is nothing to convert.

      Please read the article again - the author is showing how to get a plain text password from imported Secure.string.

    • @Wena

      ConvertTo-SecureString and ConvertFrom-SecureString don't handle clear text passwords.

      ConvertFrom-SecureString converts a string secured in memory to an encrypted string
      (which can be stored in a file).

      The encrypted string looks like:

      01000000d08c9ddf0115d1118c7a00c04fc297eb010000008c9435141716494d9426986c90f68166000000000200000000001066000000010000200000001193597f27759d6c27c14c833f7fc977364733368224a4a624f3e7dee1cd122a000000000e80000000020000200000005bbaa1f752f376e

      This is not a password, but the encrypted password.

      ConvertFrom-SecureString converts the encrypted string to a string secured in memory
      (which can be used by other functions using secured strings).

      The method explained by Adam is the correct way to get back passwords in a human-readable format.

      [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pw))
      avatar
  5. Just to add theory background:

    When we use 'ConvertFrom-SecureString' to encrypt the password in Text string form, we are using the native Windows Data Protection API (DAPI) functionality to encrypt the password. This string can be written to a plain text file, but the way that DAPI works is that the encryption is such that only the original user on the original machine the encryption was performed on can decrypt the string back into a ‘Secure string’ to be reused.

    Also In order for DAPI to work, the GPO setting of 'Network Access: Do not allow storage of passwords and credentials for network authentication' must be set to Disabled. Otherwise, upon user logoff or a machine reboot, the key is lost and it will not be able to decrypt the secure string text.

    • @Swapnil Kambli

      Im afraid what your saying with the GPO setting is not true. This only disables Credentials manager. Decrypting secure strings still works, as long as the user/computer is the same (RSA key).

  6. Justin (Rank: 1)
    3 years ago

    This should could get you going to create a password that others can use (based on NTFS permissions):

     

    First step is to save a a secure password to a file using AES. The below will run as a stand alone script:

                # Prompt you to enter the username and password
                $credObject = Get-Credential

    # The credObject now holds the password in a ‘securestring’ format
                $passwordSecureString = $credObject.password

    # Define a location to store the AESKey
                $AESKeyFilePath = “aeskey.txt”
               
    # Define a location to store the file that hosts the encrypted password
                $credentialFilePath = “credpassword.txt”

    # 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)

    Set-Content $AESKeyFilePath $AESKey # Any existing AES Key file will be overwritten

    $password = $passwordSecureString | ConvertFrom-SecureString -Key $AESKey

    Add-Content $credentialFilePath $password

     

     

    Then in your script where you need to use credentials use the following:

                #set up path and user variables
                $AESKeyFilePath = “aeskey.txt” # location of the AESKey               
                $SecurePwdFilePath = “credpassword.txt” # location of the file that hosts the encrypted password               
                $userUPN = "domain\userName" # User account login

    #use key and password to create local secure password
                $AESKey = Get-Content -Path $AESKeyFilePath
                $pwdTxt =
    Get-Content -Path $SecurePwdFilePath
                $securePass = $pwdTxt |
    ConvertTo-SecureString -Key $AESKey

    #crete a new psCredential object with required username and password
                $adminCreds = New-Object System.Management.Automation.PSCredential($userUPN, $securePass)

    #use the $adminCreds for some task
                some-Task-that-needs-credentials -Credential $adminCreds

    Please be aware that if the user can get access to the password file and the key file, they can decrypt the password for the user.

Leave a reply to Swapnil Kambli (Rank: 3)
Click here to cancel the reply

Please enclose code in pre tags

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

*

© 4sysops 2006 - 2021

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account