Приходится иметь дело с форматом Base64? Тогда этот сайт идеально вам подойдет! Воспользуйтесь нашим невероятно удобным онлайн-инструментом для кодирования или декодирования ваших данных.

Декодирование Base64 в PowerShell: полное руководство

Где-то в строке лога, в конфиге или в сообщении об ошибке вы на него наталкиваетесь: длинный ряд букв и цифр, изредка с плюсом или слэшем, и один-два знака равенства, подозрительно припаркованных в самом конце. Выглядит как шум. Шумом он не является. Это Base64, и вы уже знаете, чего хотите: то, что он прячет.

Base64 - это перевод, а не сжатие и не замок. Он переписывает любую последовательность байтов в печатаемый текст, используя алфавит из 64 символов плюс знак равенства в качестве завершающего заполнения: четыре символа на каждые три входных байта (поэтому закодированные данные примерно на 33% крупнее исходных). Главная страница этого сайта подробно разбирает алфавит, побитовую арифметику и варианты формата, так что в этой статье мы займёмся тем, где именно PowerShell делает разницу: единственным методом .NET, который вы будете вызывать, правилами, которые он налагает, и десятком уголков реальной работы, где декодирование в PowerShell становится интересным.

Метод и его контракт

PowerShell не имеет собственного cmdlet для Base64. Работу выполняет метод класса .NET, который входит в платформу ещё с .NET Framework 1.1, выпущенного в 2003 году, за три года до выхода самого PowerShell:

$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!

Вот и весь API: одна строка на входе, один байтовый массив на выходе. Он работает в любом PowerShell на любой операционной системе, в Windows PowerShell 5.1 и в PowerShell 7 на Windows, Linux и macOS, потому что это просто .NET. Контракт короткий и легко запоминается, поэтому вот он в виде таблицы:

Вход Что вы получите
$null Пустой массив, без ошибки. PowerShell молча превращает $null в пустую строку перед вызовом
Пустая строка Пустой массив, без ошибки
Корректные данные byte[], никогда не строка, даже если данные - это текст
Некорректные данные FormatException, обёрнутый для вас в MethodInvocationException

Одно предупреждение перед тем, как писать обработку ошибок: у этого FormatException всего одно сообщение, и оно покрывает три разных греха. Символ вне алфавита, больше двух символов заполнения или непробельный символ, спрятанный среди заполнения, - всё это порождает ровно одно и то же предложение. Когда вы его увидите, сообщение не скажет, какой именно грех вы совершили, так что придётся вернуться и перечитать свой ввод:

try {
  [System.Convert]::FromBase64String("SGV!G8s=")
}
catch {
  $real = $_.Exception.InnerException
  $real.GetType().Name
  # FormatException
  $real.Message
}

И у правила «длина кратно четырём» есть один край, который удивляет людей при первой же встрече. Четыре символа без заполнения - это вполне корректно: просто лишние биты последнего символа отбрасываются. Три символа - не кратное четырём число, и такой ввод отклоняется:

[System.Convert]::FromBase64String("SGVs").Count
# 3: четыре символа без заполнения - это нормально
[System.Convert]::FromBase64String("SGV")
# FormatException: три символа - не кратное четырём число

Что декодер принимает, а что нет

Декодер строг в отношении алфавита и снисходительный ровно в одном конкретном вопросе. Допустимые символы - это 64 цифры Base64 (от A до Z, от a до z, от 0 до 9, плюс и слэш) и знак равенства в качестве завершающего заполнения. Игнорируются ровно четыре пробельных символа - где бы и как часто они ни появлялись: табуляция, перевод строки, возврат каретки и пробел. Официальная документация .NET перечисляет их по их Unicode-названиям, а это значит, что перед вами задокументированная гарантия, а не случайное везение.

На практике это суперсила. MIME, почковое кодирование, сделавшее Base64 знаменитым, переносит закодированные строки на границе 76 символов, поэтому данные, прошедшие через письмо, тикет или лог-файл, обычно приходят, разбитыми на множество строк. Декодеру всё равно. Вставляйте как есть:

$wrapped = "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4gQmFzZTY0IHRleHQg`r`n" +
           "YXJyaXZlcyB3cmFwcGVkIGF0IHNldmVudHktc2l4IGNvbHVtbnMgaW4gbWFpbCwgc28gdGhlIGRl`r`n" +
           "Y29kZXIgbXVzdCBub3QgY2FyZS4="
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($wrapped))
# The quick brown fox jumps over the lazy dog. Base64-текст приходит
# переломанным при 76 символах в письме, так что декодеру всё равно.

Всё остальное, что не символ алфавита, - жёсткий стоп. Самые частые нарушители в дикой природе - неразрывный пробел (любимец текста, вставленного с веб-страницы) и метка порядка байтов (невидимая метка, которая цепляется к вам, когда файл был прочитан с неправильной кодировкой). Ни то, ни другое не считается пробельным символом с точки зрения этого метода, поэтому оба варианта приводят к исключению:

try {
  [System.Convert]::FromBase64String("SGVs`u{00A0}G8=")
}
catch {
  $_.Exception.InnerException.GetType().Name
  # FormatException
}

Такая строгость - осознанный выбор, а не упрямство. RFC 4648, стандарт, закрепивший Base64 в 2006 году, предписывает реализациям отклонять символы вне алфавита, если протокол явно не разрешает снисходительность, потому что декодер, молча глотающий чужие символы, можно превратить в скрытый канал для контрабанды данных мимо всего, что проверяет только алфавит. Декодер .NET следует строгому правилу, и вам обычно этого и нужно.

Байтовый массив - это не строка

Метод намеренно останавливается на байтовом массиве. Что эти байты означают - это второе решение, которое можете принять только вы, и ошибиться в нём - значит совершить самый известный промах в работе с Base64 в PowerShell. Допустимое по умолчанию допущение, UTF-8, подходит почти для всего, что есть в интернете, и весь цикл занимает два вызова:

$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!

Кодировки, к которым вы будете обращаться на самом деле, и что делает каждая из них, когда вы ошиблись:

Кодировка Когда использовать Если вы ошиблись
UTF8 Веб-API, JSON, JWT, всё современное. Безопасный вариант по умолчанию Текст в Latin-1 или UTF-16 вернётся кракозябрами
Unicode (UTF-16LE) Данные пришли из Windows-инструментов, из значения реестра или из строки .NET, закодированной до отправки У каждого символа появляется невидимый зазор, потому что вы прочитали один байт там, где задумано два
ASCII Классические учётные данные HTTP Basic и другие протоколы, гарантированно 7-битные Всё, что выше значения 127, превращается в вопросительный знак
Latin1 Легаси-европейский текст, старше UTF-8 Многобайтовые последовательности UTF-8 рассыпаются на несколько неверных букв
Default Почти никогда. Это системная кодировка машины Ваш скрипт будет по-разному работать при каждом региональном параметре Windows

Классический провал - текст UTF-8, декодированный как UTF-16. Байты настоящие, метод доволен, а результат всё равно мусор:

# "SGk=" - это байты "Hi" в UTF-8
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("SGk="))
# Один нечитаемый символ: 2 байта UTF-8 прочитаны как одна 2-байтовая единица UTF-16

Практическое правило: если декодированный текст выглядит так, будто у каждого символа невидимый зазор вокруг, или будто это совсем другой алфавит, - вы ошиблись ровно на одну кодировку. Спросите, где данные были созданы, и при сомнениях доверяйте UTF-8, но глазами проверьте первые несколько символов. И решайте кодировку до того, как декодировать, а не после того, как кракозябры появятся в вашем логе.

base64url: алфавит, который хорошо себя ведёт в URL

Родственник Base64 встретится вам в каждом API-токене, JWT и идентификаторе, вшитом в URL, с которым вам доведётся иметь дело. Плюс и слэш стандартного Base64 законны в URL только после процентного кодирования, а заполнение из знаков равенства выглядит как разделитель полей. Поэтому RFC 4648 определил алфавит, безопасный для URL и имён файлов: те же 64 символа, только плюс становится дефисом, а слэш - подчёркиванием. Заполнение обычно отбрасывают совсем, потому что длина данных делает его ненужным. В RFC внимательно оговорено, что этот вариант следует называть base64url, а не просто «base64», и дальше в этом разделе мы следуем именно этому.

У .NET есть выделенный класс для этого варианта, System.Buffers.Text.Base64Url, добавленный в .NET 9 вместе с быстрыми методами кодирования и декодирования, построенными целиком на параметрах ReadOnlySpan<T>. Актуальный PowerShell (7.4 и новее, когда он работает на версии .NET, где этот класс уже есть) сегодня действительно может вызывать эти перегрузки со span-параметрами напрямую: связыватель методов теперь выполняет неявное преобразование массив/строка в span, так что [System.Buffers.Text.Base64Url]::DecodeFromChars("--__AQI") работает без лишних церемоний. Всегда так не было: Windows PowerShell 5.1 и старые выпуски PowerShell 7.x вообще не умели привязываться к span-параметрам, а до .NET 9 класса попросту не существовало, поэтому любому скрипту, который должен работать на 5.1, на старом 7.x или на хосте с версией .NET до девятой, по-прежнему нужна переносимая версия: поменяйте два символа местами и восстановите заполнение перед тем, как передать текст стандартному декодеру. Заполнение, которое нужно добавить, - это ровно то, что делает длину кратной четырём:

$token = "--__AQI"  # base64url, без заполнения
$standard = $token.Replace("-", "+").Replace("_", "/")
$pad = 4 - ($standard.Length % 4)
if ($pad -eq 4) { $pad = 0 }
$standard = $standard.PadRight($standard.Length + $pad, "=")
$bytes = [System.Convert]::FromBase64String($standard)
$bytes -join ","
# 251,239,255,1,2

В этом небольшом блоке живут две ловушки. Первая - арифметика заполнения: данные, чья длина и так кратна четырём, заполнения не требуют, и именно страж -eq 4 держит выражение честным. Вторая - направление: когда вы только декодируете, вы добавляете заполнение и меняете символы; вы никогда не убираете заполнение со входных данных стандартного Base64, потому что стандартные декодеры ожидают, что оно на месте. Если источник - JWT или API-токен, то перед вами base64url без заполнения, и рецепт выше - ровно та форма, которая вам нужна.

Открываем JWT без ключей

JSON Web Token - это три сегмента base64url, склеенные точками: заголовок, данные и подпись. Первые два - обычный JSON, и поскольку Base64 не является шифрованием, прочитать их может любой, у кого есть токен. Это особенность, а не дефект: токен рассчитан на то, чтобы его осматривали, и именно подпись делает его неподдельным. PowerShell превращает этот взгляд в трёхстрочник:

$jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
$parts = $jwt.Split(".")
function Decode-UrlSegment([string]$segment) {
  $standard = $segment.Replace("-", "+").Replace("_", "/")
  $pad = 4 - ($standard.Length % 4)
  if ($pad -eq 4) { $pad = 0 }
  $standard = $standard.PadRight($standard.Length + $pad, "=")
  return [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($standard))
}
Decode-UrlSegment $parts[0] | ConvertFrom-Json | ConvertTo-Json -Compress
Decode-UrlSegment $parts[1] | ConvertFrom-Json
# свойство name:
(Decode-UrlSegment $parts[1] | ConvertFrom-Json).name
# John Doe

Три вещи, которые стоит держать в уме. Третий сегмент, подпись, тоже записан в base64url, но декодируется в байты бинарной подписи, а не в текст, так что не ждите там красивого JSON. Заголовок обычно просто сообщает, каким алгоритмом подписан токен (HS256, RS256, ...), а заголовок, в котором написано none, - это красный флаг, а не удобство. И чтение данных - не значит доверять им: base64 позволяет вам увидеть заявления, а делает их подлинными только подпись. Если ваша работа - принимать токены, проверяйте подпись ключом издателя; если ваша работа - отлаживать один из них, кода выше достаточно.

Файлы, PEM и долгий путь к байтам

Самая распространённая форма - текстовый файл, в котором лежит Base64 чего-то побольше: резервной копии, скачанного бинарного файла, сериализованного объекта. Весь путь занимает четыре строки, и современный способ получить вывод - настоящий байтовый массив, а не текстовые догадки:

$encoded = Get-Content -Path ./payload.b64 -Raw
$encoded = $encoded.Trim()
$bytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("./payload.bin", $bytes)
$bytes.Length
# сколько байтов содержал этот текст

Обратное чтение исходного бинарного файла - то место, где PowerShell 6 и новее оправдывают своё существование. Параметр -AsByteStream читает сырые байты, а в сочетании с -Raw отдаёт вам настоящий byte[] одним движением:

$bytes = Get-Content -Path ./photo.png -AsByteStream -Raw
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path ./photo.b64 -Value $encoded -NoNewline
$bytes.Length
# исходный размер, до 33-процентного текстового налога

Уберите -Raw - и получите поток отдельных объектов байтов (Object[] при присвоении), что подходит для осмотра, но неправильно для передачи в методы .NET, ожидающие массив. А в Windows PowerShell 5.1 параметра -AsByteStream нет вообще, поэтому на 5.1 надёжное чтение - это [System.IO.File]::ReadAllBytes(), который существует везде.

PEM - это бронированный родственник, которого вы знаете из каждого сертификата и закрытого ключа: тело стандартного Base64, обычно сложенное по 64 символа, между строками -----BEGIN ... и -----END .... Броня - это текст; тело - это данные. Снимите броню, склейте строки, декодируйте:

$pem = Get-Content -Path ./certificate.pem -Raw
$body = ($pem -split "`n") | Where-Object { $_ -notmatch "^-----" } | ForEach-Object { $_.Trim() }
$der = [System.Convert]::FromBase64String(($body -join ""))
$der.Length
# бинарный размер сертификата в DER

Поскольку стандартный декодер в любом случае игнорирует пробельные символы, -join "" - это двойная страховка, а не требование, но явное указание в скрипте того, что именно вы отбрасываете, делает его поведение одинаковым на любой машине и при любой конвенции окончаний строк. В противоположном направлении, при свёртке байтов DER в PEM, нужен всего лишь Base64-кодер плюс две строки текста, и статья про кодирование на сестринском сайте показывает 64-колоночный перенос целиком.

Сертификаты и коробка инструментов Windows

Сертификаты - самые тяжёлые Base64-граждане в повседневной работе, и PowerShell способен взять в руки всю эту семью. Файл PFX - это бинарный пакет из сертификата и закрытого ключа, и именно этот формат вы чаще всего находите, лежащим Base64-текстом в конфиг-файлах и скриптах развёртывания. Обратное декодирование в живой сертификат - это однострочник с типом .NET, и он работает кроссплатформенно в PowerShell 7:

$bytes = [System.Convert]::FromBase64String($pfxText)
$password = ConvertTo-SecureString "secret" -AsPlainText -Force
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($bytes, $password)
$cert.Subject
# CN=example.org
$cert.NotAfter
# когда он перестаёт быть действующим

PowerShell 7 также поставляется с Get-PfxCertificate, который читает файл PFX прямо с диска с параметром -Password, так что для файлов на диске можно пропустить ручное декодирование вовсе. Голый сертификат (без ключа) ещё проще: байты DER идут прямо в тот же тип X509Certificate2, без какого-либо пароля.

Помимо языка, стоит знать два нативных инструмента. На Windows certutil -decode infile.b64 outfile декодирует Base64-файл с семантикой файл-на-вход, файл-на-выход (добавьте -f, чтобы перезаписать), что делает его выбором для быстрых решений в обычном командном окне. У его брата certutil -encode есть флаг, который стоит запомнить: -unicodetext преобразует входной текст в UTF-16 до того, как закодировать его Base64, пряча целое решение о кодировке внутри одного переключателя. На Linux и macOS классическая утилита - base64 -d, которая декодирует файл или стандартный ввод, по умолчанию пропуская переводы строк; в GNU coreutils добавьте -i, если данные ещё содержат пробелы, табуляции или CRLF из Windows-почты.

Команды в Base64-конверте

У PowerShell есть встроенная причина говорить на Base64 с версии 1.0: параметр -EncodedCommand самого хоста. Вы передаёте pwsh Base64-строку, он декодирует байты как UTF-16LE, и результат выполняется как команда. Официальное назначение, прямо из документации, - отправлять команды, которым нужны сложные кавычки или фигурные скобки, не воевая с правилами кавычек внешней оболочки:

$command = "Write-Host encoded-hello"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgAGUAbgBjAG8AZABlAGQALQBoAGUAbABsAG8A
pwsh -NoProfile -EncodedCommand $encoded
# encoded-hello

Прочтите вторую строку этого примера внимательно, потому что на неё спотыкаются все: данные должны быть в UTF-16LE, то есть [System.Text.Encoding]::Unicode. Если вместо этого закодировать команду в UTF-8, PowerShell с удовольствием декодирует её как UTF-16LE и выполнит команду, состоящую из кракозябр, а сообщение об ошибке, которое он выдаст, - точный портрет этой ошибки:

$wrong = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($command))
pwsh -NoProfile -EncodedCommand $wrong
# Ошибка: стена из нечитаемых символов, «The term ... is not recognized...»

Именно из-за этого же механизма команды безопасности интересуются Base64 в PowerShell. Длинный непроницаемый токен, переданный в -EncodedCommand, - распространённая форма для автоматизированных инструментов, и именно поэтому продукты защиты конечных точек декодируют такие данные перед выполнением: в Base64 нет ничего, что скрывало бы команду от декодера, он скрывает её только от человека, читающего список процессов. Если вы генерируете закодированные команды для собственной автоматизации, храните исходную команду рядом с токеном, потому что сам токен не объяснится в три часа ночи.

Декодирование, когда ввод огромный

Для повседневных размеров подход с единственным методом и есть быстрый. Бинарный файл в пять мегабайт становится строкой примерно из 6,9 миллиона символов, и декодирование этой строки занимает однозначные миллисекунды на современной машине. В самой документации .NET сказано, что FromBase64String предназначен для обработки единой строки, содержащей все данные; это правда, и это работает хорошо вплоть до очень больших пределов, потому что метод оперирует строкой на месте, без сколько-нибудь значимых лишних копий.

Когда данные больше, чем вы готовы удерживать в одной строке, или приходят потоком (загрузка, сокет, огромный лог), задокументированный инструмент - это System.Security.Cryptography.FromBase64Transform в обёртке CryptoStream: вы подаёте ему Base64-текст и читаете из него декодированные байты, а живым в любой момент остаётся только небольшой буфер. Обратите внимание: TransformStream, C#-помощник для этой задачи, - это расширение метода, а PowerShell расширения методов не видит, поэтому вы создаёте CryptoStream напрямую:

$inputStream = [System.IO.File]::OpenRead("./payload.b64")
$transform = [System.Security.Cryptography.FromBase64Transform]::new()
$stream = [System.Security.Cryptography.CryptoStream]::new(
  $inputStream, $transform, [System.Security.Cryptography.CryptoStreamMode]::Read)
$destination = [System.IO.File]::Create("./payload.bin")
$buffer = New-Object byte[] 65536
while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
  $destination.Write($buffer, 0, $read)
}
$destination.Dispose()
$stream.Dispose()
$inputStream.Dispose()

Для девяноста процентов задач простой путь остаётся правильным: прочитайте весь текстовый файл с помощью Get-Content -Raw, обрежьте пробельные символы, декодируйте, запишите байты. Обращайтесь к потоковой версии, когда файл слишком велик, чтобы его удобно держать в памяти, или когда данные прибывают по кусочкам. И не пытайтесь циклом ходить по строкам и декодировать каждую строку по отдельности: группы из четырёх символов Base64 не уважают ваши переводы строк, поэтому строка, разрезающая группу посередине, не декодируется сама по себе. Прочитайте весь текст, а потом декодируйте один раз.

Ловушки, которые съедают целые полдни

  • Угадывание кодировки. UTF-8, прочитанный как UTF-16, или Latin-1, прочитанный как UTF-8, производит уверенные кракозябры. Решайте кодировку по источнику данных, ставьте по умолчанию UTF-8 и посматривайте на первые несколько декодированных символов, прежде чем доверять остальному.
  • Невидимые символы с веба. Неразрывный пробел или метка порядка байтов, вставленные со страницы или из письма с форматированием, - это чужой символ для декодера, и он бросает банальное FormatException. Прогоните ввод через .Trim() и проверку на непечатаемые символы перед декодированием.
  • Замешательство вокруг заполнения. Стандартный Base64 приходит с = или == в конце; base64url из токенов приходит без него. Подсовывание одного рецепту, сделанному для другого, - самый частый тихий сбой в API-работе, и проверка длины из раздела про base64url - тот самый страж.
  • Одно сообщение, три преступления. Поскольку сообщение FormatException покрывает и плохие символы, и лишнее заполнение, и загрязнённое заполнение всё разом, блоки catch, которые только записывают сообщение в лог, водят вас по кругу. Логируйте ещё и длину ввода, и первый проблемный участок.
  • Ожидание строки в ответ. Результат всегда - байтовый массив. В момент, когда вы начинаете напрямую форматировать его как строку, вы получаете список чисел, а не текст. Конвертируйте явной кодировкой, один раз, в самом конце.
  • Файловое значение по умолчанию в 5.1. Windows PowerShell 5.1 читает файлы без BOM кодировкой ANSI системы, тогда как PowerShell 7 предполагает UTF-8. Если ваш скрипт читает текстовый файл Base64 на 5.1, а файл в UTF-8 и вокруг данных есть не-ASCII, порча происходит до того, как декодер вообще что-то увидит.
  • Отношение к Base64 как к замку. Это перевод. Пароль, токен или секрет в Base64 - это простой текст в костюме, и любой декодер на планете, включая этот, открывает его одной строкой.

Привычки, которые держат скрипты честными

  • Обрезайте внешний ввод перед декодированием. Один .Trim() убирает больше производственных инцидентов, чем любой обработчик ошибок.
  • Валидируйте перед декодированием, когда источник недоверен: после удаления четырёх допустимых пробельных символов строка должна содержать только символы алфавита и не более двух знаков равенства в конце. Быстрая проверка регулярным выражением превращает таинственное исключение в чёткое сообщение об отклонённом вводе.
  • Держите байты байтами вплоть до самого последнего шага. Декодируйте один раз, передайте byte[] файловому API или кодировщику, которому оно нужно, и только тогда конвертируйте в текст осознанно выбранной кодировкой.
  • Логируйте длины, а не данные. Размер ввода и размер декодированного вывода рассказывают почти всё об ошибке декодирования, без вставки потенциально чувствительных данных в лог.
  • Для всего, что пересекает провод, записывайте, в каком алфавите оно - стандартном или base64url, - и какое правило заполнения, той же строкой кода, которая его декодирует. Ваш будущий я - тот, кто прочитает эту заметку.

Как PowerShell унаследовал свой декодер

Кратчайшая истинная история Base64 в PowerShell состоит в том, что PowerShell не писал его никогда. Метод, которым вы пользуетесь, Convert.FromBase64String, вышел вместе с .NET Framework 1.1 в 2003 году, и каждый PowerShell начиная с версии 1.0 в ноябре 2006 года просто открывал наружу .NET, на котором он работает. Пока проект строился, его звали Monad, впервые он был показан публично на Professional Developers Conference в октябре 2003 года, а к моменту выхода пара кодировщик-декодер .NET, которую он оборачивает, уже три года жила повседневной жизнью.

Сам формат был стандартизирован в тот же год, что и запуск оболочки. RFC 4648, опубликованный в октябре 2006 года, - тот самый документ, который зафиксировал алфавит, правила заполнения, ожидание строгого декодирования и вариант base64url, и по сей день он описывает ровно то поведение, которое реализует FromBase64String. Когда в августе 2016 года PowerShell стал открытым и кроссплатформенным как PowerShell Core, декодер приехал вместе с ним на Linux и macOS без единого изменения, потому что менять было нечего.

Единственное подлинное дополнение - поддерживаемый сообществом модуль Microsoft.PowerShell.TextUtility из PowerShell Gallery, чей cmdlet ConvertFrom-Base64 оборачивает тот же метод .NET и добавляет переключатель -AsByteArray плюс текстовый режим по умолчанию, который декодирует как UTF-8. Установите его командой Install-Module -Name Microsoft.PowerShell.TextUtility, если вам нравится форма cmdlet, но одно предупреждение: модуль сейчас в архиве и больше не поддерживается активно, и это ещё одна причина, по которой встроенный метод остаётся рекомендацией для новых скриптов.

Факты, которые стоит запомнить

  • Декодер игнорирует табуляции, переводы строк, возвраты каретки и пробелы в любом месте ввода. Сто сложенных строк декодируются ровно так же, как одна длинная строка.
  • $null и пустая строка оба декодируются в пустой массив без единого стона, что делает FromBase64String необычно снисходительным на краях.
  • Единственное сообщение FormatException покрывает три разных режима отказа. Когда оно срабатывает, ответ лежит во вводе, а не в сообщении.
  • "SABpAA==" - это строка Hi в собственном внутреннем кодировании PowerShell, UTF-16LE. Она вдвое длиннее UTF-8-кодирования тех же двух букв, и именно это соотношение - отпечаток Windows-нативного текста в любом Base64, который вам доведётся прочитать.
  • -EncodedCommand существует с первого же релиза PowerShell, и его данные предписано записывать в UTF-16LE, а не UTF-8. Закодируйте неверной кодировкой - и оболочка с удовольствием выполнит ваши кракозябры.
  • Новые span-основанные Base64-помощники .NET, включая класс Base64Url, были недоступны из старых выпусков PowerShell, потому что span - это byref-подобные типы, к которым связыватель методов не умел привязываться. Это изменилось: актуальный PowerShell (7.4+, на версии .NET, достаточно новой, чтобы поставлять этот класс) привязывает аргумент-массив или строку к параметру ReadOnlySpan<T> без единой жалобы, так что прямой вызов сегодня работает. Обмен двух символов оправдывает себя как версия, которая ещё и работает на Windows PowerShell 5.1 и старых хостах, а не как последняя оставшаяся дорога.
  • Get-Content -AsByteStream без -Raw отдаёт вам поток объектов байтов, а не байтовый массив. Добавьте -Raw - и тип будет ровно тем, что ожидают методы .NET.

Долгий путь вокруг

Всё в этой статье - о том, как взять Base64-строку и вернуть свои данные. Зеркальная операция, превращение данных в Base64, выглядит как однострочник, пока вы не столкнётесь с тем, что строки PowerShell - не байты, что UTF-16 удваивает ваш размер, что у переноса строк есть две штатные ширины, а у вывода base64url - собственная двухсимвольная хирургия. Тому направлению посвящён отдельный полный разбор, со своими ловушками и своей историей, в связанной статье на сестринском сайте, «Кодирование Base64 в PowerShell», на которую эта страница ссылается ниже.

Последнее обновление: 2026-09-07

Связанная статья: Кодирование Base64 в PowerShell: полное руководство