How to encrypt data using KMS key in AWS Powershell script - aws-powershell

I am trying to encrypt a text using AWS KMS and creating a powershell script. So I used New-KMSDataKey to encrypt my KMS master key which in output returns plaintextDataKey and ciphertextblob.
Now I am using plaintextDataKey to encrypt my plaintext using Invoke-KMSEncrypt but I get Invalid Operation error as shown below:
Below is my script:
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
What can I do to make it right? Am I doing anything wrong here? There is no other cmdlets here to encrypt data: http://docs.aws.amazon.com/powershell/latest/reference/Index.html
Could anyone please help here? How can I use the above plaintext data key to encrypt my content?

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
}

Related

Format of TPM public EK for DPS enrollment

I'm trying to use TPM based enrollment to my IoT Hub. We have a couple devices already in the field (running windows iot ent) so I've written a basic PS script to get their TPM public EK and enroll them manually in our DPS. The script produces a base64 string of the ASN.1 encoded public key. When I use that value in the Endorsement Key Field, I get a Bad Request error with the message : "Endorsement key is invalid, or does not match the Enrollment."
Am I using the wrong format to encode the public key? Is there another easy way to access the TPM public EK formatted as a base64 string?
Here is my script if that's helpful
$tpm = Get-TpmEndorsementKeyInfo -HashAlgorithm sha256
$hexPub = $tpm.PublicKey.Format($true).Replace(' ', '')
$pubBytes = New-Object byte[] -ArgumentList ($hexPub.Length / 2)
for ($i = 0; $i -lt $hexPub.Length; $i += 2) { $pubBytes[$i/2] = [System.Convert]::ToByte($hexPub.Substring($i, 2), 16) }
$pubString = [System.Convert]::ToBase64String($pubBytes)
Thanks so much

get-azurekeyvaultkey does not return "public key"

I am just testing out Azure Key Vault with key/pairs and am attempting to retrieve the public key.
I first created a Key Vault (name = "VaultTest") using Azure portal.
I then created a Key (Name = "TestKey1") again using Azure portal.
I see the key in the portal and when I click on it I see the following information:
Properties:
Key Type: RSA
RSA Key Size 2048
Created: "date time"
Updated: "date time"
Key Identifier: //vault path/keys/TestKey1/Key identifier
Settings:
Set activation date: "unchecked"
Set expiration date: "unchecked"
Enabled: True
Tags "none"
Permitted operations:
Encrypt: true
Decrypt: true
Sign: true
Verify: true
Wrap key: true
Unwrap key: true
Notice that there is no public key information displayed so I switched over to Azure Cloud Shell and executed the following command:
Get-AzureKeyVaultKey -vaultname 'VaultTest' -name 'TestKey1'
It returns VaultName, Name, Version, Id, Enabled, Expires, Not Before, Created, Updated, Purge Disabled and Tags, but no Key.
All the examples I read online (albeit somewhat old) show fields Attributes and Key being returned but those are not returning for me.
I read somewhere that if you call the URI it will return the public key info, so I copy/pasted the URI into a browser but this returns to me:
{"error":{"code":"Unauthorized","message":"Request is missing a Bearer or PoP token."}}
Am I doing something brain dead or has the function get-azurekeyvaultkey changed? If it has changed how does one get the public key information for a specific key stored in Key Vault?
I can reproduce your issue with Get-AzureKeyVaultKey -vaultname 'VaultTest' -name 'TestKey1'.
But actually it returns the Attributes and Key that you want, just pass the | ConvertTo-Json like below.
Get-AzureKeyVaultKey -vaultname 'VaultTest' -name 'TestKey1' | ConvertTo-Json

How to generate a Tink key from a user-provided password

I want to store a keyset, and would like the file to be encrypted with key produced from a user-provided "master password". And of course, at a later point I'd like to, given the same master password, be able to load that keyset by decrypting the file.
It seems that I need an Aead, which I can generate from a KeysetHandle with AeadFactory.getPrimitive(keysetHandle). But how can I produce a KeysetHandle from a "master password"?
(And for the context of this question, getting that key from a Key Management Systems, instead of producing it "out of thin air" from a master password, isn't an option.)
An Aead can be created as follows (here done from Scala):
val password: String = "..."
val aead = {
val messageDigest = MessageDigest.getInstance("SHA-256")
messageDigest.update(password.getBytes(CharsetNames.Utf8))
val key256Bit = messageDigest.digest()
val key128Bit = key256Bit.take(16)
new AesGcmJce(key128Bit)
}
A few comments:
I'd prefer the key to be based on a 32-bit digest, but the cipher picked by Tink in this case throws an exception when provided with a 32-bit key, hence the shortening to a 16-bit key.
It seems shortening the key this way is ok from a hash weakness perspective.

Use Exchange PowerShell to count old emails in inbox folder

Does anyone know of a PowerShell script that uses EWS to look into a user's Exchange 2010 SP3 Inbox and get a count of email messages and the sizes older than 60 days for example? I can't believe no one has been able to do this. I see a lot of people asking how to do this. Help please :-)
There are a few example on this http://gsexdev.blogspot.com.au/2012/10/reporting-on-item-age-count-and-size-in.html and http://gsexdev.blogspot.com.au/2014/07/creating-mailbox-folder-growth-map-with.html
Specifically for what you want to do something like this should work
## Get the Mailbox to Access from the 1st commandline argument
$MailboxName = $args[0]
$DateFrom = (Get-Date).AddDays(-60)
## Load Managed API dll
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"
## Set Exchange Version
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
#Credentials Option 1 using UPN for the windows Account
$psCred = Get-Credential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
#Credentials Option 2
#service.UseDefaultCredentials = $true
## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
## Code From http://poshcode.org/624
## Create a compilation environment
$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=#'
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
'#
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly
## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
## end code from http://poshcode.org/624
## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
"Using CAS Server : " + $Service.url
#CAS URL Option 2 Hardcoded
#$uri=[system.URI] "https://casservername/ews/exchange.asmx"
#$service.Url = $uri
## Optional section for Exchange Impersonation
#$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
# Bind to the Inbox Folder
$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
#Define ItemView to retrive just 1000 Items
$ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(250)
$psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
$psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)
$ivItemView.PropertySet = $psPropset
$sfItemSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived,$DateFrom)
$rptObj = "" | Select Mailbox,TotalNumber,TotalSize
$rptObj.Mailbox = $MailboxName
$rptObj.TotalNumber = 0
$rptObj.TotalSize = [Int64]0
$fiItems = $null
do{
$fiItems = $service.FindItems($Inbox.Id,$sfItemSearchFilter,$ivItemView)
#[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)
foreach($Item in $fiItems.Items){
$rptObj.TotalNumber += 1
$rptObj.TotalSize += [Int64]$Item.Size
}
$ivItemView.Offset += $fiItems.Items.Count
}while($fiItems.MoreAvailable -eq $true)
$rptObj | select Mailbox,TotalNumber,#{label="TotalSize(MB)";expression={[math]::Round($_.TotalSize/1MB,2)}}

Why does this twitter oauth API token request fail

[Note: all oauth tokens/secrets below were created randomly; they are
NOT my actual tokens/secrets]
curl -o /tmp/test.txt 'https://api.twitter.com/oauth/request_token?
oauth_timestamp=1345141469&
consumer_key=UEIUyoBjBRomdvrVcUTn&oauth_access_token_secret=YePiEkSDFdYAOgscijMCazcSfBflykjsEyaaVbuJeO&oauth_access_token=47849378%2drZlzmwutYqGypbLsQUoZUsGdDkVVRkjkOkSfikNZC&oauth_nonce=1345141469&
consumer_secret=rUOeZMYraAapKmXqYpxNLTOuGNmAQbGFqUEpPRlW&
oauth_version=1%2e0&
oauth_signature_method=HMAC%2dSHA1&oauth_signature=H0KLLecZNAAz%2bXoyrPRiUs37X3Zz%2bAcabMa5M4oDLkM'
[I added newlines for clarity; actual command is one single line]
Assuming all the other data is valid, why does the command above yield
"Failed to validate oauth signature and token" (even when I use my
real data)?
In particular, is my signature
"H0KLLecZNAAz%2bXoyrPRiUs37X3Zz%2bAcabMa5M4oDLkM" invalid, or am I
doing something more fundamentally wrong.
The program I used to generate this:
#!/bin/perl
use Digest::SHA;
%twitter_auth_hash = (
"oauth_access_token" => "47849378-rZlzmwutYqGypbLsQUoZUsGdDkVVRkjkOkSfikNZC",
"oauth_access_token_secret" => "YePiEkSDFdYAOgscijMCazcSfBflykjsEyaaVbuJeO",
"consumer_key" => "UEIUyoBjBRomdvrVcUTn",
"consumer_secret" => "rUOeZMYraAapKmXqYpxNLTOuGNmAQbGFqUEpPRlW"
);
# if uncommented, pull my actual data
# require "bc-private.pl";
$twitter_auth_hash{"oauth_signature_method"} = "HMAC-SHA1";
$twitter_auth_hash{"oauth_version"} = "1.0";
$twitter_auth_hash{"oauth_timestamp"} = time();
$twitter_auth_hash{"oauth_nonce"} = time();
for $i (keys %twitter_auth_hash) {
push(#str,"$i=".urlencode($twitter_auth_hash{$i}));
}
$str = join("&",#str);
# thing to sign
$url = "GET $str";
# signing it
$sig = urlencode(Digest::SHA::hmac_sha256_base64($url, "rUOeZMYraAapKmXqYpxNLTOuGNmAQbGFqUEpPRlW&YePiEkSDFdYAOgscijMCazcSfBflykjsEyaaVbuJeO"));
# full URL incl sig
$furl = "https://api.twitter.com/oauth/request_token?$str&oauth_signature=$sig";
# system("curl -o /tmp/testing.txt '$furl'");
print "FURL: $furl\n";
print "STR: $str\n";
print "SIG: $sig\n";
sub urlencode {
my($str) = #_;
$str=~s/([^a-zA-Z0-9])/"%".unpack("H2",$1)/iseg;
$str=~s/ /\+/isg;
return $str;
}
Note: I realize there are many other possible reasons this is failing,
but current question is: am I sending the parameters correctly and am
I computing the signature correctly.
Twitter asks that you do a POST for the request token.

Resources