Загрузить файл
  1. base64hub
  2. Base64 в коде
  3. PowerShell
Код

Base64 в PowerShell

Командлета для Base64 в PowerShell нет: он напрямую вызывает класс [Convert] из .NET. Главная сложность – выбрать правильную кодировку текста. Результаты ниже получены на PowerShell 7.4 в Linux.

Закодировать и декодировать строку

Переведите строку в байты с явно указанной кодировкой, затем вызовите [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".

Читайте также