파일 업로드
  1. base64hub
  2. 코드로 다루는 Base64
  3. PowerShell
코드

PowerShell에서 Base64 사용하기

PowerShell에는 Base64 cmdlet이 없고 .NET의 [Convert] 클래스를 직접 호출합니다. 관건은 알맞은 텍스트 인코딩을 고르는 것입니다. 아래 출력은 Linux의 PowerShell 7.4 기준입니다.

문자열 인코딩과 디코딩

인코딩을 명시해서 문자열을 바이트로 바꾼 다음 [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 변환 도구로 디코딩할 수 있습니다.

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-safe Base64

[Convert]에는 내장 URL-safe 모드가 없습니다. 문자와 패딩을 직접 바꾸세요.

$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을 dispose하세요. 그렇지 않으면 마지막 블록과 패딩이 빠집니다. 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-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: Windows PowerShell 5.1을 포함한 모든 PowerShell 버전.
  • Get-Content -AsByteStream: PowerShell 6 이상. 이 버전에서 -Encoding Byte가 제거되었습니다.
  • Base64Url 클래스: PowerShell 7.5 이상(.NET 9 기반).

자주 묻는 질문

-EncodedCommand는 어떤 인코딩을 기대하나요?

.NET에서 [Text.Encoding]::Unicode라고 부르는 UTF-16LE입니다. 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"을 넘기세요.

계속 읽기