From 7232ee5bd95f8c6141250c54beb1dd5e244a3da6 Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Sat, 1 Aug 2020 14:49:04 +0200 Subject: [PATCH 01/10] Remove breaking change notification --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 3cef90e..4f654a7 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,6 @@ Download the Module via Powershell-Gallery Date: Sat, 1 Aug 2020 14:49:15 +0200 Subject: [PATCH 02/10] bump version, set beta --- ACME-PS/ACME-PS.psd1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ACME-PS/ACME-PS.psd1 b/ACME-PS/ACME-PS.psd1 index 9c55f10..fc1004c 100644 --- a/ACME-PS/ACME-PS.psd1 +++ b/ACME-PS/ACME-PS.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'ACME-PS.psm1' - ModuleVersion = '1.2.5' + ModuleVersion = '1.3.0' GUID = '2DBF7E3F-F830-403A-9300-78A11C7CD00C' CompatiblePSEditions = @("Core", "Desktop") @@ -85,7 +85,7 @@ ReleaseNotes = 'Please see the release notes from the release distribution page: https://github.com/PKISharp/ACME-PS/releases' # Prerelase - # Prerelease = 'beta' + Prerelease = 'beta2' } # End of PSData hashtable } # End of PrivateData hashtable From 7c4fe0d517b59b34ce4f2f6dbc57b2cf7123effc Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Sat, 1 Aug 2020 15:23:25 +0200 Subject: [PATCH 03/10] Add ExternalAccountBinding --- ACME-PS/functions/Account/New-Account.ps1 | 42 +++++++++++++-- ACME-PS/internal/classes/AcmeDirectory.ps1 | 2 + .../functions/ConvertFrom-UrlBase64.ps1 | 18 +++++++ .../functions/New-ExternalAccountPayload.ps1 | 51 +++++++++++++++++++ 4 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 create mode 100644 ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 diff --git a/ACME-PS/functions/Account/New-Account.ps1 b/ACME-PS/functions/Account/New-Account.ps1 index 5480ae5..6df5b65 100644 --- a/ACME-PS/functions/Account/New-Account.ps1 +++ b/ACME-PS/functions/Account/New-Account.ps1 @@ -25,6 +25,15 @@ function New-Account { .PARAMETER EmailAddresses Contact adresses for certificate expiration mails and similar. + .PARAMETER ExternalAccountKID + The account KID assigned by the external account verification. + + .PARAMETER ExternalAccountAlgorithm + The algorithm to be used to hash the external account binding. + + .PARAMETER ExternalAccountMACKey + The key to hash the external account binding object. + .EXAMPLE PS> New-Account -AcceptTOS -EmailAddresses "mail@example.com" -AutomaticAccountHandling @@ -53,17 +62,42 @@ function New-Account { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]] - $EmailAddresses + $EmailAddresses, + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountKID, + + [Parameter(ParameterSetName = "ExternalAccountBinding")] + [ValidateSet('HS256','HS384','HS512')] + [string] + $ExternalAccountAlgorithm = 'HS256', + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountMACKey ) - $Contacts = @($EmailAddresses | ForEach-Object { if($_.StartsWith("mailto:")) { $_ } else { "mailto:$_" } }); + $contacts = @($EmailAddresses | ForEach-Object { if($_.StartsWith("mailto:")) { $_ } else { "mailto:$_" } }); $payload = @{ "termsOfServiceAgreed"=$AcceptTOS.IsPresent; - "contact"=$Contacts; + "contact"=$contacts; + } + + $serviceDirectory = $State.GetServiceDirectory(); + $url = $serviceDirectory.NewAccount; + + if($PSCmdlet.ParameterSetName -ne "ExternalAccountBinding" -and $serviceDirectory.Meta.ExternalAccountRequired) { + throw "The ACME service requires an external account to create a new ACME account. Provide `-ExternalAccount*` Parameters." } - $url = $State.GetServiceDirectory().NewAccount; + if($PSCmdlet.ParameterSetName -eq "ExternalAccountBinding") { + $externalAccountBinding = New-ExternalAccountPayload -State $State -ExternalAccountKID $ExternalAccountKID -ExternalAccountMACKey $ExternalAccountMACKey -ExternalAccountAlgorithm $ExternalAccountAlgorithm; + $payload.Add("externalAccountBinding", $externalAccountBinding); + } if($PSCmdlet.ShouldProcess("New-Account", "Sending account registration to ACME Server $Url")) { $response = Invoke-SignedWebRequest -Url $url -State $State -Payload $payload -SuppressKeyId -ErrorAction 'Stop' diff --git a/ACME-PS/internal/classes/AcmeDirectory.ps1 b/ACME-PS/internal/classes/AcmeDirectory.ps1 index 5d8d662..0468494 100644 --- a/ACME-PS/internal/classes/AcmeDirectory.ps1 +++ b/ACME-PS/internal/classes/AcmeDirectory.ps1 @@ -29,9 +29,11 @@ class AcmeDirectoryMeta { $this.CaaIdentites = $obj.CaaIdentities; $this.TermsOfService = $obj.TermsOfService; $this.Website = $obj.Website; + $this.ExternalAccountRequired = $obj.ExternalAccountRequired; } [string[]] $CaaIdentites; [string] $TermsOfService; [string] $Website; + [bool] $ExternalAccountRequired; } diff --git a/ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 b/ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 new file mode 100644 index 0000000..85185a1 --- /dev/null +++ b/ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 @@ -0,0 +1,18 @@ +function ConvertFrom-UrlBase64 { + param( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [ValidateNotNull()] + [string] $InputText + ) + + process { + $base64 = $InputText.Replace('-','+'); + $base64 = $base64.Replace('/', '_'); + + while($base64.Length % 4 -ne 0) { + $base64 += '=' + } + + return [Convert]::FromBase64String($base64); + } +} \ No newline at end of file diff --git a/ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 b/ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 new file mode 100644 index 0000000..e7c48db --- /dev/null +++ b/ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 @@ -0,0 +1,51 @@ +function New-ExternalAccountPayload { + param( + [Parameter(Mandatory = $true, Position = 0)] + [ValidateNotNull()] + [ValidateScript({$_.AccountKeyExists()})] + [AcmeState] + $State, + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountKID, + + [Parameter(ParameterSetName = "ExternalAccountBinding")] + [ValidateSet('HS256','HS384','HS512')] + [string] + $ExternalAccountAlgorithm = 'HS256', + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountMACKey + ) + + process { + $macKeyBytes = ConvertFrom-UrlBase64 $ExternalAccountMACKey; + $macAlgorithm = switch ($ExternalAccountAlgorithm) { + "HS256" { [Security.Cryptography.HMACSHA256]::new($macKeyBytes); break; } + "HS384" { [Security.Cryptography.HMACSHA384]::new($macKeyBytes); break; } + "HS512" { [Security.Cryptography.HMACSHA512]::new($macKeyBytes); break; } + } + + $eaHeader = @{ + "alg" = $ExternalAccountAlgorithm; + "kid" = $ExternalAccountKID; + "url" = $url; + } | ConvertTo-Json -Compress + $eaPayload = $State.GetAccountKey().ExportPublicJwk() | ConvertTo-Json -Compress; + + $eaHashContent = [Text.Encoding]::ASCII.GetBytes("$($eaHeader).$($eaPayload)"); + $eaSignature = (ConvertTo-UrlBase64 -InputBytes $macAlgorithm.ComputeHash($eaHashContent)); + + $externalAccountBinding = @{ + "protected" = $eaHeader; + "payload" = $eaPayload; + "signature" = $eaSignature; + }; + + return $externalAccountBinding; + } +} \ No newline at end of file From 6cde565ac9bdbe4d7a56e36a9872627da697d21c Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Sat, 1 Aug 2020 15:23:39 +0200 Subject: [PATCH 04/10] Write tests and rebuild PSM --- .../internal/functions/New-SignedMessage.ps1 | 4 +- .../New-ExternalAccountPayload.Tests.ps1 | 26 ++++ dist/ACME-PS/ACME-PS.psd1 | 4 +- dist/ACME-PS/ACME-PS.psm1 | 119 +++++++++++++++++- 4 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 ACME-PS/tests/New-ExternalAccountPayload.Tests.ps1 diff --git a/ACME-PS/internal/functions/New-SignedMessage.ps1 b/ACME-PS/internal/functions/New-SignedMessage.ps1 index 41578ca..6ff1a0c 100644 --- a/ACME-PS/internal/functions/New-SignedMessage.ps1 +++ b/ACME-PS/internal/functions/New-SignedMessage.ps1 @@ -55,13 +55,15 @@ function New-SignedMessage { $signedPayload = @{}; - $signedPayload.add("header", $null); + $signedPayload.add("header", $null); # TODO what does this line exist? $signedPayload.add("protected", (ConvertTo-UrlBase64 -InputText $jsonHeaders)); + if($null -eq $messagePayload -or $messagePayload.Length -eq 0) { $signedPayload.add("payload", ""); } else { $signedPayload.add("payload", (ConvertTo-UrlBase64 -InputText $messagePayload)); } + $signedPayload.add("signature", (ConvertTo-UrlBase64 -InputBytes $SigningKey.Sign("$($signedPayload.Protected).$($signedPayload.Payload)"))); $result = $signedPayload | ConvertTo-Json; diff --git a/ACME-PS/tests/New-ExternalAccountPayload.Tests.ps1 b/ACME-PS/tests/New-ExternalAccountPayload.Tests.ps1 new file mode 100644 index 0000000..a8a5340 --- /dev/null +++ b/ACME-PS/tests/New-ExternalAccountPayload.Tests.ps1 @@ -0,0 +1,26 @@ +InModuleScope "ACME-PS" { + Describe "UnitTesting New-ExternalAccountPayload" -Tag "UnitTest" { + $simpleState = Get-State -Path "$PSScriptRoot\states\simple"; + $state = New-State -WarningAction 'SilentlyContinue'; + + $state.Set($simpleState.GetServiceDirectory()) + $state.SetNonce($simpleState.GetNonce()); + $state.Set($simpleState.GetAccountKey()); + + $result = New-ExternalAccountPayload -State $State ` + -ExternalAccountKID "myKID" -ExternalAccountAlgorithm "HS256" ` + -ExternalAccountMACKey "SLrdl4skg66W0NxZMwwAKPSvDtin-41SCweDRDBxMSSyh5AyoL1mNva6IMhFP13uyOQv5RI40WnnvzyXGlp77w" + It 'Returns an Object' { + $result | Should -Not -BeNullOrEmpty; + } + It 'Contains protected' { + $result.ContainsKey("protected") | Should -BeTrue; + } + It 'Contains payload' { + $result.ContainsKey("payload") | Should -BeTrue; + } + It 'Contains signature' { + $result.ContainsKey("signature") | Should -BeTrue; + } + } +} \ No newline at end of file diff --git a/dist/ACME-PS/ACME-PS.psd1 b/dist/ACME-PS/ACME-PS.psd1 index 9c55f10..fc1004c 100644 --- a/dist/ACME-PS/ACME-PS.psd1 +++ b/dist/ACME-PS/ACME-PS.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'ACME-PS.psm1' - ModuleVersion = '1.2.5' + ModuleVersion = '1.3.0' GUID = '2DBF7E3F-F830-403A-9300-78A11C7CD00C' CompatiblePSEditions = @("Core", "Desktop") @@ -85,7 +85,7 @@ ReleaseNotes = 'Please see the release notes from the release distribution page: https://github.com/PKISharp/ACME-PS/releases' # Prerelase - # Prerelease = 'beta' + Prerelease = 'beta2' } # End of PSData hashtable } # End of PrivateData hashtable diff --git a/dist/ACME-PS/ACME-PS.psm1 b/dist/ACME-PS/ACME-PS.psm1 index 7cc2e19..1812e3c 100644 --- a/dist/ACME-PS/ACME-PS.psm1 +++ b/dist/ACME-PS/ACME-PS.psm1 @@ -610,11 +610,13 @@ class AcmeDirectoryMeta { $this.CaaIdentites = $obj.CaaIdentities; $this.TermsOfService = $obj.TermsOfService; $this.Website = $obj.Website; + $this.ExternalAccountRequired = $obj.ExternalAccountRequired; } [string[]] $CaaIdentites; [string] $TermsOfService; [string] $Website; + [bool] $ExternalAccountRequired; } class AcmeAccount { @@ -1220,6 +1222,25 @@ class AcmeDiskPersistedState : AcmeState { } } +function ConvertFrom-UrlBase64 { + param( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [ValidateNotNull()] + [string] $InputText + ) + + process { + $base64 = $InputText.Replace('-','+'); + $base64 = $base64.Replace('/', '_'); + + while($base64.Length % 4 -ne 0) { + $base64 += '=' + } + + return [Convert]::FromBase64String($base64); + } +} + function ConvertTo-OriginalType { param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] @@ -1400,6 +1421,58 @@ function Invoke-ACMEWebRequest { return $result; } +function New-ExternalAccountPayload { + param( + [Parameter(Mandatory = $true, Position = 0)] + [ValidateNotNull()] + [ValidateScript({$_.AccountKeyExists()})] + [AcmeState] + $State, + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountKID, + + [Parameter(ParameterSetName = "ExternalAccountBinding")] + [ValidateSet('HS256','HS384','HS512')] + [string] + $ExternalAccountAlgorithm = 'HS256', + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountMACKey + ) + + process { + $macKeyBytes = ConvertFrom-UrlBase64 $ExternalAccountMACKey; + $macAlgorithm = switch ($ExternalAccountAlgorithm) { + "HS256" { [Security.Cryptography.HMACSHA256]::new($macKeyBytes); break; } + "HS384" { [Security.Cryptography.HMACSHA384]::new($macKeyBytes); break; } + "HS512" { [Security.Cryptography.HMACSHA512]::new($macKeyBytes); break; } + } + + $eaHeader = @{ + "alg" = $ExternalAccountAlgorithm; + "kid" = $ExternalAccountKID; + "url" = $url; + } | ConvertTo-Json -Compress + $eaPayload = $State.GetAccountKey().ExportPublicJwk() | ConvertTo-Json -Compress; + + $eaHashContent = [Text.Encoding]::ASCII.GetBytes("$($eaHeader).$($eaPayload)"); + $eaSignature = (ConvertTo-UrlBase64 -InputBytes $macAlgorithm.ComputeHash($eaHashContent)); + + $externalAccountBinding = @{ + "protected" = $eaHeader; + "payload" = $eaPayload; + "signature" = $eaSignature; + }; + + return $externalAccountBinding; + } +} + function New-SignedMessage { [CmdletBinding(SupportsShouldProcess=$false)] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( @@ -1457,13 +1530,15 @@ function New-SignedMessage { $signedPayload = @{}; - $signedPayload.add("header", $null); + $signedPayload.add("header", $null); # TODO what does this line exist? $signedPayload.add("protected", (ConvertTo-UrlBase64 -InputText $jsonHeaders)); + if($null -eq $messagePayload -or $messagePayload.Length -eq 0) { $signedPayload.add("payload", ""); } else { $signedPayload.add("payload", (ConvertTo-UrlBase64 -InputText $messagePayload)); } + $signedPayload.add("signature", (ConvertTo-UrlBase64 -InputBytes $SigningKey.Sign("$($signedPayload.Protected).$($signedPayload.Payload)"))); $result = $signedPayload | ConvertTo-Json; @@ -1589,6 +1664,15 @@ function New-Account { .PARAMETER EmailAddresses Contact adresses for certificate expiration mails and similar. + .PARAMETER ExternalAccountKID + The account KID assigned by the external account verification. + + .PARAMETER ExternalAccountAlgorithm + The algorithm to be used to hash the external account binding. + + .PARAMETER ExternalAccountMACKey + The key to hash the external account binding object. + .EXAMPLE PS> New-Account -AcceptTOS -EmailAddresses "mail@example.com" -AutomaticAccountHandling @@ -1617,17 +1701,42 @@ function New-Account { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]] - $EmailAddresses + $EmailAddresses, + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountKID, + + [Parameter(ParameterSetName = "ExternalAccountBinding")] + [ValidateSet('HS256','HS384','HS512')] + [string] + $ExternalAccountAlgorithm = 'HS256', + + [Parameter(ParameterSetName = "ExternalAccountBinding", Mandatory = $true)] + [ValidateNotNull()] + [string] + $ExternalAccountMACKey ) - $Contacts = @($EmailAddresses | ForEach-Object { if($_.StartsWith("mailto:")) { $_ } else { "mailto:$_" } }); + $contacts = @($EmailAddresses | ForEach-Object { if($_.StartsWith("mailto:")) { $_ } else { "mailto:$_" } }); $payload = @{ "termsOfServiceAgreed"=$AcceptTOS.IsPresent; - "contact"=$Contacts; + "contact"=$contacts; + } + + $serviceDirectory = $State.GetServiceDirectory(); + $url = $serviceDirectory.NewAccount; + + if($PSCmdlet.ParameterSetName -ne "ExternalAccountBinding" -and $serviceDirectory.Meta.ExternalAccountRequired) { + throw "The ACME service requires an external account to create a new ACME account. Provide `-ExternalAccount*` Parameters." } - $url = $State.GetServiceDirectory().NewAccount; + if($PSCmdlet.ParameterSetName -eq "ExternalAccountBinding") { + $externalAccountBinding = New-ExternalAccountPayload -State $State -ExternalAccountKID $ExternalAccountKID -ExternalAccountMACKey $ExternalAccountMACKey -ExternalAccountAlgorithm $ExternalAccountAlgorithm; + $payload.Add("externalAccountBinding", $externalAccountBinding); + } if($PSCmdlet.ShouldProcess("New-Account", "Sending account registration to ACME Server $Url")) { $response = Invoke-SignedWebRequest -Url $url -State $State -Payload $payload -SuppressKeyId -ErrorAction 'Stop' From daab1720f1b779a0b1004184e5891760d41f8767 Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Mon, 3 Aug 2020 22:00:32 +0200 Subject: [PATCH 05/10] Remove pre-release --- ACME-PS/ACME-PS.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ACME-PS/ACME-PS.psd1 b/ACME-PS/ACME-PS.psd1 index fc1004c..cd592a6 100644 --- a/ACME-PS/ACME-PS.psd1 +++ b/ACME-PS/ACME-PS.psd1 @@ -85,7 +85,7 @@ ReleaseNotes = 'Please see the release notes from the release distribution page: https://github.com/PKISharp/ACME-PS/releases' # Prerelase - Prerelease = 'beta2' + # Prerelease = 'beta4' } # End of PSData hashtable } # End of PrivateData hashtable From cd6c1e7795672fdfe7bad32344701125df899024 Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Mon, 3 Aug 2020 22:00:46 +0200 Subject: [PATCH 06/10] More precise docs --- ACME-PS/functions/Account/New-Account.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ACME-PS/functions/Account/New-Account.ps1 b/ACME-PS/functions/Account/New-Account.ps1 index 6df5b65..abddbfc 100644 --- a/ACME-PS/functions/Account/New-Account.ps1 +++ b/ACME-PS/functions/Account/New-Account.ps1 @@ -32,7 +32,7 @@ function New-Account { The algorithm to be used to hash the external account binding. .PARAMETER ExternalAccountMACKey - The key to hash the external account binding object. + The key to hash the external account binding object (needs to be base64 or base64url encoded) .EXAMPLE From a0608d3a8a5ff29ddd21f231ada1570846e9fd6e Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Mon, 3 Aug 2020 22:01:02 +0200 Subject: [PATCH 07/10] Fix Base64 conversion --- ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 b/ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 index 85185a1..2f50a90 100644 --- a/ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 +++ b/ACME-PS/internal/functions/ConvertFrom-UrlBase64.ps1 @@ -7,7 +7,7 @@ function ConvertFrom-UrlBase64 { process { $base64 = $InputText.Replace('-','+'); - $base64 = $base64.Replace('/', '_'); + $base64 = $base64.Replace('_', '/'); while($base64.Length % 4 -ne 0) { $base64 += '=' From 09d915e23bcaef23004c4456651ac8f3c41138da Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Mon, 3 Aug 2020 22:01:30 +0200 Subject: [PATCH 08/10] Fix some errors --- ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 b/ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 index e7c48db..59f7daa 100644 --- a/ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 +++ b/ACME-PS/internal/functions/New-ExternalAccountPayload.ps1 @@ -34,8 +34,8 @@ function New-ExternalAccountPayload { "alg" = $ExternalAccountAlgorithm; "kid" = $ExternalAccountKID; "url" = $url; - } | ConvertTo-Json -Compress - $eaPayload = $State.GetAccountKey().ExportPublicJwk() | ConvertTo-Json -Compress; + } | ConvertTo-Json -Compress | ConvertTo-UrlBase64 + $eaPayload = $State.GetAccountKey().ExportPublicJwk() | ConvertTo-Json -Compress | ConvertTo-UrlBase64; $eaHashContent = [Text.Encoding]::ASCII.GetBytes("$($eaHeader).$($eaPayload)"); $eaSignature = (ConvertTo-UrlBase64 -InputBytes $macAlgorithm.ComputeHash($eaHashContent)); From 58760d06875559e8d4085ef8dda1d65465cc8df3 Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Mon, 3 Aug 2020 22:01:52 +0200 Subject: [PATCH 09/10] Write a simple test for base64 encoding --- ACME-PS/tests/A-Manual-Test-Run.ps1 | 2 +- ACME-PS/tests/UrlBase64.Tests.ps1 | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 ACME-PS/tests/UrlBase64.Tests.ps1 diff --git a/ACME-PS/tests/A-Manual-Test-Run.ps1 b/ACME-PS/tests/A-Manual-Test-Run.ps1 index 1b4cc19..748d9cc 100644 --- a/ACME-PS/tests/A-Manual-Test-Run.ps1 +++ b/ACME-PS/tests/A-Manual-Test-Run.ps1 @@ -2,7 +2,7 @@ try { $ModuleBase = Split-Path -Path $PSScriptRoot -Parent Remove-Module ACME-PS -ErrorAction Ignore - Import-Module "$ModuleBase\ACME-PS.psd1" -ErrorAction 'Stop' + Import-Module "$ModuleBase\ACME-PS.psd1" -Force -ErrorAction 'Stop' Invoke-Pester -Path "$ModuleBase\tests" } diff --git a/ACME-PS/tests/UrlBase64.Tests.ps1 b/ACME-PS/tests/UrlBase64.Tests.ps1 new file mode 100644 index 0000000..7b2795f --- /dev/null +++ b/ACME-PS/tests/UrlBase64.Tests.ps1 @@ -0,0 +1,15 @@ +InModuleScope ACME-PS { + Context 'ConvertTo-Base64Url and ConvertFrom-Base64 url are roundtrippable' { + $value = [byte[]]@(131,251,190,1); + + $base64Form = ConvertTo-UrlBase64 -InputBytes $value; + It 'Converted the input successfully' { + $base64Form | Should -Be "g_u-AQ"; + } + + $roundtripped = ConvertFrom-UrlBase64 $base64Form; + It 'Should match the orginial array' { + $roundtripped | Should -Be $value; + } + } +} \ No newline at end of file From a74cb1d668635549344495c10bef24cfa05ac4e4 Mon Sep 17 00:00:00 2001 From: Thomas Glatzer Date: Mon, 3 Aug 2020 22:02:20 +0200 Subject: [PATCH 10/10] Update distribution --- dist/ACME-PS/ACME-PS.psd1 | 2 +- dist/ACME-PS/ACME-PS.psm1 | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/ACME-PS/ACME-PS.psd1 b/dist/ACME-PS/ACME-PS.psd1 index fc1004c..cd592a6 100644 --- a/dist/ACME-PS/ACME-PS.psd1 +++ b/dist/ACME-PS/ACME-PS.psd1 @@ -85,7 +85,7 @@ ReleaseNotes = 'Please see the release notes from the release distribution page: https://github.com/PKISharp/ACME-PS/releases' # Prerelase - Prerelease = 'beta2' + # Prerelease = 'beta4' } # End of PSData hashtable } # End of PrivateData hashtable diff --git a/dist/ACME-PS/ACME-PS.psm1 b/dist/ACME-PS/ACME-PS.psm1 index 1812e3c..d39b60d 100644 --- a/dist/ACME-PS/ACME-PS.psm1 +++ b/dist/ACME-PS/ACME-PS.psm1 @@ -1231,7 +1231,7 @@ function ConvertFrom-UrlBase64 { process { $base64 = $InputText.Replace('-','+'); - $base64 = $base64.Replace('/', '_'); + $base64 = $base64.Replace('_', '/'); while($base64.Length % 4 -ne 0) { $base64 += '=' @@ -1457,8 +1457,8 @@ function New-ExternalAccountPayload { "alg" = $ExternalAccountAlgorithm; "kid" = $ExternalAccountKID; "url" = $url; - } | ConvertTo-Json -Compress - $eaPayload = $State.GetAccountKey().ExportPublicJwk() | ConvertTo-Json -Compress; + } | ConvertTo-Json -Compress | ConvertTo-UrlBase64 + $eaPayload = $State.GetAccountKey().ExportPublicJwk() | ConvertTo-Json -Compress | ConvertTo-UrlBase64; $eaHashContent = [Text.Encoding]::ASCII.GetBytes("$($eaHeader).$($eaPayload)"); $eaSignature = (ConvertTo-UrlBase64 -InputBytes $macAlgorithm.ComputeHash($eaHashContent)); @@ -1671,7 +1671,7 @@ function New-Account { The algorithm to be used to hash the external account binding. .PARAMETER ExternalAccountMACKey - The key to hash the external account binding object. + The key to hash the external account binding object (needs to be base64 or base64url encoded) .EXAMPLE