Закодировать и декодировать строку
Переведите строку в байты с явно указанной кодировкой, затем вызовите [Convert]:
$enc = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("héllo wörld"))
$enc # aMOpbGxvIHfDtnJsZA==
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($enc)) # héllo wörld
Результат зависит от кодировки. [Text.Encoding]::Unicode – это UTF-16LE, поэтому те же две буквы дают другой результат:
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("hi")) # aGk=
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("hi")) # aABpAA==
Для всего, что уходит в веб-API, конфигурационные файлы или другие языки, используйте UTF-8. Unicode – только для -EncodedCommand. Если в декодированной строке между буквами видны промежутки, это был UTF-16; такие строки декодирует инструмент Base64 в UTF-16.
Заголовок Basic Auth для REST-запроса:
$token = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("user:pass"))
$headers = @{ Authorization = "Basic $token" }
$headers.Authorization # Basic dXNlcjpwYXNz
Invoke-RestMethod -Uri $url -Headers $headers
PowerShell 6+ умеет собрать заголовок и сам через Invoke-RestMethod -Authentication Basic -Credential $cred, и тогда пароль не лежит в обычной строке.
-EncodedCommand
powershell.exe и pwsh принимают скрипт в Base64 через -EncodedCommand (краткие формы -enc, -e). Это нужно, чтобы передавать скрипты с кавычками и спецсимволами через cmd.exe, планировщики и удалённые инструменты без проблем с экранированием. Текст должен быть в UTF-16LE:
$cmd = 'Write-Output "hi from base64"'
$b = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd))
$b
# VwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIAAiAGgAaQAgAGYAcgBvAG0AIABiAGEAcwBlADYANAAiAA==
pwsh -NoProfile -EncodedCommand $b # hi from base64
Злоумышленники тоже используют -enc, чтобы спрятать скрипты от беглого взгляда, поэтому средства защиты такие команды помечают. Прочитать команду, не выполняя её:
[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($b))
# Write-Output "hi from base64"
Инструмент закодированная команда PowerShell работает в обе стороны прямо в браузере и никогда не запускает скрипт.
URL-safe Base64
Встроенного URL-safe режима в [Convert] нет. Замените символы и дополнение сами:
$u = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("<<???>>"))
$u.TrimEnd('=').Replace('+', '-').Replace('/', '_') # PDw_Pz8-Pg
$s = "PDw_Pz8-Pg".Replace('-', '+').Replace('_', '/')
$s = $s.PadRight($s.Length + (4 - $s.Length % 4) % 4, '=')
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s)) # <<???>>
PowerShell 7.5 работает на .NET 9, поэтому там доступен и [System.Buffers.Text.Base64Url]::EncodeToString($bytes); в 7.4 и Windows PowerShell 5.1 обходитесь заменами, показанными выше. Где используется каждый вариант, читайте в статье Base64 и Base64URL.
Закодировать и декодировать файл
[Convert]::ToBase64String([IO.File]::ReadAllBytes("$PWD/hello.txt")) # aGVsbG8K
[IO.File]::WriteAllBytes("$PWD/copy.txt", [Convert]::FromBase64String("aGVsbG8K"))
Префикс $PWD/ здесь не для красоты. Методы .NET разрешают относительные пути от рабочего каталога процесса, а Set-Location его не меняет. Если запустить pwsh в /tmp, выполнить Set-Location /w, а затем [IO.File]::ReadAllBytes("hello.txt"), получите FileNotFoundException, хотя файл лежит прямо там.
Через командлеты байты тоже можно прочитать, но параметр в разных версиях называется по-разному:
# PowerShell 7+
[Convert]::ToBase64String((Get-Content hello.txt -AsByteStream -Raw))
# Windows PowerShell 5.1
[Convert]::ToBase64String((Get-Content hello.txt -Encoding Byte -Raw))
Без -Raw командлет Get-Content возвращает по объекту на каждый байт, и на больших файлах это очень медленно.
Большие файлы
ReadAllBytes загружает файл целиком. Если в нём сотни мегабайт, пропустите его потоком через ToBase64Transform из .NET:
$in = [IO.File]::OpenRead((Resolve-Path big.bin))
$out = [IO.File]::Create("$PWD/big.b64")
$b64 = [Security.Cryptography.CryptoStream]::new(
$out, [Security.Cryptography.ToBase64Transform]::new(), 'Write')
$in.CopyTo($b64)
$b64.Dispose(); $in.Dispose()
Освободите CryptoStream до того, как использовать вывод, иначе последнего блока и дополнения не будет. Наш тестовый файл в 300 КБ дал ту же строку, что и ToBase64String. Декодирование устроено так же, только с FromBase64Transform в режиме 'Read'; этот вариант есть на странице Base64 в C#.
В Windows работают также certutil -encode in.bin out.txt и certutil -decode, но -encode оборачивает вывод в строки -----BEGIN CERTIFICATE-----, так что перед использованием в другом месте их нужно удалить.
Переносы строк MIME
Передайте InsertLineBreaks, чтобы получить строки по 76 символов с CRLF:
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("x" * 60), 'InsertLineBreaks')
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
# eHh4
Ошибки и проверка
На некорректных данных .NET бросает System.FormatException, а PowerShell оборачивает его в MethodInvocationException:
[Convert]::FromBase64String("aGk")
# Exception calling "FromBase64String" with "1" argument(s): "The input is not a
# valid Base-64 string as it contains a non-base 64 character, more than two
# padding characters, or an illegal character among the padding characters."
aGk не проходит, потому что не хватает дополнения. То же сообщение появляется при URL-safe символах, префиксах data: и кавычках, скопированных вместе со строкой. Простая функция проверки:
function Test-Base64([string]$s) {
try { $null = [Convert]::FromBase64String($s); $true } catch { $false }
}
Test-Base64 "aGk=" # True
Test-Base64 "aGk" # False
Если строка из лога или письма не хочет декодироваться, починка Base64 уберёт кавычки и переносы строк и исправит дополнение. Причины объяснены в статье об ошибках Base64.
Версии
[Convert]::ToBase64String/FromBase64String: любая версия PowerShell, включая Windows PowerShell 5.1.Get-Content -AsByteStream: PowerShell 6+.-Encoding Byteтам убрали.- Класс
Base64Url: PowerShell 7.5+ (на основе .NET 9).
Вопросы и ответы
Какую кодировку ждёт -EncodedCommand?
UTF-16LE, которую .NET называет [Text.Encoding]::Unicode. Команда, закодированная из байтов UTF-8, декодируется в мусор и падает с ошибкой разбора.
Как безопасно декодировать подозрительную команду -enc?
Декодируйте её, не запуская: [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($b)), или вставьте в декодер закодированных команд PowerShell, который только показывает скрипт.
Есть ли командлет для Base64 вроде ConvertTo-Base64?
В самом PowerShell нет. Некоторые модули его добавляют, но [Convert]::ToBase64String и [Convert]::FromBase64String работают везде без всякой установки.
Почему [IO.File]::ReadAllBytes говорит, что файл не найден?
.NET разрешает относительные пути от рабочего каталога процесса, а Set-Location его не меняет. Передавайте (Resolve-Path file.bin) или "$PWD/file.bin".