internal/AllFunctions.ps1
function ConvertTo-OriginalType { param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] $inputObject, [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)] [ValidateNotNull()] [string] $TypeName ) process { $result = $inputObject -as ([type]$TypeName); if(-not $result) { throw "Could not convert inputObject to $TypeName"; } Write-Verbose "Converted input object to type $TypeName"; return $result; } } function ConvertTo-UrlBase64 { param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName="FromString")] [ValidateNotNullOrEmpty()] [string] $InputText, [Parameter(Mandatory = $true, ParameterSetName="FromByteArray")] [ValidateNotNullOrEmpty()] [byte[]] $InputBytes ) if($PSCmdlet.ParameterSetName -eq "FromString") { $InputBytes = [System.Text.Encoding]::UTF8.GetBytes($InputText); } $encoded = [System.Convert]::ToBase64String($InputBytes); $encoded = $encoded.TrimEnd('='); $encoded = $encoded.Replace('+', '-'); $encoded = $encoded.Replace('/', '_'); return $encoded; } function Export-AcmeObject { param( [Parameter(Mandatory=$true, Position = 0)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter(Mandatory=$true, ValueFromPipeline = $true)] [ValidateNotNull()] $InputObject, [Parameter()] [switch] $Force ) process { $ErrorActionPreference = 'Stop' if((Test-Path $Path) -and -not $Force) { throw "$Path already exists." } Write-Debug "Exporting $($InputObject.GetType()) to $Path" if($Path -like "*.json") { Write-Verbose "Exporting object to JSON file $Path" $InputObject | ConvertTo-Json | Out-File -FilePath $Path -Encoding utf8 -Force:$Force; } else { Write-Verbose "Exporting object to CLIXML file $Path" Export-Clixml $Path -InputObject $InputObject -Force:$Force; } } } function Import-AcmeObject { param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateScript({Test-Path $_})] [string] $Path, [Parameter()] [string] $TypeName ) process { $ErrorActionPreference = 'Stop' if($Path -like "*.json") { Write-Verbose "Importing object from JSON file $Path" $imported = Get-Content $Path -Raw | ConvertFrom-Json; } else { Write-Verbose "Importing object from CLIXML file $Path" $imported = Import-Clixml $Path; } if($TypeName) { $result = $imported | ConvertTo-OriginalType -TypeName $TypeName } else { $result = $imported | ConvertTo-OriginalType } return $result; } } function Invoke-ACMEWebRequest { <# #> [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [Alias("Url")] [uri] $Uri, [Parameter(Position = 1)] [ValidateNotNull()] [string] $JsonBody, [Parameter(Mandatory = $true)] [ValidateSet("GET", "POST", "HEAD")] [string] $Method ) $httpRequest = [System.Net.Http.HttpRequestMessage]::new($Method, $Uri); Write-Verbose "Sending HttpRequest ($Method) to $Uri"; if($Method -in @("POST", "PUT")) { $httpRequest.Content = [System.Net.Http.StringContent]::new($JsonBody, [System.Text.Encoding]::UTF8); $httpRequest.Content.Headers.ContentType = "application/jose+json"; Write-Debug "The content of the request is $JsonBody"; } #TODO: This should possibly swapped out to be something singleton-ish. $httpClient = [System.Net.Http.HttpClient]::new(); $httpResponse = $httpClient.SendAsync($httpRequest).GetAwaiter().GetResult(); $response = $httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult(); if($httpResponse.Content.Headers.ContentType -eq "application/problem+json") { throw "Server returned Problem: $response" } if($httpRequest.StatusCode -lt 500) { $result = [AcmeHttpResponse]::new($httpResponse, $response); return $result; } throw "Unexpected response from server: $($httpResponse.StatusCode), with content:`n$response" return $response; } function Invoke-SignedWebRequest { [CmdletBinding()] [OutputType("AcmeHttpResponse")] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] [string] $Url, [Parameter(Mandatory = $true, Position = 1)] [AcmeState] $State, [Parameter(Mandatory = $true, Position = 2)] [ValidateNotNull()] [object] $Payload, [Parameter()] [switch] $SupressKeyId ) process { $accountKey = $State.GetAccountKey(); $account = $State.GetAccount(); $keyId = $(if($account -and -not $SupressKeyId) { $account.KeyId }); $nonce = $State.GetNonce(); $requestBody = New-SignedMessage -Url $Url -SigningKey $accountKey -KeyId $keyId -Nonce $nonce -Payload $Payload $response = Invoke-AcmeWebRequest $Url $requestBody -Method POST -ErrorAction 'Continue' if($null -ne $response -and $response.NextNonce) { $State.SetNonce($response.NextNonce); } if($response.IsError) { throw $response; } return $response; } } function New-SignedMessage { [CmdletBinding(SupportsShouldProcess=$false)] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( "PSUseShouldProcessForStateChangingFunctions", "", Scope="Function", Target="*")] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] [string] $Url, [Parameter(Mandatory = $true, Position = 1)] [ISigningKey] $SigningKey, [Parameter(Position = 2)] [string] $KeyId, [Parameter(Position = 3)] [string] $Nonce, [Parameter(Mandatory = $true, Position = 4)] [ValidateNotNull()] [object] $Payload ) $headers = @{}; $headers.Add("alg", $SigningKey.JwsAlgorithmName()); $headers.Add("url", $Url); if($Nonce) { Write-Debug "Nonce $Nonce will be used"; $headers.Add("nonce", $Nonce); } if($KeyId) { Write-Debug "KeyId $KeyId will be used"; $headers.Add("kid", $KeyId); } if(-not ($KeyId)) { Write-Debug "No KeyId present, addind JWK."; $headers.Add("jwk", $SigningKey.ExportPublicJwk()); } if($Payload -is [string]) { Write-Debug "Payload was string, using without Conversion." $messagePayload = $Payload; } else { Write-Debug "Payload was object, converting to Json"; $messagePayload = $Payload | ConvertTo-Json -Compress; } $jsonHeaders = $headers | ConvertTo-Json -Compress Write-Debug "Payload is now: $messagePayload"; Write-Debug "Headers are: $jsonHeaders" $signedPayload = @{}; $signedPayload.add("header", $null); $signedPayload.add("protected", ($jsonHeaders | ConvertTo-UrlBase64)); $signedPayload.add("payload", ($messagePayload | ConvertTo-UrlBase64)); $signedPayload.add("signature", (ConvertTo-UrlBase64 -InputBytes $SigningKey.Sign("$($signedPayload.Protected).$($signedPayload.Payload)"))); $result = $signedPayload | ConvertTo-Json; Write-Debug "Created signed message`n: $result"; return $result; } |