Last active
April 14, 2022 04:55
-
-
Save Jaykul/8c65f10c0f09dd8e296365b70c48b577 to your computer and use it in GitHub Desktop.
Working with RSA MachineKeys
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
using namespace System.Security.Principal | |
using namespace System.Security.Cryptography | |
using namespace System.Security.AccessControl | |
using namespace System.Security.AccessControl.AccessRule | |
Set-Variable -Scope Script -Name RSA_KEY_LENGTH -Value 2048 -Option Constant -ErrorAction SilentlyContinue | |
function Add-RsaKey | |
{ | |
[CmdletBinding()] | |
param( | |
[Parameter(ValueFromPipeline,Mandatory)] | |
[string[]]$KeyName, | |
[switch]$Passthru | |
) | |
begin | |
{ | |
$MyCommand = $PSCmdlet.MyInvocation | Add-Member -Name ToString -Type ScriptMethod -Value { "TRACE " + $this.MyCommand.Name + (($This.BoundParameters.GetEnumerator() | Format-Table -HideTableHeaders | Out-String -stream) -replace "\s+$" -replace "\s+\s", " " -join " -" -replace "[\s-]+$" ) } -Force -PassThru | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Begin")) | |
} | |
end | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "End")) | |
} | |
process | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Process")) | |
foreach($Key in $KeyName) | |
{ | |
# Configure parameters to create the key | |
$keyParams = [CspParameters]::new() | |
$keyParams.KeyContainerName = $Key | |
$keyParams.Flags = $keyParams.Flags -bor [CspProviderFlags]::UseMachineKeyStore | |
# Create the key | |
[RSACryptoServiceProvider]::new($RSA_KEY_LENGTH, $keyParams).Clear(); | |
# Grant-RsaKeyAccess -KeyName $Key -Identity "BuiltInAdministrators" | |
if($Passthru) | |
{ | |
Export-RsaKey -KeyName $KeyName -IncludePrivate | |
} | |
} | |
Write-Information ("Exit {0}" -f $PSCmdlet.MyInvocation.InvocationName) | |
} | |
} | |
function Grant-RsaKeyAccess | |
{ | |
[CmdletBinding()] | |
param( | |
[Parameter(ValueFromPipeline,Mandatory,ParameterSetName="FromProvider")] | |
[System.Security.Cryptography.RSACryptoServiceProvider]$InputObject, | |
[Parameter(ValueFromPipeline,Mandatory,ParameterSetName="FromName")] | |
[string]$KeyName, | |
$Identity = "BuiltInAdministrators" | |
) | |
begin | |
{ | |
$MyCommand = $PSCmdlet.MyInvocation | Add-Member -Name ToString -Type ScriptMethod -Value { | |
"TRACE " + $this.MyCommand.Name + ( | |
($This.BoundParameters.GetEnumerator() | | |
Format-Table -HideTableHeaders | | |
Out-String -stream) -replace "\s+$" -replace "\s+\s", " " -join " -" -replace "[\s-]+$" ) | |
} -Force -PassThru | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Begin")) | |
} | |
end | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "End")) | |
} | |
process | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Process")) | |
if($KeyName) | |
{ | |
# Parameters to find the key | |
$keyParams = [CspParameters]::new() | |
$keyParams.KeyContainerName = $KeyName | |
$keyParams.Flags = $keyParams.Flags -bor [CspProviderFlags]::UseMachineKeyStore -bor [CspProviderFlags]::UseExistingKey | |
try | |
{ | |
# Load the key | |
$InputObject = [RSACryptoServiceProvider]::new($keyParams) | |
} | |
catch | |
{ | |
$ErrorRecord = [System.Management.Automation.ErrorRecord]::new($_.Exception, "GrantNonExistentKey", "ObjectNotFound", $Key) | |
$PSCmdlet.WriteError($ErrorRecord) | |
continue | |
} | |
# Set the security of the params to be the security of the key | |
$keyParams.CryptoKeySecurity = $InputObject.CspKeyContainerInfo.CryptoKeySecurity | |
} | |
# Support WellKnown SIDs: | |
if($Identity -is [string]) | |
{ | |
$Sid=[WellKnownSidType]::NullSid | |
if([WellKnownSidType]::TryParse($Identity, $true, [ref]$Sid) -or [WellKnownSidType]::TryParse("${Identity}Sid", $true, [ref]$Sid)) | |
{ | |
$Identity = [SecurityIdentifier]::new($Sid, $Null) | |
} | |
elseif($Identity -in "User","CurrentUser") | |
{ | |
$Identity = [System.Security.Principal.SecurityIdentifier][System.Security.Principal.WindowsIdentity]::GetCurrent().User | |
} | |
} | |
try | |
{ | |
# Create new access rules | |
$grantAllow = [CryptoKeyAccessRule]::new($Identity, [CryptoKeyRights]::FullControl, [AccessControlType]::Allow) | |
} | |
catch | |
{ | |
$ErrorRecord = [System.Management.Automation.ErrorRecord]::new($_.Exception, "InvalidIdentity", "InvalidArgument", $Identity) | |
$PSCmdlet.WriteError($ErrorRecord) | |
continue | |
} | |
# Add them to the existing security object for the key | |
$keyParams.CryptoKeySecurity.AddAccessRule($grantAllow) | |
# Must call new here to cause a persist of the new rules | |
$csp = [RSACryptoServiceProvider]::new($keyParams); | |
$csp.Clear(); | |
Write-Information ("Exit {0}" -f $PSCmdlet.MyInvocation.InvocationName) | |
} | |
} | |
function Remove-RsaKey | |
{ | |
[CmdletBinding(SupportsShouldProcess, ConfirmImpact="High")] | |
param( | |
[Parameter(ValueFromPipeline,Mandatory)] | |
[string[]]$KeyName | |
) | |
begin | |
{ | |
$MyCommand = $PSCmdlet.MyInvocation | Add-Member -Name ToString -Type ScriptMethod -Value { | |
"TRACE " + $this.MyCommand.Name + ( | |
($This.BoundParameters.GetEnumerator() | | |
Format-Table -HideTableHeaders | | |
Out-String -stream) -replace "\s+$" -replace "\s+\s", " " -join " -" -replace "[\s-]+$" ) | |
} -Force -PassThru | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Begin")) | |
} | |
end | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "End")) | |
} | |
process | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Process")) | |
foreach($Key in $KeyName) | |
{ | |
$keyParams = [CspParameters]::new() | |
$keyParams.KeyContainerName = $Key | |
$keyParams.Flags = $keyParams.Flags -bor [CspProviderFlags]::UseMachineKeyStore -bor [CspProviderFlags]::UseExistingKey | |
try | |
{ | |
$csp = [RSACryptoServiceProvider]::new($keyParams) | |
} | |
catch | |
{ | |
$ErrorRecord = [System.Management.Automation.ErrorRecord]::new($_.Exception, "RemoveNonExistentKey", "ObjectNotFound", $Key) | |
$PSCmdlet.WriteError($ErrorRecord) | |
continue | |
} | |
if($PSCmdlet.ShouldProcess( "Removed the key '$($csp.CspKeyContainerInfo.KeyContainerName)' from '$($csp.CspKeyContainerInfo.UniqueKeyContainerName)'?", | |
"Remove the key '$($csp.CspKeyContainerInfo.KeyContainerName)' from '$($csp.CspKeyContainerInfo.UniqueKeyContainerName)'?", | |
"Removing RSA Keys from Machine Store" )) | |
{ | |
$csp.PersistKeyInCsp = $false | |
$csp.Clear() | |
} | |
} | |
} | |
} | |
function Get-RsaKey | |
{ | |
[CmdletBinding(DefaultParameterSetName="AsCngKey")] | |
param( | |
[Parameter(ValueFromPipeline,Mandatory)] | |
[string[]]$KeyName, | |
[switch]$IncludePrivate, | |
[Parameter(Mandatory,ParameterSetName="AsCsp")] | |
[Alias("CSP")] | |
[switch]$ReturnCSP, | |
[Parameter(Mandatory,ParameterSetName="AsRsa")] | |
[Alias("RSA")] | |
[switch]$ReturnRSA | |
) | |
begin | |
{ | |
$MyCommand = $PSCmdlet.MyInvocation | Add-Member -Name ToString -Type ScriptMethod -Value { | |
"TRACE " + $this.MyCommand.Name + ( | |
($This.BoundParameters.GetEnumerator() | | |
Format-Table -HideTableHeaders | | |
Out-String -stream) -replace "\s+$" -replace "\s+\s", " " -join " -" -replace "[\s-]+$" ) | |
} -Force -PassThru | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Begin")) | |
} | |
end | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "End")) | |
} | |
process | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Process")) | |
foreach($Key in $KeyName) | |
{ | |
try | |
{ | |
# Load from MachineKeyStore | |
$keyParams = [CspParameters]::new() | |
$keyParams.KeyContainerName = $Key | |
$keyParams.Flags = $keyParams.Flags -bor [CspProviderFlags]::UseMachineKeyStore -bor [CspProviderFlags]::UseExistingKey | |
$csp = [RSACryptoServiceProvider]::new($keyParams) | |
} | |
catch | |
{ | |
$ErrorRecord = [System.Management.Automation.ErrorRecord]::new($_.Exception, "ExportNonExistentKey", "ObjectNotFound", $Key) | |
$PSCmdlet.WriteError($ErrorRecord) | |
continue | |
} | |
if($ReturnCSP) { | |
return $csp | |
} | |
$Rsa = [system.security.cryptography.rsacng]::new() | |
$Rsa.ImportParameters($csp.ExportParameters($IncludePrivate)) | |
if($ReturnRSA) { | |
return $Rsa | |
} | |
$Rsa.Key | |
} | |
} | |
} | |
function Export-RsaKey | |
{ | |
[CmdletBinding()] | |
param( | |
[Parameter(ValueFromPipeline,Mandatory)] | |
[string[]]$KeyName, | |
[switch]$IncludePrivate | |
) | |
begin | |
{ | |
$MyCommand = $PSCmdlet.MyInvocation | Add-Member -Name ToString -Type ScriptMethod -Value { | |
"TRACE " + $this.MyCommand.Name + ( | |
($This.BoundParameters.GetEnumerator() | | |
Format-Table -HideTableHeaders | | |
Out-String -stream) -replace "\s+$" -replace "\s+\s", " " -join " -" -replace "[\s-]+$" ) | |
} -Force -PassThru | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Begin")) | |
} | |
end | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "End")) | |
} | |
process | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Process")) | |
foreach($Key in $KeyName) | |
{ | |
$keyParams = [CspParameters]::new() | |
$keyParams.KeyContainerName = $Key | |
$keyParams.Flags = $keyParams.Flags -bor [CspProviderFlags]::UseMachineKeyStore -bor [CspProviderFlags]::UseExistingKey | |
try | |
{ | |
$csp = [RSACryptoServiceProvider]::new($keyParams) | |
} | |
catch | |
{ | |
$ErrorRecord = [System.Management.Automation.ErrorRecord]::new($_.Exception, "ExportNonExistentKey", "ObjectNotFound", $Key) | |
$PSCmdlet.WriteError($ErrorRecord) | |
continue | |
} | |
$xml = $csp.ToXmlString($IncludePrivate) | |
$csp.Clear() | |
$xml | |
} | |
} | |
} | |
function Import-RsaKey | |
{ | |
[CmdletBinding()] | |
param( | |
[Parameter(ValueFromPipelineByPropertyName,Mandatory)] | |
[string]$KeyName, | |
[Parameter(ValueFromPipelineByPropertyName,Mandatory)] | |
[string]$RsaKeyXml | |
) | |
begin | |
{ | |
$MyCommand = $PSCmdlet.MyInvocation | Add-Member -Name ToString -Type ScriptMethod -Value { | |
"TRACE " + $this.MyCommand.Name + ( | |
($This.BoundParameters.GetEnumerator() | | |
Format-Table -HideTableHeaders | | |
Out-String -stream) -replace "\s+$" -replace "\s+\s", " " -join " -" -replace "[\s-]+$" ) | |
} -Force -PassThru | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Begin")) | |
} | |
end | |
{ | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "End")) | |
} | |
process | |
{ | |
$ErrorActionPreference = "Stop" | |
$PSCmdlet.WriteInformation( $MyCommand, ("Trace", "Process")) | |
$keyParams = [CspParameters]::new() | |
$keyParams.KeyContainerName = $KeyName | |
$keyParams.Flags = $keyParams.Flags -bor [CspProviderFlags]::UseMachineKeyStore | |
$csp = [RSACryptoServiceProvider]::new($keyParams) | |
$csp.FromXmlString($RsaKeyXml) | |
$csp.Clear() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment