Public/Get-KeyAuthorization.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
function Get-KeyAuthorization {
    [CmdletBinding()]
    [OutputType('System.String')]
    param(
        [Parameter(Mandatory,Position=0,ValueFromPipeline)]
        [string]$Token,
        [Parameter(Position=1)]
        [PSTypeName('PoshACME.PAAccount')]$Account,
        [switch]$ForDNS
    )

    # https://tools.ietf.org/html/rfc8555#section-8.1

    # A key authorization is a string that expresses
    # a domain holder's authorization for a specified key to satisfy a
    # specified challenge, by concatenating the token for the challenge
    # with a key fingerprint, separated by a "." character:

    # keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))

    # The "JWK_Thumbprint" step indicates the computation specified in
    # [RFC7638], using the SHA-256 digest [FIPS180-4]. As noted in
    # [RFC7518] any prepended zero octets in the fields of a JWK object
    # MUST be stripped before doing the computation.

    # As specified in the individual challenges below, the token for a
    # challenge is a string comprised entirely of characters in the URL-
    # safe base64 alphabet. The "||" operator indicates concatenation of
    # strings.

    Begin {
        # make sure any account passed in is actually associated with the current server
        # or if no account was specified, that there's a current account.
        if (-not $Account) {
            if (-not ($Account = Get-PAAccount)) {
                try { throw "No Account parameter specified and no current account selected. Try running Set-PAAccount first." }
                catch { $PSCmdlet.ThrowTerminatingError($_) }
            }
        } else {
            if ($Account.id -notin (Get-PAAccount -List).id) {
                try { throw "Specified account id $($Account.id) was not found in the current server's account list." }
                catch { $PSCmdlet.ThrowTerminatingError($_) }
            }
        }
        # make sure it's valid
        if ($Account.status -ne 'valid') {
            try { throw "Account status is $($Account.status)." }
            catch { $PSCmdlet.ThrowTerminatingError($_) }
        }

        # hydrate the account key
        $acctKey = $Account.key | ConvertFrom-Jwk

        # create the key thumbprint
        $pubJwk = $acctKey | ConvertTo-Jwk -PublicOnly -AsJson
        $jwkBytes = [Text.Encoding]::UTF8.GetBytes($pubJwk)
        $sha256 = [Security.Cryptography.SHA256]::Create()
        $jwkHash = $sha256.ComputeHash($jwkBytes)
        $thumb = ConvertTo-Base64Url $jwkHash
    }

    Process {
        # append the thumbprint to the token to make the key authorization
        $keyAuth = "$Token.$thumb"

        if ($ForDNS) {
            # do an extra SHA256 hash + Base64Url encode for DNS TXT values
            $keyAuthBytes = [Text.Encoding]::UTF8.GetBytes($keyAuth)
            $keyAuthHash = $sha256.ComputeHash($keyAuthBytes)
            $txtValue = ConvertTo-Base64Url -Bytes $keyAuthHash
            return $txtValue

        } else {
            # return it as-is
            return $keyAuth
        }
    }
}