Last active
August 29, 2015 14:21
-
-
Save gpduck/1ab2a5effb886a3bab56 to your computer and use it in GitHub Desktop.
Adds CmsMessage commands that should be mostly compatible with the ones provided in v5 to down-level PowerShell.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
Copyright 2015 Chris Duck | |
Licensed under the Apache License, Version 2.0 (the "License"); | |
you may not use this file except in compliance with the License. | |
You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
#> | |
if(!(Get-Command -Name Protect-CmsMessage -Module Microsoft.PowerShell.Security -ErrorAction SilentlyContinue)) { | |
Add-Type -AssemblyName System.Security | |
<# | |
.PARAMETER Content | |
The message encrypted with Protect-CmsMessage that should be decrypted. | |
.PARAMETER LiteralPath | |
A literal path to a file containing a message encrypted with Protect-CmsMessage. | |
.PARAMETER Path | |
A path to a file containing a message encrypted with Protect-CmsMessage. | |
.PARAMETER To | |
A list of recipient certificates to use to encrypt the message. This can be in the format of a certificate thumbprint or a wildcard match of the subject name. | |
The following values for this parameter are allowed in v5, but not supported by this implementation: | |
* base64 encoded certificate | |
* path to certificate file | |
* path to certificate folder | |
.EXAMPLE | |
c:\> Protect-CmsMessage -Content "Secret Message" -To "*MyCertificate" -OutFile "c:\cmsmessage.txt" | |
c:\> Unprotect-CmsMessage -Path "c:\cmsmessage.txt" | |
Secret Message | |
#> | |
function Unprotect-CmsMessage { | |
param( | |
[Parameter(Mandatory=$true,ParameterSetName="ByContent",Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] | |
[String]$Content, | |
[Parameter(Mandatory=$true,ParameterSetName="ByLiteralPath",Position=0)] | |
$LiteralPath, | |
[Parameter(Mandatory=$true,ParameterSetName="ByPath",Position=0)] | |
$Path, | |
[Parameter(ParameterSetName="ByContent",Position=1)] | |
[Parameter(ParameterSetName="ByLiteralPath",Position=1)] | |
[Parameter(ParameterSetName="ByPath",Position=1)] | |
[String[]]$To | |
) | |
switch($PSCmdlet.ParameterSetName) { | |
"ByLiteralPath" { | |
$Content = [System.IO.File]::ReadAllText( (Resolve-Path -LiteralPath $LiteralPath) ) | |
} | |
"ByPath" { | |
$Content = [System.IO.File]::ReadAllText( (Resolve-Path -Path $Path) ) | |
} | |
} | |
if($Content -match '(?sm)-----BEGIN CMS-----\r\n(.*)\r\n-----END CMS-----') { | |
$EncodedContent = $matches[1] | |
$ContentBytes = [Convert]::FromBase64String($EncodedContent) | |
$EnvelopedCms = New-Object System.Security.Cryptography.Pkcs.EnvelopedCms | |
$EnvelopedCms.Decode($ContentBytes) | |
if($To) { | |
$To | %{ | |
$Cert = Find-RecipientCert -To $_ | |
$Recipient = $EnvelopedCms.RecipientInfos | ?{$_.RecipientIdentifier.Value.SerialNumber -eq $Cert.SerialNumber} | |
$EnvelopedCms.Decrypt($Recipient) | |
} | |
} else { | |
$EnvelopedCms.Decrypt() | |
} | |
[Text.Encoding]::ASCII.GetString($EnvelopedCms.Encode()) | |
} else { | |
Write-Error "The message does not appear to be a CMS message" | |
return | |
} | |
} | |
Export-ModuleMember -Function Unprotect-CmsMessage | |
<# | |
.PARAMETER Content | |
The message encrypted with Protect-CmsMessage that should be decrypted. | |
.PARAMETER LiteralPath | |
A literal path to a file containing a message encrypted with Protect-CmsMessage. | |
.PARAMETER Path | |
A path to a file containing a message encrypted with Protect-CmsMessage. | |
.PARAMETER To | |
A list of recipient certificates to use to encrypt the message. This can be in the format of a certificate thumbprint or a wildcard match of the subject name. | |
The following values for this parameter are allowed in v5, but not supported by this implementation: | |
* base64 encoded certificate | |
* path to certificate file | |
* path to certificate folder | |
.PARAMETER OutFile | |
The path of the file the protected message should be written to. | |
.EXAMPLE | |
c:\> Protect-CmsMessage -Content "Secret Message" -To "*MyCertificate" -OutFile "c:\cmsmessage.txt" | |
c:\> Unprotect-CmsMessage -Path "c:\cmsmessage.txt" | |
Secret Message | |
#> | |
function Protect-CmsMessage { | |
param( | |
[Parameter(Mandatory=$true,ParameterSetName="ByContent",Position=0)] | |
[Parameter(Mandatory=$true,ParameterSetName="ByLiteralPath",Position=0)] | |
[Parameter(Mandatory=$true,ParameterSetName="ByPath",Position=0)] | |
[String[]]$To, | |
[Parameter(Mandatory=$true,ParameterSetName="ByContent",Position=1,ValueFromPipeline=$true)] | |
$Content, | |
[Parameter(Mandatory=$true,ParameterSetName="ByLiteralPath",Position=1)] | |
$LiteralPath, | |
[Parameter(Mandatory=$true,ParameterSetName="ByPath",Position=1)] | |
$Path, | |
[Parameter(ParameterSetName="ByContent",Position=2)] | |
[Parameter(ParameterSetName="ByLiteralPath",Position=2)] | |
[Parameter(ParameterSetName="ByPath",Position=2)] | |
$OutFile | |
) | |
$Recipients = @() | |
$To | %{ | |
$Recipient = $_ | |
$Cert = Find-RecipientCert -To $Recipient | |
if($Cert) { | |
$Recipients += New-Object System.Security.Cryptography.Pkcs.CmsRecipient("IssuerAndSerialNumber", $Cert) | |
} else { | |
throw "Unable to locate a certificate for $Recipient" | |
} | |
} | |
switch($PSCmdlet.ParameterSetName) { | |
"ByLiteralPath" { | |
$Content = [System.IO.File]::ReadAllText( (Resolve-Path -LiteralPath $LiteralPath) ) | |
} | |
"ByPath" { | |
$Content = [System.IO.File]::ReadAllText( (Resolve-Path -Path $Path) ) | |
} | |
} | |
$ContentInfo = New-Object System.Security.Cryptography.Pkcs.ContentInfo(,[Text.Encoding]::ASCII.GetBytes($Content)) | |
$EnvelopedCms = New-Object System.Security.Cryptography.Pkcs.EnvelopedCms($ContentInfo) | |
$Recipients | %{ | |
$EnvelopedCms.Encrypt($_) | |
} | |
$MessageBase64 = [Convert]::ToBase64String($EnvelopedCms.Encode()) | |
$CmsMessageSB = New-Object System.Text.StringBuilder | |
$CmsMessageSB.AppendLine("-----BEGIN CMS-----") > $null | |
$LineLength = 76 | |
$StringPos = 0 | |
while($StringPos -lt $MessageBase64.Length) { | |
if($StringPos + $LineLength -gt $MessageBase64.Length) { | |
$SubstringLength = $MessageBase64.Length - $StringPos | |
} else { | |
$SubstringLength = $LineLength | |
} | |
$CmsMessageSB.AppendLine($MessageBase64.Substring($StringPos, $SubstringLength)) > $null | |
$StringPos += $SubstringLength | |
} | |
$CmsMessageSB.AppendLine("-----END CMS-----") > $null | |
if($OutFile) { | |
Set-Content -Path $OutFile -Value $CmsMessageSB.ToString() | |
} else { | |
$CmsMessageSB.ToString() | |
} | |
} | |
Export-ModuleMember -Function Protect-CmsMessage | |
<# | |
#> | |
function Get-CmsMessage { | |
param( | |
[Parameter(Mandatory=$true,ParameterSetName="ByContent",ValueFromPipeline=$true)] | |
$Content, | |
[Parameter(Mandatory=$true,ParameterSetName="ByLiteralPath")] | |
$LiteralPath, | |
[Parameter(Mandatory=$true,ParameterSetName="ByPath")] | |
$Path | |
) | |
switch($PSCmdlet.ParameterSetName) { | |
"ByLiteralPath" { | |
$Content = [System.IO.File]::ReadAllText( (Resolve-Path -LiteralPath $LiteralPath) ) | |
} | |
"ByPath" { | |
$Content = [System.IO.File]::ReadAllText( (Resolve-Path -Path $Path) ) | |
} | |
} | |
if($Content -match '(?sm)-----BEGIN CMS-----\r\n(.*)\r\n-----END CMS-----') { | |
$EncodedContent = $matches[1] | |
$ContentBytes = [Convert]::FromBase64String($EncodedContent) | |
$EnvelopedCms = New-Object System.Security.Cryptography.Pkcs.EnvelopedCms | |
$EnvelopedCms.Decode($ContentBytes) | |
$EnvelopedCms | Add-Member -MemberType NoteProperty -Name Recipients -Value (@($EnvelopedCms.RecipientInfos.RecipientIdentifier.Value.IssuerName)) | |
$EnvelopedCms | Add-Member -MemberType NoteProperty -Name Content -Value $Content | |
$EnvelopedCms | |
} else { | |
Write-Error "The message does not appear to be a CMS message" | |
return | |
} | |
} | |
Export-ModuleMember -Function Get-CmsMessage | |
<# | |
Internal function to locate certificates in the current user's store. | |
#> | |
function Find-RecipientCert { | |
param( | |
$To | |
) | |
dir cert:\CurrentUser\My | ?{ | |
$EnhancedKeyUsages = $_.Extensions | ?{$_.Oid.Value -eq "2.5.29.37"} | |
($EnhancedKeyUsages.EnhancedKeyUsages | %{$_.Value}) -contains "1.3.6.1.4.1.311.80.1" -and | |
($_.Thumbprint -eq $To -or $_.Subject -like $To) | |
} | select -First 1 | |
} | |
} else { | |
Import-Module -Name Microsoft.PowerShell.Security | |
Export-ModuleMember -Cmdlet Get-CmsMessage,Protect-CmsMessage,Unprotect-CmsMessage | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment