如何使用AWS Powershell脚本中的KMS密钥加密数据

时间:2016-11-18 00:54:01

标签: aws-powershell aws-kms

我正在尝试使用AWS KMS加密文本并创建powershell脚本。所以我使用New-KMSDataKey来加密我的KMS主密钥,在输出中返回plaintextDataKeyciphertextblob

现在我使用plaintextDataKey使用Invoke-KMSEncrypt加密我的纯文本,但我收到无效操作错误,如下所示:

enter image description here

以下是我的剧本:

param([string]$zonesecret, [string]$KMSKey, [string]$Keyspec, [string]$region= 'us-east-1', [string]$AccessKey, [string]$SecretKey)

# splat
$splat = @{KeyId=$KMSKey; KeySpec=$Keyspec; Region=$region}
# generate a data key 
$datakey = New-KMSDataKey @splat

$plaintextDataKey = [Convert]::ToBase64String($datakey.Plaintext.ToArray())
$encryptedDataKey = [Convert]::ToBase64String($datakey.CiphertextBlob.ToArray())
Write-Host $plaintextDataKey
Write-Host $encryptedDataKey

#encrypt using aes-256; pass zonesecret and plaintextDataKey
# memory stream
[byte[]]$byteArray = [System.Text.Encoding]::UTF8.GetBytes($zonesecret)
$memoryStream = New-Object System.IO.MemoryStream($byteArray,0,$byteArray.Length)

$splat = @{Plaintext=$memoryStream; KeyId=$plaintextDataKey; Region=$region;}
if(![string]::IsNullOrEmpty($AccessKey)){$splat += @{AccessKey=$AccessKey;}}
if(![string]::IsNullOrEmpty($SecretKey)){$splat += @{SecretKey=$SecretKey;}}

# encrypt
**$encryptedMemoryStream = Invoke-KMSEncrypt @splat** # ERROR: 
Write-Host $encryptedMemoryStream

$base64encrypted = [System.Convert]::ToBase64String($encryptedMemoryStream.CiphertextBlob.ToArray())
Write-Host $base64encrypted

我能做些什么来做对吗?我在这里做错了吗?此处没有其他cmdlet可用于加密数据:http://docs.aws.amazon.com/powershell/latest/reference/Index.html

有人可以帮忙吗?如何使用上述明文数据密钥加密我的内容?

1 个答案:

答案 0 :(得分:2)

所提供的代码中包含许多正确的骨骼,可以成为这种动物的一部分,但它有几个阻碍其工作的问题,包括遗漏支持KMS的一些概念。使用数据密钥是使用KMS的好方法,但是当您这样做时,您必须了解您需要依赖主机系统使用提供的密钥加密和解密数据的能力。

KMS数据密钥提供envelope encryption,KMS提供的解密和解密是密钥本身,而不是数据。使用数据密钥时,必须使用KMS提供的明文密钥来加密数据,然后存储随数据提供的密钥的密文版本。当需要解密密文时,使用KMS解密数据密钥,然后使用从数据密钥密文密钥派生的明文作为AES解密的密钥。

以下是我从您提供的代码派生的工作脚本对中的测试运行示例,其中KMS密钥ID已清除:

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

以下是加密和解密脚本:

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
}