암호화 알고리즘 종류 / Java 암호화 유틸 개발 / AES256, Base64 등 암복호화 방법
단방향 해시
복호화가 불가능하여 비밀번호 해싱에 적합합니다.
일반 해시 함수
MD5
128비트 고정 길이의 출력값을 가집니다.
보안이 취약해서 인증용으로 사용이 권장되지 않습니다.
SHA-256
데이터 무결성 검증 등에 사용되는 해시 함수입니다.
속도가 빠르기 때문에 GPU 공격에 취약하여 비밀번호 저장 용도로는 부적합합니다.
사용자가 입력한 비밀번호를 해싱 후 SHA-256 해시 앞 16자리를 잘라서
유저 테이블 비밀번호 컬럼에 저장된 SHA-256 해시 앞 16자리와 비교하여 로그인하면,
충돌 확률이 증가하고 보안성이 낮아져서 권장되지 않습니다.
SHA-512
SHA-256보다 긴 해시 길이를 제공하여 충돌 저항성이 더 높지만,
해싱 속도가 빠르기 때문에 여전히 비밀번호 저장 용도로는 부적합합니다.
비밀번호 해싱 알고리즘
bcrypt
비밀번호 저장을 위해 설계된 단방향 해싱 알고리즘입니다.
Salt가 자동으로 포함되어 동일한 비밀번호라도 다른 해시를 생성합니다.
Cost Factor를 통해 연산 횟수를 조절하여 의도적으로 느리게 할 수 있습니다.
Argon2
현재 가장 권장되는 비밀번호 해싱 알고리즘입니다.
연산 시간뿐 아니라 메모리 사용량까지 증가시켜 공격 비용을 상승시킬 수 있습니다.
양방향 암호화
복호화가 가능합니다.
비밀키 암호화 기법
대칭형 암호화 알고리즘이고 비밀키로 암/복호화합니다.
| 블록 암호화 방식 | DES, AES 등 |
| 스트림 암호화 방식 | LFSR, RC4 등 |
공개키 암호화 기법
비대칭형 암호화이고 공개키로 암호화, 개인키로 복호화합니다.
- RSA 등
SHA512 해싱 방법
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.security.MessageDigest;
@Slf4j
@Component
public class SHA512Util {
public String getSHA512(String raw) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(raw.getBytes());
byte[] msgb = md.digest();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < msgb.length; i++) {
String tmp = Integer.toHexString(msgb[i] & 0xFF);
while (tmp.length() < 2)
tmp = "0" + tmp;
sb.append(tmp.substring(tmp.length() - 2));
}
return sb.toString();
}
}
위 SHA512 유틸을 통해 해싱할 수 있습니다.
AES256 암복호화 방법
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
public class AES256Util {
private byte[] iv;
private byte[] sessionKey;
private Key keySpec;
public AES256Util(String key) {
try {
// 고정 IV 사용 시, 같은 평문은 동일한 암호문이 생성되므로 보안에 취약
// 매 암호화마다 랜덤 생성되도록 변경 필요
this.iv = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
sessionKey = key.substring(0, 32).getBytes(StandardCharsets.UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(sessionKey, "AES");
this.keySpec = secretKeySpec;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 암호화
public String aesEncode(String str) {
try {
// AES/CBC/PKCS5Padding 대신 AES/GCM/NoPadding을 사용하면,
// 인증을 포함해서 위변조 탐지 가능하여 더 권장됩니다.
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
byte[] encrypted = c.doFinal(str.getBytes("UTF-8"));
String enStr = new String(Base64.encodeBase64(encrypted));
return enStr;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//복호화
public String aesDecode(String str) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(sessionKey, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] cipheredText = cipher.doFinal(Base64.decodeBase64(str));
return (new String(cipheredText, "UTF-8"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
AES256Util 암호화 예시
AES256Util aes256Util = new AES256Util("32자리키값");
String encStr = aes256Util.aesEncode("암호화할값");
base64 인코딩 방법
import java.util.Base64;
public class Base64Util {
public String base64Encode(String str) throws UnsupportedEncodingException {
String result = "";
byte[] targetBytes = str.getBytes("UTF-8");
Base64.Encoder encoder = Base64.getEncoder();
String encodedString = encoder.encodeToString(targetBytes);
result = encodedString;
return result;
}
public String base64Decode(String str) throws UnsupportedEncodingException {
String result = "";
Base64.Decoder decoder = Base64.getDecoder();
byte[] decodedBytes2 = decoder.decode(str);
result = new String(decodedBytes2, "UTF-8");
return result;
}
}
Base64는 암호화가 아닌 단순 인코딩으로, 보안 목적이 아닌 데이터 전송 및 표현을 위해 사용됩니다.
base64 인코딩 예시
Base64Util base64Util = new Base64Util();
String encStr = base64Util.base64Encode("암호화할값");