비밀번호로 텍스트를 암호화하는 방법
- 암호화와 키 유형 비밀번호를 선택한 상태로 두세요.
- 비밀번호를 입력하세요. 비밀번호 표시를 켜면 입력하는 동안 비밀번호가 보입니다. 특별한 이유가 없다면 PBKDF2 반복 횟수는 310,000 그대로 두세요. 복호화하는 사람도 같은 숫자를 입력해야 합니다.
- 평문에 메시지를 입력하고 암호화를 누르세요. 비밀번호 칸에서 Enter를 눌러도, 텍스트 칸에서 Ctrl+Enter를 눌러도 됩니다.
- 암호화된 Base64를 복사하거나 다운로드하세요. 문자열을 URL에 넣는다면 URL-safe 출력 (- _ , = 없음) 을 체크하세요.
출력 아래에는 솔트, IV, 암호문, 태그의 크기와 합계, 사용한 키 유도 방식이 표시됩니다. 결과를 보낼 때는 Base64를 한 메시지로 보내고, 비밀번호는 전화나 다른 메신저처럼 다른 경로로 보내세요. 받는 사람은 AES 복호화를 열어 과정을 거꾸로 진행합니다.
이 도구의 용도
이 도구는 더 나은 경로가 없을 때 텍스트 한 조각을 안전하게 공유하기 위한 것입니다. 손님에게 알려 줄 Wi-Fi 비밀번호, 동료에게 보내는 복구 코드, 너무 많은 사람이 볼 수 있는 티켓에 붙여넣는 메모 같은 경우입니다. “그냥 채팅으로 보내지 뭐"를 도청자가 읽을 수 없는 방식으로 바꿔 줍니다.
시크릿 관리 도구는 아닙니다. 아무것도 저장하지 않고, 접근 권한을 관리하지 않으며, 누가 무엇을 복호화했는지 기록하지 않고, 키를 교체하거나 이미 보낸 비밀을 취소하지도 않습니다. 애플리케이션 자격 증명, 운영 환경 키, 팀 전체가 공유하는 정보는 제대로 된 시크릿 관리 도구나 공유 기능이 있는 비밀번호 관리자에 두어야 합니다. 또한 암호화된 텍스트는 나중에 비밀번호를 알게 된 사람이라면 누구든 언제까지나 복호화할 수 있다는 점도 기억하세요.
정확한 형식
비밀번호 모드의 출력은 다음과 같습니다.
Base64( salt[16] ‖ iv[12] ‖ ciphertext ‖ tag[16] )
- 키: UTF-8 비밀번호에 16바이트 솔트와 310,000회 반복으로 PBKDF2-HMAC-SHA256을 적용해 32바이트를 만듭니다.
- 암호: 12바이트 IV, 128비트 태그, 연관 데이터 없음으로 AES-256-GCM을 씁니다.
- 난수: 암호화할 때마다
crypto.getRandomValues로 새 솔트와 IV를 만듭니다.
암호문의 길이는 UTF-8 메시지와 정확히 같으므로 전체 데이터에는 44바이트가 더해집니다. 33바이트 메시지는 77바이트가 되고, 이는 Base64 104자입니다. 버전 바이트는 없고 반복 횟수도 저장되지 않습니다. 반복 횟수를 바꿨다면 받는 사람이 같은 값을 입력해야 하며, 그렇지 않으면 비밀번호가 틀렸을 때와 같은 오류로 복호화가 실패합니다.
원시 키 모드에서는 PBKDF2 단계가 없어지고 출력은 Base64( iv[12] ‖ ciphertext ‖ tag[16] )가 되며, Base64나 hex로 입력한 16, 24 또는 32바이트 키로 암호화됩니다. 랜덤 키 생성으로 키를 만들 수 있습니다. Chrome과 Edge는 192비트 AES 키를 지원하지 않으며, 이 경우 도구는 조용히 실패하지 않고 그 사실을 알려 줍니다. 128비트나 256비트를 쓰세요.
Python이나 Node.js에서 복호화하기
받는 사람에게 이 웹사이트는 필요 없습니다. Python과 cryptography 패키지(pip install cryptography)로는 다음과 같이 합니다.
import base64, os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
def _key(password, salt):
return PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt,
iterations=310000).derive(password.encode("utf-8"))
def decrypt(b64, password):
data = base64.b64decode(b64)
salt, iv, ct = data[:16], data[16:28], data[28:]
return AESGCM(_key(password, salt)).decrypt(iv, ct, None).decode("utf-8")
def encrypt(text, password):
salt, iv = os.urandom(16), os.urandom(12)
ct = AESGCM(_key(password, salt)).encrypt(iv, text.encode("utf-8"), None)
return base64.b64encode(salt + iv + ct).decode("ascii")
AESGCM.decrypt는 태그가 암호문 끝에 있다고 가정하는데, 이 형식도 태그를 그 위치에 둡니다. Node.js에서는 태그를 분리해 따로 넘겨야 합니다.
const crypto = require("crypto");
function decrypt(b64, password) {
const data = Buffer.from(b64, "base64");
const salt = data.subarray(0, 16), iv = data.subarray(16, 28);
const tag = data.subarray(data.length - 16), ct = data.subarray(28, data.length - 16);
const key = crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256");
const d = crypto.createDecipheriv("aes-256-gcm", key, iv);
d.setAuthTag(tag);
return Buffer.concat([d.update(ct), d.final()]).toString("utf8");
}
이 페이지에서 만든 데이터는 두 함수 모두로 복호화되고, encrypt()로 만든 데이터는 이 페이지에서 복호화됩니다. URL-safe 출력을 썼다면 Node의 Buffer.from(b64, "base64")는 그대로 읽고, Python에서는 base64.urlsafe_b64decode(b64 + "=" * (-len(b64) % 4))를 쓰세요. openssl enc는 GCM을 지원하지 않으므로 여기서는 쓸 수 없습니다. Base64 쪽은 Python과 Node.js 페이지에서 더 자세히 다룹니다.
자주 묻는 질문
Base64 인코딩만으로 텍스트를 보호할 수 있나요?
아니요. Base64는 바이트를 표기하는 방식만 바꿀 뿐이고, 누구나 키 없이 디코딩할 수 있습니다. 이 도구는 먼저 AES로 암호화하고, Base64는 암호문을 붙여넣을 수 있는 텍스트로 바꾸는 데만 씁니다. 자세한 내용은 Base64는 암호화가 아닙니다를 참고하세요.
같은 텍스트를 암호화해도 왜 매번 결과가 다른가요?
실행할 때마다 16바이트 랜덤 솔트와 12바이트 IV를 새로 만들기 때문입니다. 의도된 동작입니다. 같은 메시지가 같은 암호문을 만들어서는 안 되고, AES-GCM은 같은 키로 IV를 절대 재사용해서는 안 됩니다.
비밀번호는 얼마나 강력해야 하나요?
310,000회 반복하는 PBKDF2는 추측 속도를 늦추지만, 짧거나 흔한 비밀번호까지 지켜 주지는 못합니다. 무작위 단어 다섯 개 이상처럼 긴 랜덤 패스프레이즈를 쓰고, 암호문과는 다른 경로로 보내세요.
이 도구로 파일도 암호화할 수 있나요?
아니요, 텍스트만 암호화합니다. 파일에는 age나 gpg 같은 전용 도구를 쓰세요.
이 도구는 어떤 AES 모드와 키 크기를 쓰나요?
비밀번호 모드에서는 128비트 인증 태그를 쓰는 GCM 모드의 AES-256입니다. 원시 키 모드에서는 입력한 키에 따라 128, 192 또는 256비트가 됩니다.