If you need to encrypt text in your PowerShell code, ConvertFrom-SecureString can help you do it. The -Key parameter of that cmdlet allows you to use a key that can be stored in a file. This could be useful if you don’t want to have sensitive information in plain text in your code.
Let us look at the help for ConvertFrom-SecureString. This cmdlet has 3 different sets of parameters (or 2 in PS 5.1). In this occasion, we are interested in the one with the key parameter:
ConvertFrom-SecureString
[-SecureString] <SecureString>
[-Key <Byte[]>]
[<CommonParameters>]
The documentation explains that “ConvertFrom-SecureString cmdlet converts a secure string (System.Security.SecureString) into an encrypted standard string (System.String). Unlike a secure string, an encrypted standard string can be saved in a file for later use.” Regarding the -Key parameter, the help tell us that it accepts System.Byte type of data. This can be created with the following command:
$byteKey = New-Object Byte[] 32
GetType() shows that $byteKey is an array, in this case, an array of bytes:
The -Key parameter of ConvertFrom-SecureString works with the Advanced Encryption Standard (AES), which means the key must have a length of 128, 192 or 256 bits (AES128, AES192 or AES256). AES uses the same key both for encrypting and decrypting data (which is why it is described as a symmetric-key algorithm, Bellare and Rogaway, 2005, p.9).
Creating a key
To create a 192 bit key, we need to create a byte array with 24 elements. Each element will contain 8 bits or 1 byte. When it comes to binary, the values can only be 0 or 1. That means the highest value for each element is 11111111 or 255 in decimal. If we have 24 elements with 8 bit each, our key will have 192 bits in total (8 * 24). So, to create a 192 bits key PS offers us a quick way to do it:
- First we create the byte array:
$byteKey = New-Object Byte[] 24
- And then we randomize its elements with the RandomNumberGenerator class:
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($byteKey)
Digging a bit further, the documentation explains that:
- Create() “Creates an instance of the default implementation of a cryptographic random number generator that can be used to generate random data”, and
- GetBtes(Byte[]) “fills an array of bytes with a cryptographically strong random sequence of values.” This takes our $byteKey variable as parameter and this is important because it will fail without it.
In this step we can store the key in a txt to use it later or to share it:
$byteKey | Out-File -FilePath “C:\key.txt”
As a thing to keep in mind, a 192-bit key can have 2192 possible values, which makes almost impossible to guess the right key with a brute force attack. Some estimate that even with a supercomputer, it could take more than the age of the universe (around 13.7 billions of years) just to guess a 128-bit key.
Encrypting the data
That is just to create the byte key. Now we must create the code to encrypt data. Since the -SecureString parameter of ConvertFrom-SecureString only accepts a secure string, we first need to use ConvertTo-SecureString to transform our “simple” string into a secure string:
$plainText = “Your IT Blog”
$secureString = ConvertTo-SecureString $plainText -AsPlainText -Force
Now we can call ConvertFrom-SecureString with the key we created earlier:
$encryptedString = ConvertFrom-SecureString -SecureString $secureString -Key $byteKey
Here we can see the output of each variable during the process:
Decrypting the data
Now to decrypt the string, we must convert our $encryptedString to a secure string first (the opposite of what we did to encrypt the string in the first place):
$secureStringDecrypt = ConvertTo-SecureString $encryptedString -Key $byteKey
There is a couple of ways to transform this secure string in the plain string we had at the start. In PS7 there is an easier way, but since PS 5.1 is much more common, we can use SecureStringTOBSTR and PtrToStringAuto methods of Runtime.InteropServices.Marshal class. SecureStringTOBSTR takes a secure string as parameter and converts it to an unmanaged binary string. That method returns the “address, in unmanaged memory, where the s parameter was copied to”. PtrToStringAuto can take that address to convert it to a managed string. In our case this will return the original string in plain text. As the documentation recommends, “always free the BSTR when finished by calling the ZeroFreeBSTR method”.
$secureStringToBSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureStringDecrypt)
$stringDecrypted = [Runtime.InteropServices.Marshal]::PtrToStringAuto($secureStringToBSTR)
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($secureStringToBSTR)
We end up with the same string we had at the start:
Our encrypted string can be decrypted with the same key. We solved a problem but created a new one, since anyone with the key could decrypt our data. But this is a problem that can be solved by restricting the access to key, making it available only for the intended users.
The whole code looks like this:
$byteKey = New-Object Byte[] 24
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($byteKey)
$byteKey | Out-File -FilePath “C:\key.txt”
$plainText = “Your IT Blog”
$secureString = ConvertTo-SecureString $plainText -AsPlainText -Force
$encryptedString = ConvertFrom-SecureString -SecureString $secureString -Key $byteKey
$encryptedString
$secureStringDecrypt = ConvertTo-SecureString $encryptedString -Key $byteKey
$secureStringToBSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureStringDecrypt)
$stringDecrypted = [Runtime.InteropServices.Marshal]::PtrToStringAuto($secureStringToBSTR)
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($secureStringToBSTR)
$stringDecrypted