코딩마을방범대
JAVA에서의 암호화 방법 (AES) 본문
728x90
Cipher
- 암호화, 복호화 기능을 제공
- 대칭키 알고리즘
암호문의 성질
혼돈(confusion)
- 암호문으로부터 키를 알아낼 수 없게 하는 성질
- 키의 비트 하나만 바꿔도 암호문 전체가 바뀌도록 하는 성질
확산(diffusion)
- 암호문으로부터 원문을 알아낼 수 없게 하는 성질
- 원문의 비트를 하나만 바꿔도 암호문 전체가 바뀌도록 하는 성질
암호 알고리즘
- 혼돈과 확산을 달성하기 위해 Substitution과 Permutation을 이용
Substitution - 문자를 다른 문자로 바꾸는 것
Permutation - 문자들의 순서를 바꾸는 것
SPN(Substitution Permutation Network)
- Substitution-Permutation을 연속하여 수행하도록 이어 놓은 것
- 데이터를 블록 단위로 처리
- 대표적인 알고리즘은 AES(블록의 크기는 128비트(16바이트))
(모든 데이터가 16바이트 크기를 가질 수 없으므로 운용 모드가 필요)
운용 모드
- 데이터를 블록으로 나누어 처리하고 합치는 것
- ECB와 CBC가 대표적
ECB(Electronic Code Book)
- 블록 단위로 처리한 결과를 그대로 이어붙이는 방법
- 같은 값을 갖는 원문 블록은 같은 암호 블록을 출력하기 때문에 원문의 패턴이 그대로 드러남
- IV(제 2의 키값)을 사용하지 않음
CBC(Cipher Block Chaining)
- 원문 블록을 그대로 암호화하지 않고, 직전에 암호화된 블록과 XOR 연산을 한 다음에 암호화를 수행
- 같은 내용을 갖는 원문 블록이라도 전혀 다른 암호문을 갖게 됨
- 첫번째 블록은 직전 암호문이 없어 XOR 연산 대상이 없으므로, 초기화 벡터(initialization vector)를 입력받음
- 직전 블록이 다음 블록의 암호화에 관여하므로 일부 블록만 복호화하고 싶어도 전체를 복호화해야 함
IV(Initialize Vector), 초기화 벡터
- 매번 다른 IV를 생성하면 같은 평문이라도 다른 암호문을 생성할 수 있음
- 암호를 복호화할 사람에게 미리 제공되어야 하고 공개되어도 상관없음
// key를 byte로 변환하여 16byte의 길이만큼 복사한 값을 IV로 사용
private final String iv = key.substring(0, 16); // 16byte
IvParameterSpec ivParamSpec = new IvParameterSpec(iv.getBytes());
패딩
- 데이터를 특정 크기로 맞추기 위해서 사용
- 특정 크기보다 부족한 부분의 공간을 의미 없는 문자들로 채워서 비트수를 맞추는 것
- PKCS5와 PKCS7 가 대표적
PKCS5
- 8바이트 블록의 암호 알고리즘을 가정 시 패딩 크기는 8 - (L mod 8)
(자바에서는 PKCS5 패딩모드를 사용해도 PKCS7이 적용됨)
PKCS7
- 8바이트가 아닌 가변 길이를 가짐
- 블록 크기가 1에서 255까지의 값을 가질 수 있음(255는 한 바이트가 가지는 가장 큰 값)
사용법
1. Cipher 객체 인스턴스화
- 사용할 알고리즘/운용 모드/패딩 방식
getBytes
유니코드 문자열(String)을 바이트코드로 인코딩 해주는 메소드
(byte[] 형태로 리턴됨)
// 1. Cipher 객체를 AES 알고리즘으로 암호화 & CBC operation mode & PKCS5 padding scheme로 초기화
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 2. 변환 알고리즘만 명시하여 초기화도 가능
Cipher cipher = Cipher.getInstance("AES");
2. Keys 세팅
- Secret Key란
평문을 암호화하는데 사용
(AES의 종류가 무엇이냐에 따라 Secret Key의 길이가 달라짐)
String key = "키값";
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
secretKey의 길이에 따라 암호화 방식이 변경됨
32bit : AES-256
24bit : AES-192
24bit : AES-128
3. Iv 생성 (ECB일 경우 생략)
// key를 16byte로 자르기
String iv = key.substring(0, 16); // 16byte
IvParameterSpec ivParamSpec = new IvParameterSpec(iv.getBytes());
4. Cipher 초기화(Initialization)
// 작동 모드 & 키 & 초기화 벡터(IV) - CBC
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParamSpec);
// 작동 모드 & 키 - ECB
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
getPublicKey() 메서드를 호출하여 인증서(certificate)로 부터 공용키를 얻음
Cipher의 작동 모드
- ENCRYPT_MODE (암호화 모드)
- DECRYPT_MODE (복호화 모드)
- WRAP_MODE (key-wrapping 모드)
- UNWRAP_MODE (key-unwrapping 모드)
5. Encrpytion/Decryption
- 암호화 또는 복호화 작업을 위해 doFinal() 메서드를 호출
// text = 암호화 할 텍스트
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
6. 인코딩
// 64bit로 인코딩
Base64.getEncoder().encodeToString(encrypted);
byte : 8bit 2진법(0 or 1)
Hex digits : 16bit
base64 : 64bit
예시
AES/CBC
- 암호화
// Cipher 객체를 인스턴스화 (원하는 변환 형태의 이름을 전달)
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 평문을 암호화 하기 위해 secretKey 생성
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
// 초기화 벡터 생성
IvParameterSpec ivParamSpec = new IvParameterSpec(key.substring(0, 16).getBytes());
// 인코딩 모드로 세팅
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParamSpec);
// 암호화할 텍스트
String text = "01012345678";
// 암호화
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
// 64bit 로 인코딩
Base64.getEncoder().encodeToString(encrypted);
- 복호화
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivParamSpec = new IvParameterSpec(key.substring(0, 16).getBytes());
// 디코딩 모드로 세팅
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParamSpec);
// 복호화
byte[] encrypted = cipher.doFinal(Base64.getDecoder().decode(encText));
new String(encrypted);
AES/ECB
- 암호화
// Cipher 객체를 인스턴스화 (원하는 변환 형태의 이름을 전달)
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// 평문을 암호화 하기 위해 secretKey 생성
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
// 인코딩 모드로 세팅
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParamSpec);
// 암호화할 텍스트
String text = "01012345678";
// 암호화
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
// 64bit 로 인코딩
Base64.getEncoder().encodeToString(encrypted);
- 복호화
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
// 디코딩 모드로 세팅
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 복호화
byte[] encrypted = cipher.doFinal(Base64.getDecoder().decode(encText));
new String(encrypted);
SQL에서 암호화해보기
1. 암호화 모드 확인 (default: aes-128-ecb)
SELECT @@block_encryption_mode;
2. 암호화 모드 변경
SET block_encryption_mode = 'aes-256-cbc';
3. 값 확인
SELECT to_base64(AES_ENCRYPT(암호화할 텍스트, key, iv));
SQL에서 복호화해보기
SELECT AES_DECRYPT(FROM_BASE64(복호화할 텍스트), key, iv)
💡 TIPS!
1. Pageable 생성법
// PageRequest.of(페이지넘버, 사이즈, sort여부)
PageRequest.of(0, 15, Sort.by("id").accending())
2. Sort 생성법
- and를 통해 여러가지의 정렬 가능
Sort idSort = Sort.by("id").ascending()
Sort dateSort = Sort.by("createDate").descending()
idSort.and(dateSort)
3. Arrays 배열 부분 복사 방법
- Arrays.copyOf(copy할 array, index)
// ivHash의 index 16까지 복사
Arrays.copyOf(ivHash, 16);
4. yml에서 변수 가져오는 법
- yml (변수명은 자유)
aes:
key: "비밀키"
- java
Component 또는 Service로 등록해줘야함!
@Component
public class AES256 {
@Value("${aes.key}")
private String key;
728x90
'💡 백엔드 > Java' 카테고리의 다른 글
yml 정보 클래스로 가져오기 (0) | 2023.05.26 |
---|---|
SHA-256 해싱 알고리즘 (0) | 2023.05.26 |
ModelMapper란 (0) | 2023.05.25 |
Autowired를 지양하는 이유 & 정규표현식 (0) | 2023.05.25 |
스케쥴러(Scheduler)에 대하여 (1) | 2023.05.25 |