JAVA 심화

[JAVA] 심화 - 양방향 암호화 기법, 대칭 키 암호 알고리즘 (1)

beginner-in-coding 2024. 12. 30. 14:06

01. 암호화/복호화 정의

  • 암호화: 우리가 사용하는 평범한 문장(평문)을 의미할 수 없는 암호문으로 변환하는 것
  • 복호화: 암호문을 평문으로 되돌리는 것


02. 암호 알고리즘: 암호화/복호화를 할 때 사용하는 알고리즘

  • 양방향 암호 알고리즘: 대칭 키 암호 알고리즘, 비대칭 키 암호 알고리즘
  • 단방향 암호 알고리즘: 해시 알고리즘

03. 암호화 키/복호화 키

  • 암호화 키: 암호화를 위한 키
  • 복호화 키: 복호화를 위한 키

 


04. 대칭 키 암호 알고리즘 vs 비대칭 키 암호 알고리즘

  • 대칭 키 암호 알고리즘: 암호화 키 == 복호화 키
  • 비대칭 키 암호 알고리즘: 암호화 키 != 복호화 키

05. 대칭 키 암호 알고리즘 종류

*권고 여부: 암호화 강도를 고려해 전문가들은 최소 128비트 이상의 알고리즘 사용을 권고함*

*AES: 예제로 사용할 알고리즘*


06. 대칭 키 암호 알고리즘 구현하기


(1) 대칭 키 암호 알고리즘 - 암호화

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;

public class AESEncryption {
    private SecretKeySpec secretKey;
    private byte[] initialVector;

    // 대칭 키 암호화를 위한 메소드
    public String encrypt(String plainText, String secretString) throws Exception{
        byte[] sha256 = null;
        byte[] cipherText = null;

        //secretString 에 대한 해시 값 생성
        sha256 = MessageDigest.getInstance("SHA-256")
                .digest(secretString.getBytes(StandardCharsets.UTF_8));

        // 해시 값을 16바이트 배열로 나누어 저장
        sha256 = Arrays.copyOf(sha256, 16);

        // 해시 값과 AES 알고리즘을 이용하여 비밀 키 생성
        secretKey = new SecretKeySpec(sha256, "AES");

        // 첫 번째 블록 암호화를 위해 난수값을 생성하며 초기화 벡터 생성
        initialVector = new byte[16];
        SecureRandom random = new SecureRandom();
        random.nextBytes(initialVector);

        /* 암호화 객체 생성
        *   - 암호 알고리즘: ASE
        *   - 블록 암호화 운영 모드: CBC
        *   - 패딩: PKCS5Padding
        * */
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        // 암호화 모드, 대칭 키, 초기화 벡터 값을 입력하여 암호화 객체 초기화
        cipher.init(Cipher.ENCRYPT_MODE,
                secretKey,
                new IvParameterSpec(initialVector));

        // 데이터 암호화 수행
        cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        // Base64 로 인코딩하여 암호문 반환
        return Base64.getEncoder().encodeToString(cipherText);
    }

06-1. 운영 모드: 우리가 사용하는 암호화 방식은 데이터를 블록으로 나눔, 이를 위한 운영 모드로는

(1) ECB(Electronic Code Book)

  • 데이터를 블록으로 나눈 후 각각 암호화
  • 동일한 블록에 동일한 암호문 생성

(2) CBC(Cipher-Block Chaining)

  • 데이터를 블록으로 나눈 후 체인을 만들어 암호화
  • 동일한 블록에 다른 암호문 생성

*체인: 이전 블록의 암호화 결과를 다음 블록의 암호화에 사용하는 방식

*보안 강도: CBC > ECB

*위의 코드에서는 CBC를 사용, CBC는 가장 첫 번째 블록의 경우, 이전 블록이 없기 때문에 초기화 벡터를 랜덤함수를 통해 생성하여 사용함*


06-2. 패딩 방식(Public-Key Cryptography Standard, PKCS)

  • 블록 암호화 방식을 이용하려면 블록의 길이는 일정해야 함
  • 블록의 크기가 16바이트가 되지 않으면 크기를 맞춰주기 위한 임의의 값 추가
  • PKCS#5: 패딩 값 최대 8 바이트
  • PKCS#7: 패딩 값 최대 256바이트

*요즘 사용하는 알고리즘은 블록 크기가 16바이트 이상이기 대문에 패딩 값을 많이 채워주는 PKCS#&을 사용

*하지만 JAVA에서 위 패딩을 사용할 경우, 예외(NoSuchAlgorithmException)이 발생,

*PKCS#5를 사용하더라도 내부적으로는 PKCS#7로 정상 작동함 


06-3. Base64형식으로 인코딩

  • 암호문은 이진 데이터로 생성
  • 이진 데이터는 네트워크 통신 과정에서 신호왜곡, 비트 오류 등의 손실 위험이 존재
  • 데이터베이스 관점: 이진 데이터보다 문자열 데이터 처리가 더 용이
  • 다른 애플리케이션과 연동하거나 데이터 입출력 시 Base64로 표현하는 규약이 존재

(2) 대칭 키 암호 알고리즘 - 복호화

    // 대칭 키 복호화 함수
    public String decrypt(String cipherText, SecretKeySpec secretKey) throws Exception{
        byte[] plainText = null;

        // 암호화 객체 생성
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        // 암호화 모드, 대칭 키, 초기화 벡터 값을 입력하여 암호화 객체 초기화
        cipher.init(Cipher.DECRYPT_MODE,
                secretKey,
                new IvParameterSpec(initialVector));

        // 데이터 복호화 수행
        plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));

        // 바이트 형식으로 복호화된 데이터를 문자열 형식으로 변환하여 반환
        return new String(plainText);
    }

    // 대칭 키 및 암호 알고리즘 조회
    public SecretKeySpec getSecretKey(){
        return secretKey;
    }
}

(3) 대칭 키 암호 알고리즘 - 결과

  • plain Text로 123456을 입력하였을 때
  • 대칭 키 AES로 암호화 하면 encrypted와 같이 결과가 나오고
  • 이 결과를 다시 복호화하면 decrypted와 같이 원래의 plain Text가 나오는 것을 볼 수 있음