마이의 개발 블로그

[Java] Cipher(AES + CBC + PKCS5) 암호화, 복호화 예제 (시크릿키, 초기화 벡터가 원문인 경우) 본문

개발지식/Java

[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 함수)

반응형
Comments