Last active
January 6, 2020 17:07
-
-
Save michaellwest/fb05ee2521865e856d8062a9189bd7ec to your computer and use it in GitHub Desktop.
Convert the certificate private key to a PKCS8 formatted file. Made use of this for Traefik.
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
. $PSScriptRoot\rsakeytools.ps1 | |
$loadedCertificate = Get-Item -Path "Cert:\CurrentUser\My\2B127E82FA3E0247B40AA5F75707656EE37D5A46" | |
$parameters = $loadedCertificate.PrivateKey.ExportParameters($true) | |
$data = [RSAKeyUtils]::PrivateKeyToPKCS8($parameters) | |
$content = @( | |
'-----BEGIN PRIVATE KEY-----' | |
[System.Convert]::ToBase64String($data, 'InsertLineBreaks') | |
'-----END PRIVATE KEY-----' | |
) | |
$content |
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
<# | |
.SYNPOSIS | |
Convert a PrivateKey from the certificate store into a PKCS8 formatted file. | |
.LINK | |
Found C# version here https://gist.github.com/chenrui1988/6b104a010172786dbcbc0aafc466d291/ | |
.NOTES | |
Michael West | |
#> | |
class RSAKeyUtils | |
{ | |
static [byte[]] PrivateKeyToPKCS8([System.Security.Cryptography.RSAParameters]$privateKey) | |
{ | |
[AsnType]$n = [RSAKeyUtils]::CreateIntegerPos($privateKey.Modulus) | |
[AsnType]$e = [RSAKeyUtils]::CreateIntegerPos($privateKey.Exponent) | |
[AsnType]$d = [RSAKeyUtils]::CreateIntegerPos($privateKey.D) | |
[AsnType]$p = [RSAKeyUtils]::CreateIntegerPos($privateKey.P) | |
[AsnType]$q = [RSAKeyUtils]::CreateIntegerPos($privateKey.Q) | |
[AsnType]$dp = [RSAKeyUtils]::CreateIntegerPos($privateKey.DP) | |
[AsnType]$dq = [RSAKeyUtils]::CreateIntegerPos($privateKey.DQ) | |
[AsnType]$iq = [RSAKeyUtils]::CreateIntegerPos($privateKey.InverseQ) | |
[AsnType]$version = [RSAKeyUtils]::CreateInteger(@(0)) | |
[AsnType]$key = [RSAKeyUtils]::CreateOctetString([RSAKeyUtils]::CreateSequence(@($version,$n,$e,$d,$p,$q,$dp,$dq,$iq))) | |
[AsnType]$algorithmID = [RSAKeyUtils]::CreateSequence(@([RSAKeyUtils]::CreateOid("1.2.840.113549.1.1.1"),[RSAKeyUtils]::CreateNull())) | |
[AsnType]$privateKeyInfo = [RSAKeyUtils]::CreateSequence(@($version,$algorithmID,$key)) | |
return (New-Object -TypeName AsnMessage -ArgumentList $privateKeyInfo.GetBytes(),"PKCS#8").GetBytes() | |
} | |
static [byte[]] PrivateKeyToPKCS8([byte[]]$privkey) | |
{ | |
[System.Security.Cryptography.RSAParameters]$RSAParam = [RSAKeyUtils]::DecodeRSAPrivateKeyToRSAParam($privkey) | |
[AsnType]$n = [RSAKeyUtils]::CreateIntegerPos($RSAParam.Modulus) | |
[AsnType]$e = [RSAKeyUtils]::CreateIntegerPos($RSAParam.Exponent) | |
[AsnType]$d = [RSAKeyUtils]::CreateIntegerPos($RSAParam.D) | |
[AsnType]$p = [RSAKeyUtils]::CreateIntegerPos($RSAParam.P) | |
[AsnType]$q = [RSAKeyUtils]::CreateIntegerPos($RSAParam.Q) | |
[AsnType]$dp = [RSAKeyUtils]::CreateIntegerPos($RSAParam.DP) | |
[AsnType]$dq = [RSAKeyUtils]::CreateIntegerPos($RSAParam.DQ) | |
[AsnType]$iq = [RSAKeyUtils]::CreateIntegerPos($RSAParam.InverseQ) | |
[AsnType]$version = [RSAKeyUtils]::CreateInteger(@(0)) | |
[AsnType]$key = [RSAKeyUtils]::CreateOctetString([RSAKeyUtils]::CreateSequence(@($version,$n,$e,$d,$p,$q,$dp,$dq,$iq))) | |
[AsnType]$algorithmID = [RSAKeyUtils]::CreateSequence(@([RSAKeyUtils]::CreateOid("1.2.840.113549.1.1.1"),[RSAKeyUtils]::CreateNull())) | |
[AsnType]$privateKeyInfo = [RSAKeyUtils]::CreateSequence(@($version,$algorithmID,$key)) | |
return (New-Object -TypeName AsnMessage -ArgumentList $privateKeyInfo.GetBytes(),"PKCS#8").GetBytes() | |
} | |
static [System.Security.Cryptography.RSAParameters] DecodeRSAPrivateKeyToRSAParam([byte[]]$privkey) | |
{ | |
[System.Security.Cryptography.RSAParameters]$RSAparams = (New-Object -TypeName RSAParameters) | |
[byte[]]$MODULUS = [byte[]] | |
[byte[]]$E = [byte[]] | |
[byte[]]$D = [byte[]] | |
[byte[]]$P = [byte[]] | |
[byte[]]$Q = [byte[]] | |
[byte[]]$DP = [byte[]] | |
[byte[]]$DQ = [byte[]] | |
[byte[]]$IQ = [byte[]] | |
[System.IO.MemoryStream]$mem = (New-Object -TypeName MemoryStream -ArgumentList $privkey) | |
[System.IO.BinaryReader]$binr = (New-Object -TypeName BinaryReader -ArgumentList $mem) | |
[byte]$bt = 0 | |
[uint16]$twobytes = 0 | |
[int]$elems = 0 | |
try | |
{ | |
$twobytes = $binr.ReadUInt16() | |
if ($twobytes -eq 0x8130) | |
{ | |
$binr.ReadByte() | |
} | |
elseif ($twobytes -eq 0x8230) | |
{ | |
$binr.ReadInt16() | |
} | |
else | |
{ | |
return (New-Object -TypeName RSAParameters) | |
} | |
$twobytes = $binr.ReadUInt16() | |
if ($twobytes -ne 0x0102) | |
{ | |
return (New-Object -TypeName RSAParameters) | |
} | |
$bt = $binr.ReadByte() | |
if ($bt -ne 0x00) | |
{ | |
return (New-Object -TypeName RSAParameters) | |
} | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$MODULUS = $binr.ReadBytes($elems) | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$E = $binr.ReadBytes($elems) | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$D = $binr.ReadBytes($elems) | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$P = $binr.ReadBytes($elems) | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$Q = $binr.ReadBytes($elems) | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$DP = $binr.ReadBytes($elems) | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$DQ = $binr.ReadBytes($elems) | |
$elems = [RSAKeyUtils]::GetIntegerSize($binr) | |
$IQ = $binr.ReadBytes($elems) | |
$RSAparams.Modulus = $MODULUS | |
$RSAparams.Exponent = $E | |
$RSAparams.D = $D | |
$RSAparams.P = $P | |
$RSAparams.Q = $Q | |
$RSAparams.DP = $DP | |
$RSAparams.DQ = $DQ | |
$RSAparams.InverseQ = $IQ | |
return $RSAparams | |
} | |
catch [Exception] | |
{ | |
return $RSAparams | |
} | |
finally | |
{ | |
$binr.Close() | |
} | |
} | |
static [int] GetIntegerSize([System.IO.BinaryReader]$binr) | |
{ | |
[byte]$bt = 0 | |
[byte]$lowbyte = 0x00 | |
[byte]$highbyte = 0x00 | |
[int]$count = 0 | |
$bt = $binr.ReadByte() | |
if ($bt -ne 0x02) | |
{ | |
return 0 | |
} | |
$bt = $binr.ReadByte() | |
if ($bt -eq 0x81) | |
{ | |
$count = $binr.ReadByte() | |
} | |
elseif ($bt -eq 0x82) | |
{ | |
$highbyte = $binr.ReadByte() | |
$lowbyte = $binr.ReadByte() | |
[byte[]]$modint = [byte[]]::CreateInstance([byte], 4) | |
$modint[0] = $lowbyte | |
$modint[1] = $highbyte | |
$modint[2] = 0x00 | |
$modint[3] = 0x00 | |
$count = [BitConverter]::ToInt32($modint,0) | |
} | |
else | |
{ | |
$count = $bt | |
} | |
while ($binr.ReadByte() -eq 0x00) | |
{ | |
$count = 1 | |
} | |
$binr.BaseStream.Seek(-1,[System.IO.SeekOrigin]::Current) | |
return $count | |
} | |
static [AsnType] CreateOctetString([AsnType]$value) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($value)) | |
{ | |
return (New-Object -TypeName AsnType -ArgumentList ([byte]0x04,[byte[]]@(0x00))) | |
} | |
return (New-Object -TypeName AsnType -ArgumentList ([byte]0x04,[byte[]]@($value.GetBytes()))) | |
} | |
static [bool] IsEmpty([byte[]]$octets) | |
{ | |
if ($null -eq $octets -or 0 -eq $octets.Length) | |
{ | |
return $true | |
} | |
return $false | |
} | |
static [bool] IsEmpty([String]$s) | |
{ | |
if ($null -eq $s -or 0 -eq $s.Length) | |
{ | |
return $true | |
} | |
return $false | |
} | |
static [bool] IsEmpty([String[]]$strings) | |
{ | |
if ($null -eq $strings -or 0 -eq $strings.Length) | |
{ | |
return $true | |
} | |
return $false | |
} | |
static [bool] IsEmpty([AsnType]$value) | |
{ | |
if ($null -eq $value) | |
{ | |
return $true | |
} | |
return $false | |
} | |
static [bool] IsEmpty([AsnType[]]$values) | |
{ | |
if ($null -eq $values -or 0 -eq $values.Length) | |
{ | |
return $true | |
} | |
return $false | |
} | |
static [bool] IsEmpty([byte[][]]$arrays) | |
{ | |
if ($null -eq $arrays -or 0 -eq $arrays.Length) | |
{ | |
return $true | |
} | |
return $false | |
} | |
static [AsnType] CreateInteger([byte[]]$value) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($value)) | |
{ | |
$zero = [byte[]]::CreateInstance([byte], 1) | |
$zero[0] = 0 | |
return [RSAKeyUtils]::CreateInteger($zero) | |
} | |
return (New-Object -TypeName AsnType -ArgumentList ([byte]0x02,[byte[]]$value)) | |
} | |
static [AsnType] CreateNull() | |
{ | |
return (New-Object -TypeName AsnType -ArgumentList ([byte]0x05,[byte[]]@(0x00))) | |
} | |
static [byte[]] Duplicate([byte[]]$b) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($b)) | |
{ | |
$empty = [byte[]]::CreateInstance([byte], 0) | |
return $empty | |
} | |
[byte[]]$d = [byte[]]::CreateInstance([byte], $b.Length) | |
[Array]::Copy($b,$d,$b.Length) | |
return $d | |
} | |
static [AsnType] CreateIntegerPos([byte[]]$value) | |
{ | |
[byte[]]$i = $null | |
$d = [RSAKeyUtils]::Duplicate($value) | |
if ([RSAKeyUtils]::IsEmpty($d)) | |
{ | |
$zero = [byte[]]::CreateInstance([byte], 1) | |
$zero[0] = 0 | |
$d = $zero | |
} | |
if ($d.Length -gt 0 -and $d[0] -gt 0x7F) | |
{ | |
$i = [byte[]]::CreateInstance([byte], $d.Length + 1) | |
$i[0] = 0x00 | |
[Array]::Copy($d,0,$i,1,$value.Length) | |
} | |
else | |
{ | |
$i = $d | |
} | |
return [RSAKeyUtils]::CreateInteger($i) | |
} | |
static [byte[]] Concatenate([AsnType[]]$values) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($values)) | |
{ | |
return [byte[]]::CreateInstance([byte], 0) | |
} | |
[int]$length = 0 | |
foreach ($t in $values) | |
{ | |
if ($null -ne $t) | |
{ | |
$length += $t.GetBytes().Length | |
} | |
} | |
[byte[]]$cated = [byte[]]::CreateInstance([byte], $length) | |
[int]$current = 0 | |
foreach ($t in $values) | |
{ | |
if ($null -ne $t) | |
{ | |
[byte[]]$b = $t.GetBytes() | |
[Array]::Copy($b,0,$cated,$current,$b.Length) | |
$current += $b.Length | |
} | |
} | |
return $cated | |
} | |
static [AsnType] CreateSequence([AsnType[]]$values) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($values)) | |
{ | |
throw (New-Object -TypeName ArgumentException -ArgumentList "A sequence requires at least one value.") | |
} | |
[byte[]]$octets = [byte[]][RSAKeyUtils]::Concatenate($values) | |
return (New-Object -TypeName AsnType -ArgumentList ([byte](0x10 -bor 0x20)), ([byte[]]@($octets))) | |
} | |
static [AsnType] CreateOid([String]$value) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($value)) | |
{ | |
return $null | |
} | |
[String[]]$tokens = $value.Split(@(' ','.')) | |
if ([RSAKeyUtils]::IsEmpty($tokens)) | |
{ | |
return $null | |
} | |
[UInt64]$a = 0 | |
[System.Collections.Generic.List[UInt64]]$arcs = (New-Object -TypeName System.Collections.Generic.List[UInt64]) | |
foreach ($t in $tokens) | |
{ | |
if ($t.Length -eq 0) | |
{ | |
break | |
} | |
try | |
{ | |
$a = [Convert]::ToUInt64($t,[CultureInfo]::InvariantCulture) | |
} | |
catch [FormatException] | |
{ | |
break | |
} | |
catch [OverflowException] | |
{ | |
break | |
} | |
$arcs.Add($a) > $null | |
} | |
if (0 -eq $arcs.Count) | |
{ | |
return $null | |
} | |
[System.Collections.Generic.List[byte]]$octets = (New-Object -TypeName System.Collections.Generic.List[byte]) | |
if ($arcs.Count -ge 1) | |
{ | |
$a = $arcs[0] * 40 | |
} | |
if ($arcs.Count -ge 2) | |
{ | |
$a += $arcs[1] | |
} | |
$octets.Add([byte]($a)) | |
for([int]$i = 2; $i -lt $arcs.Count; $i++) | |
{ | |
[System.Collections.Generic.List[byte]]$temp = (New-Object -TypeName System.Collections.Generic.List[byte]) | |
[UInt64]$arc = $arcs[$i] | |
do { | |
$temp.Add(([byte]0x80 -bor ($arc -band 0x7F))) > $null | |
$arc = $arc -shr 7 | |
} while (0 -ne $arc) | |
[byte[]]$t = $temp.ToArray() | |
$t[0] = [byte](0x7F -band $t[0]) | |
[Array]::Reverse($t) | |
foreach ($b in $t) | |
{ | |
$octets.Add($b) > $null | |
} | |
} | |
return [RSAKeyUtils]::CreateOid($octets.ToArray()) | |
} | |
static [AsnType] CreateOid([byte[]]$value) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($value)) | |
{ | |
return $null | |
} | |
return (New-Object -TypeName AsnType -ArgumentList 0x06,$value) | |
} | |
} | |
class AsnMessage | |
{ | |
[byte[]] hidden $Octets | |
[String] hidden $m_format | |
[int] $Length | |
AsnMessage ([byte[]]$octets,[String]$format) | |
{ | |
$this.Octets = $octets | |
$this.m_format = $format | |
} | |
[byte[]] GetBytes() | |
{ | |
if ($null -eq $this.Octets) | |
{ | |
return [byte[]]::CreateInstance([byte], 0) | |
} | |
return $this.Octets | |
} | |
[String] GetFormat() | |
{ | |
return $this.m_format | |
} | |
} | |
class AsnType | |
{ | |
AsnType ([byte]$tag,[byte[]]$octets) | |
{ | |
$this.Tag = @($tag) | |
$this.Octets = $octets | |
$this.Length = [byte[]]::CreateInstance([byte], 0) | |
} | |
[byte[]] $Tag | |
[byte[]] $Length | |
[byte[]] $Octets | |
[byte[]] GetBytes() | |
{ | |
$this.SetLength() | |
if (0x05 -eq $this.Tag[0]) | |
{ | |
return $this.Concatenate([byte[][]]@($this.Tag,$this.Octets)) | |
} | |
$val = [byte[][]]@($this.Tag,$this.Length,$this.Octets) | |
return $this.Concatenate([byte[][]]@($this.Tag,$this.Length,$this.Octets)) | |
} | |
[void] SetLength() | |
{ | |
if ($null -eq $this.Octets) | |
{ | |
$zero = [byte[]]::CreateInstance([byte], 1) | |
$zero[0] = 0 | |
$this.Length = $zero | |
return | |
} | |
if (0x05 -eq $this.Tag[0]) | |
{ | |
$empty = [byte[]]::CreateInstance([byte], 0) | |
$this.Length = $empty | |
return | |
} | |
[byte[]]$len = $null | |
if ($this.Octets.Length -lt 0x80) | |
{ | |
$len= [byte[]]::CreateInstance([byte], 1) | |
$len[0] = [byte]$this.Octets.Length | |
} | |
elseif ($this.Octets.Length -le 0xFF) | |
{ | |
$len = [byte[]]::CreateInstance([byte], 2) | |
$len[0] = 0x81 | |
$len[1] = [byte](($this.Octets.Length -band 0xFF)) | |
} | |
elseif ($this.Octets.Length -le 0xFFFF) | |
{ | |
$len = [byte[]]::CreateInstance([byte], 3) | |
$len[0] = 0x82 | |
$len[1] = [byte](($this.Octets.Length -band 0xFF00) -shr 8) | |
$len[2] = [byte](($this.Octets.Length -band 0xFF)) | |
} | |
elseif ($this.Octets.Length -le 0xFFFFFF) | |
{ | |
$len = [byte[]]::CreateInstance([byte], 4) | |
$len[0] = 0x83 | |
$len[1] = [byte](($this.Octets.Length -band 0xFF0000) -shr 16) | |
$len[2] = [byte](($this.Octets.Length -band 0xFF00) -shr 8) | |
$len[3] = [byte](($this.Octets.Length -band 0xFF)) | |
} | |
else | |
{ | |
$len = [byte[]]::CreateInstance([byte], 5) | |
$len[0] = 0x84 | |
$len[1] = [byte](($this.Octets.Length -band 0xFF000000) -shr 24) | |
$len[2] = [byte](($this.Octets.Length -band 0xFF0000) -shr 16) | |
$len[3] = [byte](($this.Octets.Length -band 0xFF00) -shr 8) | |
$len[4] = [byte](($this.Octets.Length -band 0xFF)) | |
} | |
$this.Length = $len | |
} | |
[byte[]] Concatenate([byte[][]]$values) | |
{ | |
if ([RSAKeyUtils]::IsEmpty($values)) | |
{ | |
return [byte[]] | |
} | |
[int]$len = 0 | |
foreach ($b in $values) | |
{ | |
if ($null -ne $b) | |
{ | |
$len += $b.Length | |
} | |
} | |
[byte[]]$cated = [byte[]]::CreateInstance([byte], $len) | |
[int]$current = 0 | |
foreach ($b in $values) | |
{ | |
if ($null -ne $b) | |
{ | |
[Array]::Copy($b,0,$cated,$current,$b.Length) | |
$current += $b.Length | |
} | |
} | |
return $cated | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment