문자열 인코딩과 디코딩
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\n | 0 |
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가 암호화된 데이터 앞에 쓰는 헤더입니다. 이 문자열은 암호문이므로 디코딩해도 평문이 아니라 바이너리 잡음이 나옵니다.