DCaaS_utils.ps1
# Decrypts the given ADAuthInfo BLOB with the given certificate # Dec 22nd 2022 function Unprotect-ADAuthInfo { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [byte[]]$Data, [Parameter(Mandatory=$true)] [System.Security.Cryptography.X509Certificates.X509Certificate]$Certificate ) Process { # Parse the blob $ADAuthInfo = Parse-ADAuthInfo -Data $Data # Ref: Microsoft.AD.DCaaS.Serialization.ADAuthInfoGenerator::Encrypt [Array]::Reverse($ADAuthInfo.Key) # This may be encrypted with other (=older) certificate.. try { $key = $Certificate.PrivateKey.Decrypt($ADAuthInfo.Key,$true) [System.Security.Cryptography.AesCryptoServiceProvider]$aesCryptoServiceProvider = [System.Security.Cryptography.AesCryptoServiceProvider]::new() $aesCryptoServiceProvider.KeySize = 256 $aesCryptoServiceProvider.BlockSize = 128; $aesCryptoServiceProvider.Mode = [System.Security.Cryptography.CipherMode]::CBC $aesCryptoServiceProvider.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 $aesCryptoServiceProvider.Key = $key $aesCryptoServiceProvider.IV = $ADAuthInfo.IV [System.IO.MemoryStream]$memoryStream = [System.IO.MemoryStream]::new() [System.Security.Cryptography.ICryptoTransform]$cryptoTransform = $aesCryptoServiceProvider.CreateDecryptor() [System.Security.Cryptography.CryptoStream]$cryptoStream = [System.Security.Cryptography.CryptoStream]::new($memoryStream,$cryptoTransform,[System.Security.Cryptography.CryptoStreamMode]::Write) $cryptoStream.Write($ADAuthInfo.EncryptedData,0,$ADAuthInfo.EncryptedData.Length) $cryptoStream.FlushFinalBlock() $decryptedData = $memoryStream.ToArray() return $decryptedData } catch { # Probably just encrypted using other (older) certificate return $null } finally { if($cryptoStream) {$cryptoStream.Dispose()} if($cryptoTransform) {$cryptoTransform.Dispose()} if($memoryStream) {$memoryStream.Dispose()} if($aesCryptoServiceProvider) {$aesCryptoServiceProvider.Dispose()} } } } # Parses the given ADAuthInfo BLOB and returns the parsed attributes # Dec 22nd 2022 function Parse-ADAuthInfo { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [byte[]]$Data ) Process { # Parse the blob $p = 0; $version = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $reserved = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $encKeyAlg = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $payloadEncKeyAlg = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $thumbPrintSize = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $encKeySize = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $IVSize = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $encDataSize = [System.BitConverter]::ToInt32($Data,$p); $p += 4 $thumbPrint = $Data[$p..($p+$thumbPrintSize-1)]; $p += $thumbPrintSize $encKey = $Data[$p..($p+$encKeySize-1)]; $p += $encKeySize $IV = $Data[$p..($p+$IVSize-1)]; $p += $IVSize $encData = $Data[$p..($p+$encDataSize-1)]; $p += $encDataSize return [PSCustomObject][ordered]@{ "ThumbPrint" = $thumbPrint "Key" = $encKey "IV" = $IV "EncryptedData" = $encData } } } # Creates a new ADAuthInfo BLOB using the given parameters # Dec 22nd 2022 function New-ADAuthInfo { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [byte[]]$Data, [Parameter(Mandatory=$true)] [byte[]]$Thumbprint, [Parameter(Mandatory=$true)] [byte[]]$Key, [Parameter(Mandatory=$true)] [byte[]]$InitialVector ) Process { # Set the values $version = 1 $reserved = 0 $encKeyAlg = 1 $payloadEncKeyAlg = 1 $thumbPrintSize = $Thumbprint.Length $encKeySize = $Key.Length $IVSize = $InitialVector.Length $encDataSize = $Data.Length [System.IO.MemoryStream]$memoryStream = [System.IO.MemoryStream]::new(0x144 + $encDataSize) # Construct the blob $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$version ),0,4) $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$reserved ),0,4) $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$encKeyAlg ),0,4) $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$payloadEncKeyAlg),0,4) $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$thumbPrintSize ),0,4) $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$encKeySize ),0,4) $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$IVSize ),0,4) $memoryStream.Write([System.BitConverter]::GetBytes([Int32]$encDataSize ),0,4) $memoryStream.Write($thumbPrint ,0,$thumbPrintSize) $memoryStream.Write($Key ,0,$encKeySize) $memoryStream.Write($InitialVector,0,$IVSize) $memoryStream.Write($Data ,0,$encDataSize) $blob = $memoryStream.ToArray() return $blob } End { $memoryStream.Dispose() } } # Encrypts the given ADAuthInfo BLOB with the given certificate # Dec 22nd 2022 function Protect-ADAuthInfo { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [byte[]]$Data, [Parameter(Mandatory=$true)] [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate ) Process { # Create the encryptor [System.Security.Cryptography.AesCryptoServiceProvider]$aesCryptoServiceProvider = [System.Security.Cryptography.AesCryptoServiceProvider]::new() $aesCryptoServiceProvider.KeySize = 256 $aesCryptoServiceProvider.BlockSize = 128; $aesCryptoServiceProvider.Mode = [System.Security.Cryptography.CipherMode]::CBC $aesCryptoServiceProvider.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 $aesCryptoServiceProvider.GenerateKey() $aesCryptoServiceProvider.GenerateIV() # Ref: Microsoft.AD.DCaaS.Serialization.ADAuthInfoGenerator::Encrypt $key = $Certificate.PublicKey.Key.Encrypt($aesCryptoServiceProvider.Key,$true) [Array]::Reverse($key) [System.IO.MemoryStream]$memoryStream = [System.IO.MemoryStream]::new() [System.Security.Cryptography.ICryptoTransform]$cryptoTransform = $aesCryptoServiceProvider.CreateEncryptor() [System.Security.Cryptography.CryptoStream]$cryptoStream = [System.Security.Cryptography.CryptoStream]::new($memoryStream,$cryptoTransform,[System.Security.Cryptography.CryptoStreamMode]::Write) $cryptoStream.Write($Data,0,$Data.Length) $cryptoStream.FlushFinalBlock() $encryptedData = $memoryStream.ToArray() $ADAutInfo = New-ADAuthInfo -Data $encryptedData -Thumbprint (Convert-HexToByteArray -HexString $Certificate.Thumbprint) -Key $key -InitialVector $aesCryptoServiceProvider.IV return $ADAutInfo } End { $cryptoStream.Dispose() $cryptoTransform.Dispose() $memoryStream.Dispose() $aesCryptoServiceProvider.Dispose() } } # Gets access token using Azure AD Domain Services Sync certificate or password # Dec 22nd 2022 function Get-DCaaSAccessToken { [CmdletBinding()] param( [Parameter(Mandatory=$False)] [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, [Parameter(Mandatory=$False)] [string]$Password, [Parameter(Mandatory=$True)] [guid]$TenantId, [Parameter(Mandatory=$True)] [guid]$ClientId, [Parameter(Mandatory=$False)] [string]$Scope = "https://graph.microsoft.com//.default" ) Process { # Use client certificate if([string]::IsNullOrEmpty($Password)) { # Load the private key (otherwise signing won't work) $privKey = Load-PrivateKey -Certificate $certificate # Create header $kid = $certificate.Thumbprint $x5t = Convert-ByteArrayToB64 -Bytes (Convert-HexToByteArray -HexString $kid) -UrlEncode $x5c = Convert-ByteArrayToB64 -Bytes $certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert) $nbf = [int]((Get-Date).ToUniversalTime() - $epoch).TotalSeconds $exp = $nbf + 600 # Valid for 10 minutes $header=[ordered]@{ "x5t" = $x5t "kid" = $kid "x5c" = $x5c "alg" = "RS256" "typ" = "JWT" } # Create payload $payload=[ordered]@{ "aud" = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" "iss" = $ClientId "nbf" = $nbf "exp" = $exp "sub" = $ClientId "jti" = (New-Guid).ToString() } $client_assertion = New-JWT -Header $header -Payload $payload -PrivateKey $privKey $body=[ordered]@{ "client_id" = $ClientId "client_info" = 1 "client_assertion_type" = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" "client_assertion" = $client_assertion "scope" = $Scope "grant_type" = "client_credentials" } } # Use password else { $body=[ordered]@{ "client_id" = $ClientId "client_secret" = $Password "scope" = $Scope "grant_type" = "client_credentials" } } $response = Invoke-RestMethod -UseBasicParsing -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" -Method Post -Body $body return $response.access_token } End { if($privKey) { Unload-PrivateKey -PrivateKey $privKey } } } |