[오늘의 배움] byte[] <-> String 인코딩 문제 (feat. base64)

이상민·2021년 12월 16일
0

[오늘의 배움]

목록 보기
65/70
post-thumbnail

암호의 인코딩

Google tink 라이브러리를 사용하면 암호문은 byte[]으로 반환되고, 이를 복호화해 다시 문자열을 얻을 수 있다. 하지만 현재 프로젝트의 DB에서 암호문을 저장하게 될 부분이 VARCHAR로 되어있고, 이를 수정하려면 flyway 스크립트 작성하고 등등... 귀찮은 부분이 많을거 같았다. 그래서 암호문을 문자열로 변환해 저장하고, 복호화시 문자열을 다시 byte[]로 변환해 사용하려고 했다.

// 대충 이런 느낌
String plaintext;
byte[] cipher = encrypt(plaintext);
String cipherString = new String(cipher, "UTF-8");

byte[] maybeCipher = cipherString.getBytes(StandardCharsets.UTF_8)
byte[] decipher = decrypt(cipher);
String plaintext = new String(decipher, "UTF-8");

// cipher와 maybeCipher가 다름 

하지만 위처럼 코드를 작성했더니 복호화에 실패했다. 디버거로 찍어보니 ciphermaybeCipher의 값이 달랐다. 동일한 UTF-8 인코딩 방식을 지정해줬으니 잘 될줄 알았는데, UTF-8로는 표현할 수 없는 값이었던 것 같다. UTF-8이 가변 길이인점이 원인으로 의심된다. 그래서 base64 방식을 사용해봤다. 잘된다!

// 대충 이런 느낌
String plaintext;
byte[] cipher = encrypt(plaintext);
String cipherString = Base64.getEncoder().encodeToString(cipher);

byte[] maybeCipher = Base64.getDecoder().decode(cipherString);
byte[] decipher = decrypt(cipher);
String plaintext = new String(decipher, "UTF-8");

// cipher와 maybeCipher가 동일함

Base64

맨날 JWT가 base64 인코딩 된다만 알고 어떤 인코딩 방식인지에 대해서는 생각해본적이 없어 DB를 수정하는 고통을 없애준 은혜에 보답하기 위해 찾아봤다.

Base 64 (베이스 육십사)란 8비트 이진 데이터(예를 들어 실행 파일이나, ZIP 파일 등)를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식
-- 위키피디아

이름처럼 64개의 문자를 표현할 수 있다. 2^6 = 64, 즉 6개의 비트를 가지고 인코딩을 한다. 정확히 6개의 비트를 사용하기 때문에 3개 바이트의 데이터(8*3)는 4개 문자(6*4)가 되는 등 최소 33% 정도 크기가 증가한다. 고정적인 크기로 인코딩하기 때문에 문자열 외에도 이미지등 바이트 단위 이진 데이터를 인코딩하기에 적합하다.

반면 UTF-8은 유니코드 캐릭터 세트의 문자를 인코딩하기 위한 방법이다(바이너리를 인코딩 ❌). 용도가 다르니 다음부터를 헷갈리지 말아야겠다.

profile
편하게 읽기 좋은 단위의 포스트를 추구하는 개발자입니다

0개의 댓글