How to encrypt data using KMS key in AWS Powershell script

 ̄綄美尐妖づ 提交于 2019-12-05 21:28:47
obscurerichard

The code presented has many of the right bones in it to be a part of this animal, but it has several issues that prevent it from working, including missing some of the concepts underpinning KMS. Using data keys is a great way to work with KMS, but when you do that, you must understand that you need to rely on your host system's ability to encrypt and decrypt the data with the keys provided.

KMS Data Keys provide envelope encryption support and the encryption and decryption that KMS provides is for the key itself, not your data. When using data keys, you must use the plaintext key that the KMS provides to encrypt your data, and then store the ciphertext version of the key that you were provided alongside the data. When it is time to decrypt your ciphertext, you use KMS to decrypt the data key and then use the plaintext derived from the data key ciphertext key as the key in your AES decryption.

Here's an example of a test run from working pair of scripts that I derived from the code you provided, with the KMS key ID scrubbed:

PS C:\Users\Administrator> $kmskey =  'abcdef01-2345-6789-0123-0123456789ab'
PS C:\Users\Administrator> ./encrypt.ps1 "This is a test" -KMSKey $kmskey | ConvertTo-Json > message.json
PS C:\Users\Administrator> type message.json
{
    "encryptedDataKey":      "AQEDAHix3RkObJxNv8rJGn2Oyy0bRR9GOvSOFTHR2OQ6SBt76wAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGui8Ycxf+XoJkAkuQIBEIA7R6eiM6PREoJdnaNA5gaeZfcSA3fC3UlRYGE6Epo96U+SqYPYzyXKOEyqB+1+3pCHz2zgZZlbcgzThrs=",
    "ciphertext":  "76492d1116743f0423413b16050a5345MgB8AGwANgBEAEwAaQB5AFQAOQByAGgAYgBPAGcAagBKAGIAQQBBAEwAQgBkAEEAPQA9AHwAMgAzADIAYgA0AGEAYgBlADgAMAA5AGQAZABkADEAOQBlADkAYgBjADgAZgA2ADgAMAA0ADgAZABhADQANQA5ADYAMABiAGIAYQAxADQANABiADAAOAA2ADYANgBlAGYANwAxADkANQA2ADEAMgBjAGEANQBjADAAYgBjAGMANAA=\r\n"
}
PS C:\Users\Administrator> ConvertFrom-JSON $(Get-Content .\message.json | Out-String) | .\decrypt.ps1 -KMSKey $kmskey

plaintext
---------
This is a test

PS C:\Users\Administrator> ./encrypt.ps1 "Super Secret Stuff" -KMSKey $kmskey | .\decrypt.ps1 -KMSKey $kmskey

plaintext
---------
Super Secret Stuff

And here are the encrypt and decrypt scripts:

encrypt.ps1

param(
    [Parameter(Mandatory=$True)]
    [string]$secret,

    [Parameter(Mandatory=$True)]
    [string]$KMSKey,

    [string]$Keyspec = 'AES_256',

    [string]$region = 'us-east-1'
)

# generate a data key
$datakey = New-KMSDataKey -KeyId $KMSKey -KeySpec $Keyspec -Region $region

[byte[]]$plaintextDataKey = $datakey.Plaintext.ToArray()
[byte[]]$encryptedDataKey = $datakey.CiphertextBlob.ToArray()

# Encrypt using AES using Powershell's SecureString facilities
# Any AES encryption method would do, this is just the most convenient
# way to do this from Powershell.
#
# Note that trying to use the Invoke-KMSEncrypt method is not what you want
# to do when using Data Keys and envelope encryption.
$encrypted = ConvertTo-SecureString $secret -AsPlainText -Force `
    | ConvertFrom-SecureString -key $plaintextDataKey `
    | Out-String

# Thanks to http://stackoverflow.com/a/24778625/424301 
# for the tip on using psobject return values and
# ValueFromPipelineByPropertyName parameters (see decrypt.ps1)
return New-Object psobject -property @{ 
    "ciphertext" = $encrypted;
    "encryptedDataKey" = $([Convert]::ToBase64String($encryptedDataKey))
}

decrypt.ps1

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true,
        Position=0,
        ValueFromPipelineByPropertyName=$true)]
    [string]$ciphertext,

    [Parameter(Mandatory=$true,
        Position=1,
        ValueFromPipelineByPropertyName=$true)]
    [string]$encryptedDataKey,

    [Parameter(Mandatory=$true,
        Position=2,
        ValueFromPipelineByPropertyName=$true)]
    [string]$KMSKey
)

[byte[]]$bytes = $([Convert]::FromBase64String($encryptedDataKey))
$stream = new-object System.IO.MemoryStream (,$bytes)

# decrypt the data key 
$response = Invoke-KMSDecrypt -CiphertextBlob $stream
if ($response.HttpStatusCode -eq 200) {
    $dataKey = $response.Plaintext.ToArray()
} else {
    throw "KMS data key decrypt failed: $(ConvertTo-Json $response)"
}
# Now AES decrypt the ciphertext and emit the plaintext
$secureString = ConvertTo-SecureString $ciphertext -key $dataKey
$plaintext =  [Runtime.InteropServices.Marshal]::PtrToStringAuto( `
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString))
return  New-Object psobject -property @{
    "plaintext" = $plaintext
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!