Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Last active April 14, 2022 04:55
Show Gist options
  • Save Jaykul/8c65f10c0f09dd8e296365b70c48b577 to your computer and use it in GitHub Desktop.
Save Jaykul/8c65f10c0f09dd8e296365b70c48b577 to your computer and use it in GitHub Desktop.
Working with RSA MachineKeys
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