일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 도커
- 인텔리제이
- 문자열
- 구현
- HashMap
- HTTP
- spring
- 구름LEVEL
- 자료구조
- 프로그래머스
- Java
- 개발자
- dfs
- 주니어
- 해결
- 백엔드
- 배열
- 명령어
- 스타트업
- docker
- 스프링
- Linux
- IntelliJ
- 코딩테스트
- spring boot
- 스프링부트
- 이직
- 해시맵
- bfs
- 스프링 부트
- Today
- Total
마이의 개발 블로그
[Java] Cipher(AES + CBC + PKCS5) 암호화, 복호화 예제 (시크릿키, 초기화 벡터가 원문인 경우) 본문
[Java] Cipher(AES + CBC + PKCS5) 암호화, 복호화 예제 (시크릿키, 초기화 벡터가 원문인 경우)
개발자마이 2024. 4. 3. 15:00
배경
이미 암호화된 문자열로 발급된 키값을 환경변수 등에서 가져와 프로그램에서 사용하는 경우 문자열을 기준으로 작업할 수 있도록 코드의 변경이 필요합니다. 또한 시크릿 키의 경우에도 암호화 되지 않은 문자열을 기준으로 한 번 더 암호화하는 과정을 거쳐야 하는데 보통 제시되는 예시들은 시크릿 키를 별도로 암호화하여 발급한 후에 그 암호문을 그대로 사용하는 형태로 동작하는 것이 대부분이었습니다. 그래서 약간의 수정을 거쳐 현재 프로젝트에 맞는 형태로 input과 output을 가공하여 사용하는 클래스를 작성했습니다.
이 예제는 AES 암호화 알고리즘을 사용하여 1) 암호화(encrypt), 2) 복호화(decrypt) 동작을 수행하는 코드입니다. 두 동작 모두 인수로 암호화되지 않은 String 타입 시크릿키(secretKey)와 초기화 벡터(iv)를, 각 동작에 따라 원문이나 암호문을 인수로 받습니다. 운영모드는 CBC, 암호화 패딩은 PKCS5(=PKCS7)입니다.
주의할 점
예제에서는 시크릿키(secretKey)와 초기화벡터(iv)를 멤버변수로 저장하여 사용하고 있습니다만 보안을 위해서는 프로그램과 분리하여 관리하는 게 더 좋습니다. 또한 초기화벡터의 경우 고정값이 아닌 랜덤값을 기준으로 생성하여 암호화 하는 방식이 권장되는데 저의 경우 현재 프로젝트의 상황과는 맞지 않아 이 방식은 배제한 상태입니다.
Note
자바 Cipher에서는 암호화 패딩 방식 선택 시 PKCS7이 따로 없습니다. 이것 때문에 NoSuchAlgorithmException이 종종 발생하는 것 같은데 PKCS5Padding를 적어주시면 됩니다. 5와 7의 차이는 패딩 블록의 크기가 8바이트로 고정되는지(PKCS5), 가변적인지(PKCS7)의 차이라고 하는데, 알고리즘의 동작 원리는 동일합니다.
코드
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AESCryptor {
final private String secretKey = "시크릿키(암호화 되지 않은 32자 문자열)";
final private String iv = "초기화벡터(암호화 되지 않은 16자 문자열)";
public String getSecretKey() {
return secretKey;
}
public String getIv() {
return iv;
}
public String encrypt(String plaintext, String secretKey, String iv) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public String decrypt(String encryptedText, String secretKey, String iv) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
//사용 예제
public static void main(String[] args) throws Exception {
AESCryptor aesCryptor = new AESCryptor();
String plainText = "RES4V-ROD8E-R1395"; //암호화할 텍스트
String encryptedText = aesCryptor.encrypt(plainText, aesCryptor.getSecretKey(), aesCryptor.getIv());
String decryptedText = aesCryptor.decrypt(encryptedText, aesCryptor.getSecretKey(), aesCryptor.getIv());
System.out.println("plain text: " + plainText);
System.out.println("encrypted text: " + encryptedText);
System.out.println("decrypted text: " + decryptedText);
}
}
실행 결과(main 함수)
'개발지식 > Java' 카테고리의 다른 글
[Java] 기상청 단기예보 API 데이터 가공하기 (JSONParser) (0) | 2022.02.16 |
---|---|
[Java] 오버라이딩(overriding)과 오버로딩(overloading) (0) | 2022.02.14 |