Public/New-PAAuthorization.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
80
81
82
83
84
85
86
87
88
89
function New-PAAuthorization {
    [CmdletBinding()]
    [OutputType('PoshACME.PAAuthorization')]
    param(
        [Parameter(Mandatory,Position=0,ValueFromPipeline)]
        [string[]]$Domain,
        [Parameter(Position=1)]
        [PSTypeName('PoshACME.PAAccount')]$Account
    )

    Begin {
        # Make sure the current server actually supports pre-authorization
        if (-not $script:Dir.newAuthz) {
            try { throw "The current ACME server does not support pre-authorization. Use New-PAOrder or New-PACertificate instead." }
            catch { $PSCmdlet.ThrowTerminatingError($_) }
        }

        # Make sure there's a valid 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($_) }
            }
        }
        if ($Account.status -ne 'valid') {
            try { throw "Account status is $($Account.status)." }
            catch { $PSCmdlet.ThrowTerminatingError($_) }
        }

        # super lazy IPv4 address regex, but we just need to be able to
        # distinguish from an FQDN
        $reIPv4 = [regex]'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'

        # IP identifiers (RFC8738) are an extension to the original ACME protocol
        # https://tools.ietf.org/html/rfc8738
        #
        # So we have to distinguish between domain FQDNs and IPv4/v6 addresses
        # and send the appropriate identifier type for each one. We don't care
        # if the IP address entered is actually valid or not, only that it is
        # parsable as an IP address and should be sent as one rather than a
        # DNS name.
    }

    Process {

        foreach ($name in $Domain) {

            # build the protected header for the request
            $header = @{
                alg   = $Account.alg;
                kid   = $Account.location;
                nonce = $script:Dir.nonce;
                url   = $script:Dir.newAuthz;
            }

            # build the payload object
            if ($name -match $reIPv4 -or $name -like '*:*') {
                Write-Debug "$name identified as IP address. Attempting to parse."
                $ip = [ipaddress]$name

                $payload = @{ identifier = @{type='ip';value=$ip.ToString()} }
            }
            else {
                $payload = @{ identifier = @{type='dns';value=$name} }
            }

            $payloadJson = $payload | ConvertTo-Json -Depth 5 -Compress

            # send the request
            try {
                $response = Invoke-ACME $header $payloadJson $Account -EA Stop

                # grab the location from the header
                if ($response.Headers.ContainsKey('Location')) {
                    $location = $response.Headers['Location'] | Select-Object -First 1
                } else {
                    throw 'No Location header found in response output'
                }
            } catch {
                Write-Error $_
                continue
            }

            ConvertTo-PAAuthorization $response.Content $location

        }
    }
}