Encrypting Sensitive Data for Transit or Rest with PowerShell

This blog is about how you can use the .Net cryptography model to encrypt and decrypt sensitive information such as passwords with PowerShell. It is specifically for the encryption in transit and encryption at rest scenarios, where, for example, you may be sending data to a backend service and securely storing it in that service. If you just want to secure data on a local machine, the ConvertTo-SecureString and ConvertFrom-SecureString cmdlets may suffice.

Broadly there are two types of encryption, symmetric and asymmetric encryption. Symmetric encryption uses the same key to encrypt and decrypt, whereas asymmetric uses a public/private key pair. Which you use really depends on your requirements – I use both in different scenarios.

At the end of the blog you’ll find the functions and full code for the following examples.

Symmetric encryption

Symmetric encryption uses the same both to encrypt and decrypt, which means you must keep this key secure. This type of encryption can be more useful where the encryption key is not publicly exposed. For example, I use this kind of encryption to encrypt sensitive data for rest and storage within Azure services. The encryption is done within Azure and so the key is never exposed or accessible outside our Azure environment.

I am using AES to create a 256-bit key using the CBC cipher mode, which are the current .Net defaults. We first need to create a key and an initialization vector (IV), which helps to hide observable patterns in the resulting ciphertext. Both the key and the IV and needed to both encrypt and decrypt the text.

The following code creates the key and the IV as base64 strings.

$AES = [System.Security.Cryptography.Aes]::Create()
$Key = [System.Convert]::ToBase64String($aes.Key)
$IV = [System.Convert]::ToBase64String($aes.IV)

If you intend to re-use the same key to encrypt other data, the recommendation is to generate a new IV each time:

$AES.GenerateIV()
$IV = [System.Convert]::ToBase64String($aes.IV)

To encrypt a string, simply do:

$TextToEncrypt = "SuperSenstiveData"
$EncryptedText = Encrypt-Data -Data $TextToEncrypt -Key $Key -IVector $IV

This will return the encrypted text as a base64 string.

To decrypt, pass the same key and IV you used to encrypt the text:

$DecryptedText = Decrypt-Data -Data $EncryptedText -Key $Key -IVector $IV

Asymmetric Encryption

Asymmetric encryption uses a public and private key pair. The public key is not sensitive and is ok to store in a script etc as the key can only encrypt but not decrypt the data. The private key, however, must be secured and should not be publicly accessible. This type of encryption is useful for securing sensitive data in transit. For example, I may gather some secret from a client workstation and send this to a backend service in a web request. The client has the public key and encrypts the data before sending, but only the backend service which processes the data has the private key for decryption.

I am using RSACng with a key size of 3072 and a padding type of OaepSHA384 with UTF8 encoding. There are a few things to note with this:

  • RSACng uses the Windows CNG libraries and therefore will only work on a Windows OS
  • .Net framework 4.6 minimum is required
  • Only short strings can be encrypted, depending on the padding mode used
    • For example, using OeapSHA384 padding and UTF8 encoding a maximum of 286 bytes can be encrypted. This will typically equate to 286 characters of text
    • Using the same padding with Unicode encoding can also encode a maximum or 286 bytes, but this will be only 143 characters because each character is 2 bytes wide

The following code creates the public and private keys as base64 strings.

$RSACNG = [System.Security.Cryptography.RSACng]::new(3072)
$ExportedPublicKey = $RSACNG.key.Export([System.Security.Cryptography.CngKeyBlobFormat]::GenericPublicBlob)
$ExportedPrivateKey = $RSACNG.key.Export([System.Security.Cryptography.CngKeyBlobFormat]::GenericPrivateBlob)
$PublicKey = [System.Convert]::ToBase64String($ExportedPublicKey)
$PrivateKey = [System.Convert]::ToBase64String($ExportedPrivateKey)

To encrypt, use the public key:

$TextToEncrypt = "SuperSenstiveData"
$EncryptedText = Encrypt-Data -Data $TextToEncrypt -PublicKey $PublicKey

And use the private key to decrypt:

$DecryptedText = Decrypt-Data -Data $EncryptedText -PrivateKey $PrivateKey

Happy encrypting!

Full code with examples

# Example code for encrypting and decrypting secrets with .Net cryptography using either symmetric or asymmetric encryption
###################################
## SYMMETRIC ENCRYPTION ##
## Using AES 256-bit in CBC mode ##
###################################
# Create an AES key and Initialization vector
$AES = [System.Security.Cryptography.Aes]::Create()
$Key = [System.Convert]::ToBase64String($aes.Key)
$IV = [System.Convert]::ToBase64String($aes.IV)
# Save both the key and IV and secure the key
# Use the same key and IV to encrypt and decrypt
# Recommendation is to use a different IV for each encryption where possible, eg:
# $AES.GenerateIV()
# $IV = [System.Convert]::ToBase64String($aes.IV)
# Encrypt
Function Encrypt-Data {
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
[String]$Key,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=1)]
[String]$IVector,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=2)]
[String]$Data
)
$KeyBytes = [System.Convert]::FromBase64String($Key)
$IVBytes = [System.Convert]::FromBase64String($IVector)
$aes = [System.Security.Cryptography.Aes]::Create()
$aes.Key = $KeyBytes
$aes.IV = $IVBytes
$encryptor = $aes.CreateEncryptor()
[System.Byte[]]$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Data)
$EncryptedBytes = $encryptor.TransformFinalBlock($Bytes,0,$bytes.Length)
$EncryptedBase64String = [System.Convert]::ToBase64String($EncryptedBytes)
Return $EncryptedBase64String
}
# Decrypt
Function Decrypt-Data {
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
[String]$Key,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=1)]
[String]$IVector,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=2)]
[String]$Data
)
$KeyBytes = [System.Convert]::FromBase64String($Key)
$IVBytes = [System.Convert]::FromBase64String($IVector)
$aes = [System.Security.Cryptography.Aes]::Create()
$aes.Key = $KeyBytes
$aes.IV = $IVBytes
$EncryptedBytes = [System.Convert]::FromBase64String($Data)
$Decryptor = $aes.CreateDecryptor()
$DecryptedBytes = $Decryptor.TransformFinalBlock($EncryptedBytes,0,$EncryptedBytes.Length)
$DecryptedString = [System.Text.Encoding]::Unicode.GetString($DecryptedBytes)
Return $DecryptedString
}
# Example
$Key = "/L5b+B9W1wS+dV2M2yD66W7V6…"
$IV = "+p7ppGLz7XOHR…"
$TextToEncrypt = "SuperSenstiveData"
$EncryptedText = EncryptData Data $TextToEncrypt Key $Key IVector $IV
$DecryptedText = DecryptData Data $EncryptedText Key $Key IVector $IV
#################################################
## ASYMMTRIC ENCRYPTION ##
## Using RSACng 3072-bit with SHA-384 padding ##
## Will encrypt maximum 286 bytes which in ##
## UTF-8 or ASCII will be up to 286 characters ##
## but in Unicode will be 143 ##
## Requires .Net Framework 4.6 minimum ##
#################################################
# Create the pub/prv key pair
$RSACNG = [System.Security.Cryptography.RSACng]::new(3072)
$ExportedPublicKey = $RSACNG.key.Export([System.Security.Cryptography.CngKeyBlobFormat]::GenericPublicBlob)
$ExportedPrivateKey = $RSACNG.key.Export([System.Security.Cryptography.CngKeyBlobFormat]::GenericPrivateBlob)
$PublicKey = [System.Convert]::ToBase64String($ExportedPublicKey)
$PrivateKey = [System.Convert]::ToBase64String($ExportedPrivateKey)
# Save the public and private keys and secure the public key
# Use the public key to encrypt and the private key to decrypt
# Encrypt
Function Encrypt-Data {
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
[String]$Data,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=1)]
[String]$PublicKey
)
$PublicKeyBytes = [System.Convert]::FromBase64String($PublicKey)
[System.Byte[]]$DataBytes = [System.Text.Encoding]::UTF8.GetBytes($Data)
$CNGPublicKey = [System.Security.Cryptography.CngKey]::Import($PublicKeyBytes,[System.Security.Cryptography.CngKeyBlobFormat]::GenericPublicBlob)
$Encryptor = [System.Security.Cryptography.RSACng]::new($CNGPublicKey)
$EncryptedBytes = $Encryptor.Encrypt($DataBytes,[System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA384)
$EncryptedBase64String = [System.Convert]::ToBase64String($EncryptedBytes)
return $EncryptedBase64String
}
# Decrypt
Function Decrypt-Data {
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
[String]$Data,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=1)]
[String]$PrivateKey
)
$PrivateKeyBytes = [System.Convert]::FromBase64String($PrivateKey)
$CNGPrivateKey = [System.Security.Cryptography.CngKey]::Import($PrivateKeyBytes,[System.Security.Cryptography.CngKeyBlobFormat]::GenericPrivateBlob)
$Decryptor = [System.Security.Cryptography.RSACng]::new($CNGPrivateKey)
$EncryptedBytes = [System.Convert]::FromBase64String($Data)
$DecryptedBytes = $Decryptor.Decrypt($EncryptedBytes,[System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA384)
$DecryptedString = [System.Text.Encoding]::UTF8.GetString($DecryptedBytes)
return $DecryptedString
}
# Example
$PublicKey = "UlNBMQAMAAADAAAAgAEAAAAAAAAAAAAAAQABy2LZ…"
$TextToEncrypt = "SuperSenstiveData"
$EncryptedText = EncryptData Data $TextToEncrypt PublicKey $PublicKey
$PrivateKey = "UlNBMgAMAAADAAAAgAEA…."
$DecryptedText = DecryptData Data $EncryptedText PrivateKey $PrivateKey

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.