Base64 형식을 다루어야 하나요? 그러면 여러분에게 이 웹사이트가 딱 맞네요! 저희 웹사이트의 아주 편리한 온라인 도구를 사용하여 데이터를 인코딩하거나 디코딩해보세요.

PowerShell에서의 Base64 인코딩: 완전한 가이드

문자열, 파일, 인증서, 토큰이 있고, 네트워크 너머의 상대편은 그것을 문자와 숫자의 긴 행렬로 요구합니다: 인쇄 가능하게, 이메일이나 URL, 설정 파일에 붙여 넣을 수 있게, 트랜스포트를 깨뜨리는 바이너리 바이트 하나 없이. 그것이 Base64입니다. 번역이지, 압축도 잠금도 아닙니다: 입력 바이트 3개가 출력 문자 4개가 되므로, 보내는 텍스트는 출발한 것보다 약 33% 커지고, 64자 알파벳에 끝 패딩으로서의 등호를 씁니다.

이 사이트의 홈 페이지가 알파벳과 비트 계산, 변형들을 자세히 다룹니다. 이 글은 PowerShell 쪽에서 인코딩 방향을 다룹니다: 당신이 부를 .NET 메서드 하나, 첫 스크립트에서 모두가 넘어지는 빠진 단계, 프로토콜마다 다른 줄바꿈 관례, URL-safe 알파벳, 그리고 PowerShell 인코딩이 신중한 사람을 보답하고 무심한 사람을 벌하는 실제 업무 몇 가지입니다.

메서드와 빠진 단계

PowerShell에는 자체 Base64 cmdlet이 없습니다. 일을 해 주는 것은 .NET 프레임워크의 메서드이며, 이 메서드는 2003년 .NET Framework 1.1부터 프레임워크의 일부였습니다. PowerShell 자체가 출시되기 3년 전의 일입니다:

$bytes = [System.Text.Encoding]::UTF8.GetBytes("Hello")
[System.Convert]::ToBase64String($bytes)
# SGVsbG8=

전체 API가 이것입니다: 모든 운영체제의 모든 PowerShell에서, 바이트 배열 하나가 들어가 문자열 하나가 나옵니다. 결국 .NET이기 때문이죠. 빠진 단계는 예시의 첫 번째 줄이고, 초보자들이 첫 시간을 잃어버리는 곳입니다. 이 메서드는 당신의 문자열을 받지 않습니다. 바이트를 받습니다. '내 문자열은 어떤 바이트를 뜻하는가'라는 질문은 당신만이 답할 수 있는 인코딩 질문입니다. PowerShell에서 실제로 부를 수 있는 오버로드들의 계약은 이렇습니다:

넘기는 것 돌아오는 것
byte[] 길이가 요구하는 곳에서 = 패딩이 달린, 표준 Base64의 긴 한 줄
byte[]에 InsertLineBreaks를 더하면 같은 데이터, 76자에서 줄바꿈되고 줄 사이에는 CRLF
byte[], offset, count 배열의 요청된 조각만, 인코딩되어
"Hello" 같은 문자열 변환 예외. PowerShell은 스스로 문자열을 바이트 배열로 바꿀 수 없습니다
$null ArgumentNullException, MethodInvocationException에 싸여 전달됩니다

그 표에 없는 것을 알아채세요: '이 텍스트를 인코딩해 줘'라고 하는 오버로드는 없습니다. PowerShell에서 텍스트를 인코딩하는 것은 항상 두 단계 과정입니다. 인코딩을 결정하고, 바이트를 만든 다음, 그때서야 Base64 메서드가 대화에 들어옵니다. 스크립트에서 그 두 결정을 눈에 보일 정도로 따로 두세요. 두 번째는 보이지 않고, 첫 번째가 바로 버그가 사는 곳이니까요.

텍스트 인코딩: 먼저 인코딩을 고르라

무엇이든 현대의 인터넷을 건너는 것에 대해, 안전한 기본값은 UTF-8입니다. 웹 API, JSON, JWT, 지난 10년간 브라우저나 서버가 쓴 모든 것은 Base64 아래에 UTF-8 바이트를 기대하며, 두 단계 패턴은 당신이 기르고 싶은 습관입니다:

$text = "Hello, PowerShell!"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
$encoded = [System.Convert]::ToBase64String($bytes)
# SGVsbG8sIFBvd2VyU2hlbGwh

다른 인코딩에 손을 뻗는다면, 당신은 보통 레거시 시스템을 상대하는 중이고, 아래 표가 실용적인 가이드입니다:

인코딩 언제 쓰나 잘못 고르면
UTF8 웹 API, JSON, JWT, 현대의 모든 것. 기본 선택지 다른 쪽의 디코더는 당신의 텍스트 대신 모지바케를 본다
Unicode (UTF-16LE) 소비자가 .NET 문자열을 인코딩하는 Windows 또는 .NET 컴포넌트이거나, -EncodedCommand인 경우 페이로드가 소비자가 기대하는 것의 두 배 길이가 되고, 놀라움으로 가득 찰 것
ASCII HTTP Basic 인증 자격 증명 같은 클래식한 7비트 프로토콜 값이 127을 넘는 것은 인코딩이 일어나기도 전에 교체된다
Latin1 UTF-8 이전의 레거시 유럽 시스템 문자당 한 바이트, Latin-1이 아닌 모든 문자가 물음표가 된다

양쪽 방향으로 작용하는 유용한 디버깅 트릭이 있습니다: Base64의 패딩과 길이로 몇 바이트가 인코딩되었는지 알 수 있고, 디코딩된 텍스트의 외관으로 그것이 2바이트 세계에서 왔는지 1바이트 세계에서 왔는지 알 수 있습니다. 크기가 의심스럽게 짝수이고, 정상 문자와 빈 것처럼 보이는 문자가 번갈아 들어차 있는 페이로드는 보통 UTF-8 의상을 입은 UTF-16이거나, 그 반대입니다.

UTF-16의 놀라움

PowerShell은 내부적으로 문자열을 UTF-16으로 저장하며, 이 사실은 Base64 업무에 아주 특정한, 그리고 매우 흔한 한 곳에서 새어 나옵니다. 바로 이 곳입니다: 그 자체가 .NET이나 Windows 컴포넌트인 소비자를 위해 Base64를 쓰고, .NET 문자열이 그래서 Unicode로 인코딩하는 상황입니다. 몇몇 소비자에 대해서는 올바른 직감이고, 나머지 전부에게는 크기를 두 배로 만드는 실수입니다. 같은 보이는 문자 4개, 인코딩 두 가지:

$same = "Café"
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($same))
# Q2Fmw6k=  5바이트
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($same))
# QwBhAGYA6QA=  8바이트

같은 텍스트, 두 배 크기, 그리고 두 문자열은 서로 바꾸어 쓸 수 없습니다. 하나를 기대하고 다른 것을 받는 소비자는 크게 실패하지 않습니다. 그저 쓰레기를 읽을 뿐입니다. 이것에게 물리지 않게 해 주는 규칙은, 인코딩을 로컬의 세부 사항이 아니라 프로토콜의 일부로 취급하는 것입니다. 받는 쪽 시스템이 브라우저, REST API, 모던한 서버라면, 문서가 따로 말하지 않는 한 UTF-8입니다. -EncodedCommand를 통한 PowerShell 호스트 자체이거나, Windows 전용 파이프라인 안의 .NET 문자열이라면, UTF-16LE입니다. 프로토콜이 말하지 않는다면, 다른 쪽에 GetString을 무엇으로 부를 건지 물어보세요. 그것이 실제로 결정하는 질문이니까요.

숫자, 바이트, 그리고 그 외 모든 것

이 메서드는 바이트 배열을 받도록 선언되어 있지만, PowerShell의 타입 변환은 무엇이 그 자격이 되는지에 대해 관대하며, 그 끝을 알면 놀라움을 피할 수 있습니다:

[System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# AQID+vs=
[System.Convert]::ToBase64String([char[]]"Café")
# Q2Fm6Q==  문자당 한 바이트, 문자의 값을 숫자로
[System.Convert]::ToBase64String([int[]](72, 101, 108, 108, 111))
# SGVsbG8=
[System.Convert]::ToBase64String(123)
# ew==  배열 전체가 기대되는 자리에서 단일 숫자가 받아들여짐

그 블록에서 주목할 가치가 있는 끝이 두 개 있습니다. 문자 배열은 문자의 숫자 값을 사용해 문자당 한 바이트로 변환되며, 라틴 텍스트에 대해서는 이 트릭을 쓰는 레거시 시스템이 기대하는 것 그 자체이지만, 그 너머에서는 조용히 잘못된 바이트를 만들어 냅니다. 255를 넘는 정수는 대신 크게 실패하는 끝입니다: PowerShell의 바이트 변환은 0-255 범위를 벗어난 값을 예외로 거부하므로, 256은 데이터를 조용히 부패시키지 않고 캐스트 단계에서 스크립트를 멈춥니다. 출처가 숫자라면, 캐스트를 명시적으로 하세요: [byte[]](1, 2, 3)은 그것이 뜻하는 것을 정확히 말합니다.

메서드에 문자열을 넘기면, 다른 이유로 같은 종류의 큰 실패를 얻습니다: 문자열이 어떤 바이트를 뜻하는지 알 길 없으니, PowerShell의 변환 엔진이 포기하는 것입니다. $null을 넘기면, .NET은 아무것도 하기 전에 예외를 던집니다. 둘 다 올바른 동작이며, 둘 다 첫 섹션의 두 단계 패턴이 가질 가치가 있는 유일한 패턴인 이유입니다.

줄바꿈: 76, 64, 그리고 없음

기본적으로 인코더는 주는 데이터가 얼마든 긴 한 줄을 만듭니다. 몇 킬로바이트짜리 파일에는 괜찮습니다. 하지만 사람이 읽을 데이터, 이메일에 붙여 넣을 데이터, 소스 컨트롤 diff에서 비교할 데이터에는, 800만 자의 벽은 실용적인 문제이고, 관례는 줄바꿈입니다. PowerShell과 그것이 상대하는 프로토콜들은 세 가지 폭을 알며, 그것들은 서로 바꾸어 쓸 수 없습니다:

폭 누가 기대하나 줄 끝
76자 MIME, 메일, 대부분의 텍스트 전송. InsertLineBreaks의 기본값 CRLF
64자 PEM 파일: 인증서, 개인 키, -----BEGIN 가족의 나머지 관례상 LF
없음 API, 토큰, 설정 파일, 페이로드가 기계로 처리되는 모든 것 줄 자체가 없음

내장 줄바꿈은 한 매개변수 변경이고, 메일 스타일 페이로드가 원하는 것이 바로 그것입니다:

$text = "The quick brown fox jumps over the lazy dog. Base64 output arrives wrapped at different widths depending on who is reading it."
$wrapped = [System.Convert]::ToBase64String(
  [System.Text.Encoding]::UTF8.GetBytes($text),
  [Base64FormattingOptions]::InsertLineBreaks)
# 76자 한 줄, 그 사이 CRLF, MIME이 기대하는 그대로

PEM은 내장 기능의 예외입니다. OpenSSL과 -----BEGIN 생태계 전체가 64자에서 줄바꿈하고, 그 폭을 만들어 주는 .NET 플래그는 없기 때문입니다. 루프는 짧고, 표준 레시피입니다:

$der = [System.IO.File]::ReadAllBytes("./certificate.der")
$b64 = [System.Convert]::ToBase64String($der)
$lines = for ($i = 0; $i -lt $b64.Length; $i += 64) {
  $b64.Substring($i, [Math]::Min(64, $b64.Length - $i))
}
$pem = @("-----BEGIN CERTIFICATE-----") + @($lines) + @("-----END CERTIFICATE-----")
Set-Content -Path "./certificate.pem" -Value ($pem -join "`n")

폭이 애초에 중요한 이유는, Base64의 4자 그룹이 줄바꿈을 존중하지 않으므로, 디코더가 줄바꿈을 완전히 무시할 수도 있고 강제할 수도 있기 때문입니다. 이 사이트가 쓰는 디코더는 무시하지만, 엄격한 소비자들은 - 인증서와 메일 세계에는 그 수가 많습니다 - 예상하지 못한 줄바꿈을 이질적 문자로 취급하고 페이로드를 거부합니다. 폭을 고를 때 당신은 소비자와 계약을 하고 있으며, 스크립트에 계약 상대가 누구인지 이름을 붙여 주는 코멘트가 하나쯤은 가치가 있습니다.

base64url: 두 문자와 패딩 결정

표준 Base64의 더하기와 슬래시는 퍼센트 인코딩 후에만 URL 안에서 합법이고, 등호 패딩은 필드 구분자로 읽힙니다. 그래서 RFC 4648이 URL과 파일명에서 안전한 알파벳을 정의했습니다: 같은 64자이지만, 더하기는 하이픈으로, 슬래시는 밑줄로 바뀌어 있고, 데이터의 길이가 불필요하게 만들므로 패딩은 보통 생략됩니다. 당신이 다뤄 온 모든 API 토큰과 JWT는 이 변형으로 쓰여 있으며, 표준은 그것을 단순히 base64가 아니라 base64url이라고 부르라고 고집합니다.

PowerShell의 표준 인코더는 표준 알파벳을 생산하므로, base64url로의 전환은 두 문자 스왑과 패딩 결정입니다:

$bytes = [System.Text.Encoding]::UTF8.GetBytes("Париж encoded 大阪")
$standard = [System.Convert]::ToBase64String($bytes)
$standard
# 0J/QsNGA0LjQtiBlbmNvZGVkIOWkp+mYqg==  표준 알파벳, 패딩 포함
$url = $standard.Replace("+", "-").Replace("/", "_").TrimEnd("=")
$url
# 0J_QsNGA0LjQtiBlbmNvZGVkIOWkp-mYqg  URL 안전 형태, 패딩 제거

패딩 제거는 base64url 세계에서는 안전합니다. 소비자가 문자열의 길이로 패딩이 무엇이 되었을지 다시 계산하기 때문입니다. 하지만 어디에나 그렇지는 않으므로, 결정을 명시하세요: 토큰, JWT 세그먼트, URL 내장에는 패딩을 빼고, 엄격한 표준 알파벳 소비자에게 가는 것은 패딩을 유지하며, 무엇을 고랐는지 기록해 두세요. .NET 런타임에는 이 알파벳의 전용 클래스, System.Buffers.Text.Base64Url(.NET 9에서 추가)이 실제로 들어 있으며, 메서드는 ReadOnlySpan<T> 매개변수를 중심으로 만들어졌습니다. 현재의 PowerShell(7.4 이상, 그 클래스를 담은 .NET 버전 위에서 돌아가게 되면)은 실제로 이것을 직접 부를 수 있습니다 - 메서드 바인더가 이제 배열 인수를 스팬으로 암시적으로 변환하기 때문에 [System.Buffers.Text.Base64Url]::EncodeToString($bytes)가 오늘 동작합니다 - 하지만 스크립트가 Windows PowerShell 5.1, 오래된 PowerShell 7.x 릴리스, .NET 9 이전 런타임의 호스트에서 돌아가야 할 때면, 꺼내야 할 것은 여전히 두 문자 스왑이며, 그것은 그 모든 버전에서 동작합니다.

JWT 발행하기

JSON Web Token은 base64url의 깃발 같은 실전 용도이고, 인코딩 파이프라인의 좋은 전체 테스트이기도 합니다. JWT는 마침표로 연결된 인코딩 세그먼트 세 개, 헤더와 페이로드와 서명이니까요. 앞의 둘은 base64url의 컴팩트 JSON이고, 셋째는 앞의 둘의 정확한 텍스트 위에 계산된 해시의 바이너리 출력입니다. PowerShell로 처음부터 끝까지 만드는 HS256 토큰 전체는 이렇습니다:

$header = @{ alg = "HS256"; typ = "JWT" } | ConvertTo-Json -Compress
$payload = @{ sub = "1234567890"; name = "John Doe"; iat = 1516239022 } | ConvertTo-Json -Compress
function UrlEncode64([byte[]]$bytes) {
  $standard = [System.Convert]::ToBase64String($bytes).TrimEnd("=")
  return $standard.Replace("+", "-").Replace("/", "_")
}
$left = (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($header))) + "." + (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($payload)))
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes("secret"))
$signature = UrlEncode64 ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($left)))
$jwt = $left + "." + $signature
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MTYyMzkwMjIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UifQ.6MWZy9doHbfyomJd4soTRUQft7PmRM2EyxxT3SLoiyE
#  PowerShell 7.4 기준. 세그먼트의 내부 키 순서 - 따라서 서명도 - 버전마다 다를 수 있음

서명 세그먼트를 보세요: base64url 알파벳 문자의 행렬로, 더하기가 하이픈으로, 슬래시가 밑줄로 나타나는 그 알파벳입니다. 그 예시에 대해 세 가지를 알면 프로덕션 인시던트에서 벗어나 있습니다. 첫째, 서명은 키 순서와 간격을 포함한 정확한 JSON 텍스트 위에서 계산되므로, 서명하신 JSON과 검증할 때 쓰는 JSON은 바이트 단위로 동일해야 합니다. PowerShell의 ConvertTo-Json는 키 순서를 당신 대신 결정하고, 그것은 당신이 통제할 수 있는 것이 아니므로, 서명과 검증 사이에서 토큰의 세그먼트를 손으로 재배열하거나 다시 포맷하지 마세요. 둘째, -Compress는 겉치레가 아닙니다: 헤더나 페이로드에 공백 하나만 있어도, 표준 형태는 컴팩트이므로 규정 준수 구현에 대해 결코 검증되지 않는 토큰이 됩니다. 셋째, 타임스탬프 iat는 Unix 에포시 이후의 초 단위이므로, 변환 없이 Get-Date에서 바로 만든 페이로드는 해가 다른 값이 됩니다. 디코딩 방향, 누군가가 발행한 토큰을 들여다보는 일은 자매 사이트의 관련 글에서 다룹니다.

파일과 바이트 스트림

파일은 전부 통틀어 가장 흔한 페이로드이고, 파이프라인은 짧습니다. 파일을 바이트로 읽고, 인코딩하고, 텍스트를 쓰세요. 중요한 두 줄은 읽기 - 반드시 바이트 읽기여야 하며 - 쓰기 - 보통 끝 줄바꿈을 추가하면 안 됩니다 - 입니다:

$bytes = [System.IO.File]::ReadAllBytes("./photo.png")
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path "./photo.b64" -Value $encoded -NoNewline
$encoded.Length
# 곧 보낼 텍스트의 크기

실용적인 노트 두 개. 첫째는 산술입니다: Base64는 모든 것을 키우며, 10메가바이트 파일의 경우 보내는 텍스트는 약 13.4메가바이트입니다. 전송에 크기 제한이 있거나, 이 텍스트가 이메일 본문이나 URL로 가려면, 에러 후에가 아니라 인코딩 전에 계산하세요. 둘째는 끝 줄바꿈입니다: Set-Content는 기본적으로 하나를 추가하며, 이 사이트가 쓰고 대부분의 모던한 디코더는 그것을 무시하지만, 일부 엄격한 소비자는 무시하지 않습니다. -NoNewline은 아무 비용도 들이지 않고 그 의문을 없애 줍니다.

PowerShell 6과 이후 버전은 언어 안에 머무는 두 번째 읽기를 제공합니다: Get-Content -AsByteStream -Raw는 파일이 하나의 바이트 배열로 한 번의 호출에서 돌아오며, .NET ReadAllBytes의 깔끔한 대체이며 이 목적에 대해서는 동일하게 동작합니다. -AsByteStream이 없는 Windows PowerShell 5.1에서는 .NET 읽기가 유일한 선택지이며, 셸의 모든 버전에서 같은 동작을 하는 것이 바로 그것입니다.

인증서: PEM과 PFX에서 텍스트로

인증서는 일상 운영에서 가장 무거운 인코딩 시민입니다. 배포가 텍스트로 그것을 운반하는 것을 좋아하기 때문이죠. PEM 인증서는 갑옷 줄 사이에 줄바꿈된 Base64 본문이고, 줄바꿈 섹션의 레시피가 전체 수출입니다:

$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([System.IO.File]::ReadAllBytes("./certificate.der"))
$cert.Subject
# CN=example.org
$b64 = [System.Convert]::ToBase64String($cert.RawData)
# 인증서의 바이너리 형태의 긴 한 줄

PFX 포맷은 또 다른 일꾼입니다: 인증서를 그 개인 키와 함께 담는 단일 바이너리 파일. 그래서 배포 스크립트와 설정 저장소에서 Base64 텍스트로 방치되어 있는 것을 가장 자주 만나는 포맷입니다. 하나를 인코딩하는 것은 이전 섹션의 평범한 파일 파이프라인이며, PowerShell 7의 읽기 방향은 cmdlet 하나면 끝나는 일입니다:

$pfxBytes = [System.IO.File]::ReadAllBytes("./certificate.pfx")
$pfxB64 = [System.Convert]::ToBase64String($pfxBytes)
# 번들의 텍스트 형태, 설정 파일에 바로 들어갈 준비 완료
Get-PfxCertificate -FilePath "./certificate.pfx" -Password (ConvertTo-SecureString "secret" -AsPlainText -Force)
# 살아 있는 인증서, 수동 디코딩 불필요

보안 문장 하나, Base64가 반대의 가정을 부추기므로 정직하게 말합니다: Base64의 PFX는 텍스트인 개인 키입니다. 인코딩은 시크릿의 형태를 바꾸고 그 비밀성에 대해선 아무것도 바꾸지 않으므로, 채팅 창, 티켓, 커밋에 붙여 넣은 Base64 PFX는 채팅 창, 티켓, 커밋에 붙여 넣은 개인 키입니다. 텍스트 형태를 바이너리 형태가 받는 것과 정확히 같은 조심으로 대하고, 둘보다 인증서 스토어나 시크릿 매니저를 우선하세요.

Basic 인증, data URI, 그리고 옛 습관들

Base64는 그것을 이름으로 불렀던 표준 문서보다 오래되었습니다. 1996년 MIME RFC 일족이 그것을 이메일에 넣었고, HTTP Basic 인증이 초기 웹의 모든 헤더 교환에 그것을 넣었습니다. 거기서 클라이언트는 여전히 자격 증명 쌍을 Base64 문자열 하나로 인코딩합니다:

$credential = [System.Text.Encoding]::UTF8.GetBytes("alice:s3cret!")
[System.Convert]::ToBase64String($credential)
# YWxpY2U6czNjcmV0IQ==
# 보내는 형태: Authorization: Basic YWxpY2U6czNjcmV0IQ==

여기서는 표준 알파벳이 맞습니다. 더하기와 슬래시를 포함해서. 헤더는 URL이 아니라서 안전한 알파벳이 필요 없으니까요. 같은 메커니즘은 data URI에도 나타납니다. 문서가 자신의 바이너리를 인라인으로 내장하는 방식이죠. 그 형태는 문자 그대로의 접두사와 바이트의 표준 Base64입니다:

$dataUri = "data:application/octet-stream;base64," + [System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# data:application/octet-stream;base64,AQID+vs=

이 두 습관은 당신이 만들 것이 아니라 만나게 될 것으로써 알 가치가 있습니다: 헤더나 링크에 긴 Base64 행렬이 들어 있으면, 이 두 포맷이 먼저 검사할 대상이며, 둘 다 평범한 디코딩 한 번이면 그것이 말하는 것에 닿습니다. 그것이 포맷의 뜻이면서, 이 사이트의 디코딩 쪽이 존재하는 이유입니다.

인코딩된 명령과 Windows 도구함

PowerShell은 1.0 버전부터 인코딩해야 할 내장된 이유가 있었습니다: 바로 호스트 자체의 -EncodedCommand 매개변수입니다. pwsh에 Base64 문자열을 건네면, 바이트를 UTF-16LE로 디코딩하고, 그 결과가 명령으로 실행됩니다. 문서화된 목적은 바깥 셸의 따옴표와 싸우는 명령이었고, 인코딩 쪽은 2줄입니다:

$command = "Write-Host 'Hello from the encoded side'"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgACcASABlAGwAbABvACAAZgByAG8AbQAgAHQAaABlACAAZQBuAGMAbwBkAGUAZAAgAHMAaQBkAGUAJwA=
pwsh -NoProfile -EncodedCommand $encoded
# Hello from the encoded side

인코딩 줄을 잘 읽어 보세요. 모두가 여기서 틀리는 곳입니다: 페이로드는 UTF-16LE여야 하며, 그것은 Unicode 인코딩이지 UTF-8이 아닙니다. 틀린 것으로 인코딩하면, 호스트는 어차피 당신의 바이트를 UTF-16LE로 디코딩하고 모지바케로 이루어진 명령을 실행하며, 그 실수의 완벽한 초상화와 같은 에러를 만들어 냅니다. 디코딩 글은 그 실패를 온전히 다루며, 이 쪽의 수정은 한 단어입니다: Unicode.

언어 바깥에서는 네이티브 도구들이 각자 자신의 조용한 인코딩 결정을 들고 있습니다. Windows에서는 certutil -encode infile outfile.b64가 PEM이 기대하는 갑옷 줄을 가진 표준 Base64 파일을 만들고, -f가 기존 출력을 덮어쓰며, 기억할 가치가 있는 플래그는 -unicodetext입니다. certutil이 출력 파일을 Unicode로 쓰게 하는 것(마이크로소프트 문서의 표현을 빌리면: "출력 파일을 Unicode로 씁니다")으로, 하나의 스위치가 인코딩 결정을 숨깁니다. Linux에서는 클래식한 유틸리티가 base64 -w 0 file이고, -w 0가 바로 하중을 지는 부분입니다: 없으면 GNU base64가 76자에서 줄바꿈하여, 한 줄을 원했는데 MIME 스타일 파일을 건넵니다. macOS에서는 BSD 버전이 그런 플래그를 필요로 하지 않습니다. 기본적으로 잘리지 않는 한 줄을 출력하기 때문이죠.

출력이 거대할 때의 인코딩

일상적인 크기에 대해서는 전부 읽고 전부 인코딩하는 파이프라인이 빠르고 간단한 것이며, 그것이 정답인 곳은 파일이 메모리에 편하게 담기기보다 커지거나, 데이터가 다운로드나 소켓에서 조각 조각 도착하기까지입니다. 그때 문서화된 도구는 스트리밍 쌍입니다: CryptoStream에 감긴 System.Security.Cryptography.ToBase64Transform. 원시 바이트를 넣고 Base64 텍스트가 나오며, 어느 순간에도 살아 있는 것은 작은 버퍼 하나뿐입니다:

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

원샷 메서드와의 차이 중 기록할 가치가 있는 것이 하나 있습니다: 스트림은 입력이 아무리 커도 줄바꿈이 전혀 없는 연속된 한 줄을 생산합니다. 공백을 무시하는 디코더는 신경 쓰지 않지만, 최종 목적지가 PEM 파일이라면, 결과에 줄바꿈 섹션의 64열 루프를 나중에 실행하세요. 그리고 C#에서는 표준 형태가 C# 글에서 보는 것과 같은 ToBase64Transform + CryptoStream 패턴입니다. PowerShell은 위와 같이 그것을 직접 운전합니다.

인코딩된 페이로드가 잘못되는 곳

  • 바이트가 아니라 문자열을 인코딩하기. ToBase64String("Hello")는 변환 예외를 던지며, 이것은 메서드가 첫 번째 결정, 즉 인코딩이 아직 이루어지지 않았다고 말하는 것입니다. 스크립트에서 그것을 보이게 하면 에러는 사라집니다.
  • UTF-8이 약속된 자리에 UTF-16. 페이로드가 기대의 두 배 길이가 되고, 소비자가 쓰레기를 읽습니다. 인코딩은 프로토콜의 일부이며, 현대 인터넷의 거의 모든 네트워크에서 그 프로토콜은 UTF-8이라 말합니다.
  • 5.1 읽기. Windows PowerShell 5.1은 스크립트가 보기 전에 BOM 없는 텍스트 파일을 머신의 ANSI 코드 페이지로 읽으므로, UTF-8 소스 파일은 인코딩 단계 전에 이미 파괴될 수 있습니다. 5.1에서는 텍스트를 명시적인 UTF-8 읽기로 읽고, 결과의 처음 문자를 확인하세요.
  • 틀린 줄바꿈 폭. MIME은 76을, PEM은 64를, API는 없음을 원하며, 엄격한 소비자는 예상하지 못한 줄바꿈을 이질적 문자로 취급합니다. 소비자에게서 폭을 고르고, 코멘트로 말해 두세요.
  • 스왑의 반대편에 있는 패딩. 등호를 버리는 것은 base64url 토큰에 맞고, 표준 패딩을 기대하는 소비자에게는 틀립니다. 알파벳 스왑과 패딩 결정은 하나의 선택이 아니라 두 개의 선택입니다.
  • 서명한 것을 다시 포맷하기. JWT 서명은 키 순서와 간격을 포함한 정확한 JSON 텍스트를 덮습니다. 클레임을 재배열하거나 공백을 하나 더하면, 토큰은 검증이 멈추고, 에러 메시지는 원인에서 어디까지나 멀리 있습니다.
  • 255를 넘는 값. 정수를 바이트로 변환하는 것은 랩핑 대신 예외를 던지므로, 256은 캐스트에서 스크립트를 멈춥니다. 소스 데이터가 숫자라면, 캐스트를 명시적으로 하고 실수는 보이는 실수로 두세요.
  • 의상을 믿기. Base64는 암호화도 압축도 아닙니다: 데이터를 3분의 1로 키우는 번역입니다. Base64의 시크릿은 평문인 시크릿이고, Base64의 파일은 33% 더 많은 공간을 필요로 하는 파일입니다.

믿을 수 있는 인코더를 위한 규칙

  • 바이트를 의도적으로 생산하세요. 어떤 인코딩 스크립트의 첫 줄도, PowerShell이 문자열을 올바른 바이트로 바꿔 줄 것이라는 기대가 아니라, 명시적인 GetBytes나 바이트 읽기여야 합니다.
  • 선택을 하는 코드 옆의 코멘트에 알파벳과 폭의 이름을 붙이세요: 표준 또는 base64url, 76에서 줄바꿈, 64에서 줄바꿈, 또는 줄바꿈 없음. 소비자는 6개월 뒤 스크립트를 읽는 사람이며, 그 사람은 당신입니다.
  • 소비자가 특별히 끝 줄바꿈을 기대하는 경우를 제외하고는 -NoNewline로 텍스트 파일을 쓰고, 줄 끝(LF 또는 CRLF)은 소비자의 문서가 기대하는 대로 고르세요.
  • 만드는 동안 왕복을 테스트하세요: 인코딩, 디코딩, 바이트 비교. 두 바이트 배열 위의 30초짜리 Compare-Object가 인코딩 실수, 줄바꿈 실수, 바이트 순서 실수를 원인이 아직 선명할 때 한꺼번에 잡습니다.
  • 페이로드가 아니라 크기를 기록하세요. 이전의 바이트 수와 이후의 문자 수는 약 1.33의 비례에 앉아 있어야 하며, 그렇지 않을 때 크기 불일치가 로그가 데이터를 담지 않고도 당신이 어디를 봐야 하는지 알려 줍니다.

PowerShell이 인코더를 물려받은 방식

PowerShell의 Base64 인코딩에 대한 가장 짧은 진짜 역사는, PowerShell이 그것을 쓴 적이 없다는 것입니다. 당신이 부르는 메서드, Convert.ToBase64String은 2003년 .NET Framework 1.1과 함께 출시되었고, 2006년 11월 버전 1.0 이후의 모든 PowerShell은 단순히 자신이 돌아가는 .NET을 노출했을 뿐입니다. 프로젝트는 개발 중에는 Monad라고 불렸고, 2003년 10월 Professional Developers Conference에서 처음 공개되었으며, 출시할 때는 그것이 감싸고 있는 .NET 인코더가 이미 세 살이 되어 웹 트래픽을 실어 나르고 있었습니다.

포맷은 셸이 출시된 그 해에 표준화되었습니다. 2006년 10월에 발행된 RFC 4648은 알파벳, 패딩 규칙, 디코딩의 엄격성, base64url 변형을 고정시켰고, 여전히 .NET 쌍이 구현하는 동작을 정확히 기술합니다. 그보다 앞서 1996년에 나온 MIME RFC들은 이미 76자 줄바꿈을 이메일에 넣어 놓았고, 그래서 그 폭이 오늘날까지 InsertLineBreaks의 기본값인 것입니다. 2016년 8월 PowerShell이 PowerShell Core라는 이름으로 오픈 소스이고 크로스 플랫폼이 되었을 때, 인코더는 아무 변경 없이 Linux와 macOS에 따라 왔습니다. 바꿀 것이 없었기 때문입니다.

나중에 바뀐 것은 .NET에서 일어났고, 대부분 PowerShell의 손길이 닿지 않는 곳에서였습니다. 런타임은 최근 버전에서 더 빠른, 스팬 기반 Base64 헬퍼를 얻었고, Base64Url 클래스와 Try 접두사 디코딩 메서드도 포함됩니다. 스팬은 byref형 타입이고, 오래된 PowerShell 릴리스는 실제로 그것에 바인딩조차 할 수 없었습니다. 하지만 현재 PowerShell의 메서드 바인더는 이제 배열에서 스팬으로의 암시적 변환을 수행하므로, 충분히 새로운 호스트의 스크립트에서 이 지름길을 부를 수 있습니다. 더 오래된 모든 것에 대한 커뮤니티의 답은 PowerShell Gallery의 Microsoft.PowerShell.TextUtility 모듈입니다. 그 ConvertTo-Base64는 같은 .NET 메서드를 감싸, UTF-8 기본값의 -Text 매개변수와 76열 줄바꿈을 위한 -InsertBreakLines 스위치를 추가합니다. cmdlet 형태를 선호한다면 Install-Module -Name Microsoft.PowerShell.TextUtility로 설치하세요. 그리고 이 모듈은 아카이브되었고 더 이상 적극적으로 유지되지 않는다는 점을 기억하세요. 그것이 새로운 스크립트에 내장 메서드가 여전히 추천인 또 다른 이유입니다.

가져갈 숫자와 이름들

  • 모든 입력 바이트 3개가 출력 문자 4개가 되므로, 인코딩된 데이터는 원본보다 약 33% 커지며, 패딩은 절대로 등호 두 개를 넘지 않습니다.
  • 기본 출력은 잘리지 않는 한 줄입니다. InsertLineBreaks는 1996년의 MIME 관례대로 CRLF로 76자에서 줄바꿈합니다. PEM은 64를 원하며, 그 폭을 만들어 주는 내장 플래그는 없습니다.
  • base64url은 더하기와 슬래시를 하이픈과 밑줄로 바꾼 표준 Base64이며, 패딩은 보통 생략되고, 모든 JWT와 API 토큰의 알파벳입니다.
  • "Café"는 UTF-8에서 5바이트, UTF-16LE에서 8바이트입니다. 같은 보이는 텍스트, 두 배 크기, 그리고 두 인코딩은 네트워크를 건너며 서로 바꾸어 쓸 수 없습니다.
  • -EncodedCommand는 첫 PowerShell 릴리스부터 존재해 왔고, 그 페이로드는 UTF-8이 아니라 UTF-16LE여야 합니다. 이 쪽에서 가장 흔한 실수를 고치는 하나의 단어는 Unicode입니다.
  • certutil -encode는 -unicodetext 안에 인코딩 결정을 숨길 수 있고, GNU base64는 76열 줄바꿈 대신 한 줄을 주려면 -w 0이 필요합니다.
  • .NET의 스팬 기반 Base64 헬퍼(Base64Url 포함)는 한때 PowerShell에서 닿을 수 없었습니다. 스팬이 오래된 메서드 바인더가 바인딩할 수 없는 byref형 타입이었기 때문이죠. 현재 PowerShell(7.4+, 그 클래스를 담기에 충분히 새로운 .NET 런타임 위에서)은 배열 인수를 스팬 매개변수에 불평 없이 해석하므로, 직접 호출이 오늘 동작합니다 - 하지만 두 문자 스왑은 여전히 모든 버전, 구한 시절이든 새로운 시절이든 동작하는 유일한 레시피입니다.
  • 단일 바이트, 123은 ew==로 인코딩됩니다: 출력의 길이로 입력의 길이를 안다는 규칙의 가능한 가장 작은 예입니다.

화살표 뒤집기

이 글의 모든 것은 당신이 가진 데이터를 Base64 문자열로 바꾸는 것에 관한 것입니다. 거울상의 연산, 문자열을 받아 데이터를 되찾는 것은 자신만의 문제 캐스트를 가지고 있습니다: 네 가지 공백을 무시하는 디코더, 세 가지 죄를 덮는 에러 메시지 하나, 들여다볼 JWT, 벗길 인증서, 설명할 -EncodedCommand. 그 방향은 자신만의 함정과 자신만의 역사를 가진, 자매 사이트의 관련 글인 PowerShell에서의 Base64 디코딩에서 온전히 다뤄집니다. 링크는 아래에 있습니다.

마지막 업데이트: 2026-09-08

관련 문서: PowerShell에서의 Base64 디코딩: 완전한 가이드