Private/Storage/ActiveDirectory/Protect-PukAdAttributeValue.ps1

function Protect-PukAdAttributeValue {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [psobject]$InputObject,

        [Parameter(Mandatory)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
    )

    # AesGcm (authenticated encryption in one step) requires .NET Core 3.0+ and does not exist
    # on .NET Framework, so it can't be used here without breaking Windows PowerShell 5.1
    # compatibility. Instead: AES-256-CBC plus an independent Encrypt-then-MAC HMAC-SHA256 tag
    # (computed over IV || ciphertext) gives equivalent tamper-detection on both engines.
    $aesMaterial = New-PukAesKey
    $combinedKeyMaterial = $aesMaterial.Key + $aesMaterial.HmacKey
    $wrappedKey = Protect-PukAesKeyWithCertificate -Key $combinedKeyMaterial -Certificate $Certificate

    $plaintextBytes = [System.Text.Encoding]::UTF8.GetBytes(($InputObject | ConvertTo-Json -Depth 10))

    $aes = [System.Security.Cryptography.Aes]::Create()
    try {
        $aes.Key = $aesMaterial.Key
        $aes.IV = $aesMaterial.IV
        $encryptor = $aes.CreateEncryptor()
        try {
            $cipherBytes = $encryptor.TransformFinalBlock($plaintextBytes, 0, $plaintextBytes.Length)
        }
        finally {
            $encryptor.Dispose()
        }
    }
    finally {
        $aes.Dispose()
    }

    $hmac = [System.Security.Cryptography.HMACSHA256]::new($aesMaterial.HmacKey)
    try {
        $macBytes = $hmac.ComputeHash($aesMaterial.IV + $cipherBytes)
    }
    finally {
        $hmac.Dispose()
    }

    $wrappedKeyB64 = [Convert]::ToBase64String($wrappedKey)
    $ivB64 = [Convert]::ToBase64String($aesMaterial.IV)
    $cipherB64 = [Convert]::ToBase64String($cipherBytes)
    $macB64 = [Convert]::ToBase64String($macBytes)

    return "$wrappedKeyB64.$ivB64.$cipherB64.$macB64"
}