MDM_utils.ps1

# This file contains utility functions for Intune MDM

# Get MDM discovery information
# Aug 20th
function Get-MDMEnrollmentService
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$UserName="user@alt.none"
    )
    Process
    {
        $messageId =          $(New-Guid).ToString()
        $deviceType =         "CIMClient_Windows"
        $applicationVersion = "10.0.18363.0"
        $OSEdition =          "4"
        
        $body=@"
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header>
        <a:Action s:mustUnderstand="1">http://schemas.microsoft.com/windows/management/2012/01/enrollment/IDiscoveryService/Discover</a:Action>
        <a:MessageID>urn:uuid:$messageId</a:MessageID>
        <a:ReplyTo>
            <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
        </a:ReplyTo>
        <a:To s:mustUnderstand="1">https://enrollment.manage.microsoft.com:443/enrollmentserver/discovery.svc</a:To>
    </s:Header>
    <s:Body>
        <Discover xmlns="http://schemas.microsoft.com/windows/management/2012/01/enrollment">
            <request xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
                <EmailAddress>$UserName</EmailAddress>
                <RequestVersion>4.0</RequestVersion>
                <DeviceType>$deviceType</DeviceType>
                <ApplicationVersion>$applicationVersion</ApplicationVersion>
                <OSEdition>$OSEdition</OSEdition>
                <AuthPolicies>
                    <AuthPolicy>OnPremise</AuthPolicy>
                    <AuthPolicy>Federated</AuthPolicy>
                </AuthPolicies>
            </request>
        </Discover>
    </s:Body>
</s:Envelope>
"@

        $headers=@{
            "Content-Type" = "application/soap+xml; charset=utf-8"
            "User-Agent"   = "ENROLLClient"
        }

        $response = Invoke-RestMethod -Method Post -Uri "https://enrollment.manage.microsoft.com/enrollmentserver/discovery.svc" -Body $body -ContentType "application/soap+xml; charset=utf-8" -Headers $headers

        # Get the data
        $activityId = $response.Envelope.Header.ActivityId.'#text'
        $serviceUri = $response.Envelope.Body.DiscoverResponse.DiscoverResult.EnrollmentServiceUrl

        if(!$serviceUri.EndsWith($activityId))
        {
            $serviceUri += "?client-request-id=$activityId"
        }

        # Return
        return $serviceUri
            
    }
}

# Enroll device to MDM
# Aug 28th
function Enroll-DeviceToMDM
{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        [String]$DeviceName
    )
    Process
    {
        # Get the claims from the access token
        $claims = Read-Accesstoken -AccessToken $AccessToken

        # Construct the values
        $enrollmentUrl =       Get-MDMEnrollmentService -UserName $claims.upn
        $binarySecurityToken = Convert-ByteArrayToB64 -Bytes ([text.encoding]::UTF8.GetBytes($AccessToken))

        $HWDevID = "$($claims.deviceid)$($claims.tid)".Replace("-","")
        $deviceId = $claims.deviceid.Replace("-","")

        # Create a private key
        $rsa = [System.Security.Cryptography.RSA]::Create(2048)

        # Initialize the Certificate Signing Request object
        $CN = "CN=$($claims.deviceid)" 
        $req = [System.Security.Cryptography.X509Certificates.CertificateRequest]::new($CN, $rsa, [System.Security.Cryptography.HashAlgorithmName]::SHA256,[System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
        
        # Create the signing request
        $csr = Convert-ByteArrayToB64 -Bytes $req.CreateSigningRequest()
        
        $headers=@{
            "Content-Type" = "application/soap+xml; charset=utf-8"
            "User-Agent"   = "ENROLLClient"
        }

        # Create the CSR request body
        $csrBody=@"
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512" xmlns:ac="http://schemas.xmlsoap.org/ws/2006/12/authorization">
    <s:Header>
        <a:Action s:mustUnderstand="1">http://schemas.microsoft.com/windows/pki/2009/01/enrollment/RST/wstep</a:Action>
        <a:MessageID>urn:uuid:0d5a1441-5891-453b-becf-a2e5f6ea3749</a:MessageID>
        <a:ReplyTo>
            <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
        </a:ReplyTo>
        <a:To s:mustUnderstand="1">$enrollmentUrl</a:To>
        <wsse:Security s:mustUnderstand="1">
            <wsse:BinarySecurityToken ValueType="urn:ietf:params:oauth:token-type:jwt" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#base64binary">$binarySecurityToken</wsse:BinarySecurityToken>
        </wsse:Security>
    </s:Header>
    <s:Body>
        <wst:RequestSecurityToken>
            <wst:TokenType>http://schemas.microsoft.com/5.0.0.0/ConfigurationManager/Enrollment/DeviceEnrollmentToken</wst:TokenType>
            <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>
            <wsse:BinarySecurityToken ValueType="http://schemas.microsoft.com/windows/pki/2009/01/enrollment#PKCS10" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#base64binary">$csr</wsse:BinarySecurityToken>
            <ac:AdditionalContext xmlns="http://schemas.xmlsoap.org/ws/2006/12/authorization">
                <ac:ContextItem Name="UXInitiated">
                    <ac:Value>true</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="HWDevID">
                    <ac:Value>$HWDevID</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="Locale">
                    <ac:Value>en-US</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="TargetedUserLoggedIn">
                    <ac:Value>true</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="EnrollmentData">
                    <ac:Value></ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="OSEdition">
                    <ac:Value>4</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="DeviceName">
                    <ac:Value>$DeviceName</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="MAC">
                    <ac:Value>00-00-00-00-00-00</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="DeviceID">
                    <ac:Value>$deviceId</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="EnrollmentType">
                    <ac:Value>Device</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="DeviceType">
                    <ac:Value>CIMClient_Windows</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="OSVersion">
                    <ac:Value>10.0.18363.0</ac:Value>
                </ac:ContextItem>
                <ac:ContextItem Name="ApplicationVersion">
                    <ac:Value>10.0.18363.0</ac:Value>
                </ac:ContextItem>
            </ac:AdditionalContext>
        </wst:RequestSecurityToken>
    </s:Body>
</s:Envelope>
"@


        # Clean the url
        $url=$enrollmentUrl.Replace(":443","")

        $response = Invoke-RestMethod -Method Post -Uri $url -Body $csrBody -ContentType "application/soap+xml; charset=utf-8" -Headers $headers

        # Get the data
        $binSecurityToken = $response.Envelope.Body.RequestSecurityTokenResponseCollection.RequestSecurityTokenResponse.RequestedSecurityToken.BinarySecurityToken.'#text'
        $xmlSecurityToken = [xml][text.encoding]::UTF8.GetString((Convert-B64ToByteArray -B64 $binSecurityToken))

        Write-Debug "BinarySecurityToken: $($xmlSecurityToken.OuterXml)"

        # Get the certificates
        $CA =       $xmlSecurityToken.'wap-provisioningdoc'.characteristic[0].characteristic[0].characteristic.characteristic.Parm.value
        $IntMedCA = $xmlSecurityToken.'wap-provisioningdoc'.characteristic[0].characteristic[1].characteristic.characteristic.Parm.value
        $binCert =  [byte[]](Convert-B64ToByteArray -B64 ($xmlSecurityToken.'wap-provisioningdoc'.characteristic[0].characteristic[2].characteristic.characteristic.Parm.value))

        # Create a new x509certificate
        $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($binCert,"",[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)

        # Store the private key to so that it can be exported
        $cspParameters = [System.Security.Cryptography.CspParameters]::new()
        $cspParameters.ProviderName =    "Microsoft Enhanced RSA and AES Cryptographic Provider"
        $cspParameters.ProviderType =    24
        $cspParameters.KeyContainerName ="AADInternals"
            
        # Set the private key
        $privateKey = [System.Security.Cryptography.RSACryptoServiceProvider]::new(2048,$cspParameters)
        $privateKey.ImportParameters($rsa.ExportParameters($true))
        $cert.PrivateKey = $privateKey

        # Generate the return value
        $joinInfo = @(
            $CA,
            $IntMedCA,
            $cert
        )
        
        return $joinInfo
            
    }
}