编码和解码字符串
先用显式指定的编码把字符串转成字节,再调用 [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==
发送给 Web API、配置文件或其他语言的内容,请用 UTF-8。只有 -EncodedCommand 才用 Unicode。如果解码出的字符串每个字母之间都有空隙,说明它是 UTF-16;Base64 转 UTF-16 工具可以解码这类字符串。
在 REST 调用中使用 Basic Auth 请求头:
$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 可以通过 -EncodedCommand(简写 -enc、-e)接受 Base64 编码的脚本。它的用途是让带引号和特殊字符的脚本能穿过 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 安全的 Base64
[Convert] 没有内置的 URL 安全模式。自己替换字符并处理填充:
$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 不会改变它。在 /tmp 中启动 pwsh,执行 Set-Location /w,再调用 [IO.File]::ReadAllBytes("hello.txt"),即使文件就在那里,也会抛出 FileNotFoundException。
用 cmdlet 也能读取字节,但参数在不同版本间有变化:
# 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 会加载整个文件。对于几百 MB 的文件,请通过 .NET 的 ToBase64Transform 流式处理:
$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 KB 的测试文件得到的字符串与 ToBase64String 相同。解码的方式一样,在 'Read' 模式下使用 FromBase64Transform 即可;C# 中的 Base64 页面给出了那个版本。
在 Windows 上,certutil -encode in.bin out.txt 和 certutil -decode 也能用,但 -encode 会用 -----BEGIN CERTIFICATE----- 行包裹输出,在别处使用前要先去掉。
MIME 换行
传入 InsertLineBreaks,就能得到用 CRLF 分隔的 76 字符行:
[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 安全字符、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 编码命令解码器 中,它只显示脚本内容。
有没有 ConvertTo-Base64 这样的 Base64 cmdlet?
PowerShell 本身没有。有些模块提供了,但 [Convert]::ToBase64String 和 [Convert]::FromBase64String 无需安装任何东西,在哪里都能用。
为什么 [IO.File]::ReadAllBytes 提示找不到文件?
.NET 以进程的工作目录为基准解析相对路径,而 Set-Location 不会改变它。请改为传入 (Resolve-Path file.bin) 或 "$PWD/file.bin"。