JAVA에서의 암호화 방법 (AES)

단비·2023년 4월 25일
0

학습

목록 보기
17/66

Cipher

  • 암호화, 복호화 기능을 제공
  • 대칭키 알고리즘

암호문의 성질

  • 혼돈(confusion)
    • 암호문으로부터 키를 알아낼 수 없게 하는 성질
    • 키의 비트 하나만 바꿔도 암호문 전체가 바뀌도록 하는 성질
  • 확산(diffusion)
    • 암호문으로부터 원문을 알아낼 수 없게 하는 성질
    • 원문의 비트를 하나만 바꿔도 암호문 전체가 바뀌도록 하는 성질

암호 알고리즘

  • 혼돈과 확산을 달성하기 위해 Substitution과 Permutation을 이용
    Substitution
    - 문자를 다른 문자로 바꾸는 것
    Permutation
    - 문자들의 순서를 바꾸는 것

SPN(Substitution Permutation Network)

  • Substitution-Permutation을 연속하여 수행하도록 이어 놓은 것
  • 데이터를 블록 단위로 처리
  • 대표적인 알고리즘은 AES(블록의 크기는 128비트(16바이트))
    • 모든 데이터가 16바이트 크기를 가질 수 없으므로 운용 모드가 필요

운용 모드

  • 데이터를 블록으로 나누어 처리하고 합치는 것
  • ECBCBC가 대표적

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());

패딩

  • 데이터를 특정 크기로 맞추기 위해서 사용
  • 특정 크기보다 부족한 부분의 공간을 의미 없는 문자들로 채워서 비트수를 맞추는 것
  • PKCS5PKCS7 가 대표적

PKCS5

  • 8바이트 블록의 암호 알고리즘을 가정 시 패딩 크기는 8 - (L mod 8)
    (자바에서는 PKCS5 패딩모드를 사용해도 PKCS7이 적용됨)

PKCS5

  • 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;
profile
tistory로 이전! https://sweet-rain-kim.tistory.com/

0개의 댓글