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

OpenSSL로 Base64 다루기

openssl base64는 거의 모든 Linux, macOS, BSD 시스템에 있으며 어디서나 똑같이 동작합니다. 실패할 때도 아무 말이 없습니다. 아래 출력은 OpenSSL 3.5 기준입니다.

문자열 인코딩과 디코딩

printf '%s' 'héllo wörld' | openssl base64        # aMOpbGxvIHfDtnJsZA==
echo 'aMOpbGxvIHfDtnJsZA==' | openssl base64 -d   # héllo wörld

openssl base64는 openssl enc -base64의 단축형이며, 두 표기 모두 같은 옵션을 받습니다. 인코딩할 때는 데이터에 줄 바꿈이 끼어들지 않도록 printf '%s'를 쓰고, 디코딩할 때는 아래에 설명할 이유로 echo를 쓰세요.

끝 줄 바꿈 함정과 -A

기본적으로 OpenSSL은 Base64를 줄 바꿈으로 끝나는 텍스트 줄 단위로 처리합니다. 디코딩할 때 \n 없는 마지막 줄은 무시되므로, 다음 명령은 아무것도 출력하지 않습니다.

printf 'aGVsbG8K' | openssl base64 -d             # (no output)
printf 'aGVsbG8K\n' | openssl base64 -d           # hello
printf 'aGVsbG8K' | openssl base64 -d -A          # hello

-A는 입력 전체를 한 줄로 다루라고 OpenSSL에 알려 줍니다. 양방향 모두에서 사용하세요.

  • 인코딩: -A가 없으면 출력이 한 줄에 64자씩 줄 바꿈됩니다. -A를 쓰면 한 줄로 나오며, HTTP 헤더, JSON, 환경 변수에 필요한 형태가 바로 이것입니다.
  • 디코딩: -A를 쓰면 입력은 줄 바꿈 없는 한 줄이어야 합니다. -A가 없으면 PEM 본문 같은 여러 줄 입력도 잘 디코딩됩니다.
head -c 60 /dev/zero | tr '\0' x | openssl base64      # 64 chars, newline, 16 chars
head -c 60 /dev/zero | tr '\0' x | openssl base64 -A   # 80 chars on one line

64자 줄 바꿈은 최대 76자를 쓰는 MIME이 아니라 PEM 형식(RFC 7468)에 맞춘 것입니다. MIME 출력에는 MIME Base64 인코더나 GNU base64(기본값 76)가 더 잘 맞습니다.

URL-safe Base64

OpenSSL에는 Base64URL 모드가 없습니다. tr로 변환하세요.

printf '%s' '<<???>>' | openssl base64 -A | tr '+/' '-_' | tr -d '='
# PDw_Pz8-Pg

디코딩할 때는 대응을 반대로 하고 패딩을 복원하세요. Bash에서 Base64 사용하기 페이지에 이를 처리하는 반복문이 있습니다. 아래에서 보듯 OpenSSL 자체는 -와 _를 조용히 무시하므로 URL-safe 입력을 그대로 넣으면 절대 안 됩니다. 차이점은 Base64와 Base64URL 비교에서 설명합니다.

파일 인코딩과 디코딩

openssl base64 -in photo.jpg -out photo.b64
openssl base64 -d -in photo.b64 -out photo-copy.jpg

OpenSSL은 스트리밍 방식이라 큰 파일도 괜찮습니다. 100 KB 무작위 파일이 왕복 후에도 바이트 하나 다르지 않았습니다. 인코딩된 파일은 64자에서 줄 바꿈되고 줄 바꿈으로 끝나므로 -A 없이 디코딩하면 됩니다. 한 줄짜리 파일이 필요하다면 인코딩할 때와 디코딩할 때 모두 -A를 추가하세요.

Data URI는 다음과 같습니다.

printf 'data:image/jpeg;base64,%s' "$(openssl base64 -A -in photo.jpg)"

OpenSSL 3.5 enc 매뉴얼의 BUGS 섹션에는 아직도 관련 문제 두 가지가 적혀 있습니다. 큰 파일에서는 -A가 “제대로 동작하지 않는다(doesn’t work properly)“는 것, 그리고 -A 없이 디코딩할 때 입력의 처음 1024바이트에 줄 바꿈이 없으면 처음 두 줄이 무시된다는 것입니다. 큰 한 줄짜리 파일이라면 먼저 fold -w 64로 줄을 바꾼 뒤 -A 없이 디코딩하세요.

조용한 실패: 결과 검증하기

OpenSSL은 잘못된 Base64를 보고하지 않습니다. 잘못된 데이터에서 멈추고, 그때까지 디코딩한 것을 쓴 다음, 상태 0으로 종료합니다. OpenSSL 3.5에서의 결과:

입력출력종료 코드
aGVsbG8K + 줄 바꿈hello\n0
aGVsbG8K (줄 바꿈 없음)없음0
aGk + 줄 바꿈 (패딩 없음)없음0
aGk=!! + 줄 바꿈없음0
PDw_Pz8-Pg== + 줄 바꿈 (URL-safe)없음0
not base64 at all + 줄 바꿈없음0

따라서 스크립트가 직접 출력을 확인해야 합니다.

n=$(echo "$b64" | openssl base64 -d | wc -c)
[ "$n" -gt 0 ] || echo "decode failed or input was empty" >&2

이렇게 하면 완전한 실패는 잡을 수 있지만, OpenSSL이 잘못된 데이터를 만나기 전에 앞부분을 디코딩한 부분 실패는 잡지 못합니다.

아니면 base64: invalid input을 출력하고 1로 종료하는 GNU base64 -d로 바꾸세요. 특정 문자열의 문제를 보려면 Base64 검사기가 잘못된 문자를 표시해 주고, 흔한 Base64 오류에서 원인을 설명합니다.

해시, 키, 암호화된 출력

OpenSSL에서 Base64는 대부분 다른 명령과 함께 쓰입니다.

printf '%s' hi | openssl dgst -sha256 -binary | openssl base64 -A
# j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=

openssl rand -base64 32          # 32 random bytes -> 44 characters

첫 번째 형식은 Base64 SHA-256 다이제스트를 만들며, SRI 해시와 많은 API 서명이 이 형식을 씁니다. 브라우저 버전은 SHA-256 Base64 변환 도구입니다. -binary가 중요합니다. 이것이 없으면 해시 자체가 아니라 해시의 hex 텍스트를 인코딩하게 됩니다.

openssl rand -base64 32는 시크릿과 키를 만드는 흔한 방법입니다. Base64 키 생성기가 브라우저에서 같은 일을 합니다.

-a(-base64와 같음)를 붙인 openssl enc는 암호문을 Base64로 인코딩합니다.

printf '%s' 'secret text' | openssl enc -aes-256-cbc -pbkdf2 -a -pass pass:demo
# U2FsdGVkX1/eMk5T8FT9dX8OyAxLRn/ukvgmovOQOeA=   (salt is random, changes every run)

U2FsdGVkX1 접두사는 Salted__의 Base64입니다. 비밀번호 없이 디코딩하면 읽을 수 있는 것이 아무것도 나오지 않습니다. 인코딩과 암호화의 이런 차이는 인코딩과 암호화의 차이에서 다룹니다.

PEM 파일

PEM 형식의 인증서와 키는 -----BEGIN ...----- 줄 사이에 든 DER의 Base64이며, 64자에서 줄 바꿈됩니다. OpenSSL은 Base64를 직접 다루지 않고도 이를 바로 변환합니다.

openssl x509 -in cert.pem -outform der -out cert.der
openssl x509 -in cert.der -inform der -outform pem

브라우저에서 PEM 블록을 살펴보려면 인증서 디코더나 PEM을 DER로 변환을 사용하세요.

자주 묻는 질문

openssl base64 -d가 아무것도 출력하지 않는 이유는 무엇인가요?

입력 끝에 줄 바꿈이 없거나 입력이 잘못된 경우입니다. -A가 없으면 OpenSSL은 줄 바꿈으로 끝나는 줄을 기대하며, 줄 바꿈 없는 마지막 줄은 조용히 버립니다. -A를 추가하거나 printf '%s' 대신 echo로 문자열을 넘기세요.

openssl base64와 openssl enc -base64는 같은가요?

네. openssl base64는 openssl enc -base64의 단축형이며, 둘 다 -d, -A, -in, -out을 받습니다.

openssl base64 -d가 성공했는지 어떻게 확인하나요?

종료 코드는 믿을 수 없습니다. 잘못된 입력에도 상태 0과 함께 빈 출력이나 일부 출력이 나옵니다. 출력이 비어 있지 않은지 확인하거나, 잘못된 입력에 1로 종료하는 GNU base64 -d를 사용하세요.

Base64 문자열 앞의 U2FsdGVkX1은 무엇인가요?

Salted__의 Base64로, 비밀번호를 쓸 때 openssl enc가 암호화된 데이터 앞에 쓰는 헤더입니다. 이 문자열은 암호문이므로 디코딩해도 평문이 아니라 바이너리 잡음이 나옵니다.

계속 읽기