๐Ÿ‘จ‍๐Ÿ‘ฉ‍๐Ÿ‘ง‍๐Ÿ‘ฆ Project/๐Ÿ“บ KIOSEK

AES-256 ๋Œ€์นญํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ†ตํ•œ ํ•™๋ฒˆ, ์ด๋ฉ”์ผ ๊ฐœ์ธ์ •๋ณด ์•”ํ˜ธํ™”

DevPoong 2023. 5. 15. 00:39

1. ๋ฌธ์ œ ์ƒํ™ฉ


๋‚ด๊ฐ€ ๋งŒ๋“  ์‚ฌ์ดํŠธ์—์„œ๋Š” ์•„๋ฌด๋ž˜๋„ ์šฐ๋ฆฌ ํ•™๊ต ๋Œ€ํ•™์ƒ์„ ์ƒ๋Œ€๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํšŒ์›๊ฐ€์ž…์‹œ ํ•™๋ฒˆ, ํŒจ์Šค์›Œ๋“œ, ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•œ๋‹ค.
์ด ์ •๋ณด๋Š” ๊ฐœ์ธ์ •๋ณด ์ด๋ฏ€๋กœ ์•”ํ˜ธํ™” ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

ํŒจ์Šค์›Œ๋“œ ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๋น„๊ต๋งŒ ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ๋‹จ๋ฐฉํ–ฅ ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ด์šฉํ•˜๋Š” PasswordEncoder์˜ ๋„์›€์„ ํ†ตํ•ด ์‰ฝ๊ฒŒ ์•”ํ˜ธํ™” ํ•˜์˜€๋Š”๋ฐ,
 ํ•™๋ฒˆ์ด๋‚˜ ์ด๋ฉ”์ผ์€ ์ •๋ณด๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ณตํ˜ธํ™”ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ์–‘๋ฐฉํ–ฅ ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ๋จผ์ € ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด ๋จผ์ € AES256์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•˜์˜€๋‹ค.

2. ๋ฌธ์ œ ํ•ด๊ฒฐ


โœ๏ธ AES-256 ๋Œ€์นญํ‚ค ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์ž

AES(Advanced Encryption Standard)

AES ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์•”ํ˜ธํ™” ๋ฐ ๋ณตํ˜ธํ™”์— ๋™์ผํ•œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์นญํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.
DES ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฐฉ์‹๋„ ์žˆ์ง€๋งŒ ์ด๋Š” 64 ๋น„ํŠธ์˜ ๊ธธ์ด๋ฅผ ๊ฐ€์ง„ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•˜๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์–ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค.


๋Œ€์นญํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ๋Œ€ํ•ด์„œ๋Š” ์•ž ๊ฒŒ์‹œ๋ฌผ์—์„œ HTTPS์— ๋Œ€ํ•ด์„œ ์„ค๋ช…ํ•˜๋ฉด์„œ ๋งํ–ˆ์ง€๋งŒ
๋Œ€์นญํ‚ค๊ฐ€ ๋…ธ์ถœ๋˜์—ˆ์„ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์ง€๋งŒ ์žฅ์ ์œผ๋กœ๋Š” ๋†’์€ ์•ˆ์ •์„ฑ๊ณผ ํ”„๋กœ์„ธ์‹ฑ ์†๋„๊ฐ€ ๋น ๋ฅด๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

AES ์•”ํ˜ธํ™”์—๋Š” Key, IV(Initialize Vector), Cipher Mode(EBC/CBC ๋“ฑ), Padding Mode(PKCS5/PKCS7 ๋“ฑ) 4๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

โœ๏ธ Secret Key์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž

AES-128, AES-192, AES-256 ๋“ฑ์˜ ์ข…๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉฐ ์ˆซ์ž๋Š” ํ‚ค์˜ ๊ธธ์ด(bit)๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
๋‚ด๊ฐ€ ์‚ฌ์šฉํ•  AES-256 ๋ฐฉ์‹์€ ํ‚ค๊ฐ€ 256bits=32byte ์ธ ๊ฒƒ์ด๋‹ค.

 

โœ๏ธ Padding Mode์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž

Block Cipher์˜ ๊ฒฝ์šฐ์—๋Š” ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๊ฐ€ ๋ธ”๋ก ํฌ๊ธฐ์˜ ์ •์ˆ˜๋ฐฐ๊ฐ€ ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ์— padding์ด ํ•„์š”ํ•˜๋‹ค.
๋Œ€๋ถ€๋ถ„์˜ ๋Œ€์นญํ˜• ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ 1. No Padding 2. PKCS#5 Padding ๋‘ ํŒจ๋”ฉ ์œ ํ˜• ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

PKCS(Public Key Cryptography Standard)#5
- ์ถ”๊ฐ€๋  ๋ฐ”์ดํŠธ ์ˆ˜๋กœ ์ฑ„์šฐ๋Š” ๋ฐฉ๋ฒ•
ex) 8๋ฐ”์ดํŠธ ๋ธ”๋Ÿญ์ธ ๊ฒฝ์šฐ
๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ 3: ddd55555
๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ 5: ddddd333

 

โœ๏ธ Cipher Mode์™€ IV ์ดˆ๊ธฐํ™” ๋ฒกํ„ฐ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž

1. ECB(Electric CodeBook) Mode
- ํ‰๋ฌธ ๋ธ”๋ก์„ ์•”ํ˜ธํ™”ํ•œ ๊ฒƒ์ด ๊ทธ๋Œ€๋กœ ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก์ด ๋˜๋Š” ๋ฐฉ๋ฒ•.
- ๋™์ผํ•œ ๋‚ด์šฉ์„ ๊ฐ–๋Š” ํ‰๋ฌธ ๋ธ”๋ก์€ ์ด์— ๋Œ€์‘๋˜๋Š” ๋™์ผํ•œ ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก์œผ๋กœ ๋ณ€ํ™˜๋œ๋‹ค.
- ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ชจ๋“œ์ด์ž ๊ฐ€์žฅ ๊ธฐ๋ฐ€์„ฑ์ด ๋‚ฎ์€ ๋ชจ๋“œ์ด๋‹ค.
- ํ‰๋ฌธ ๋ธ”๋ก๊ณผ ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก์ด ํ•ญ์ƒ 1๋Œ€1 ๊ด€๊ณ„๋ฅผ ์œ ์ง€ํ•œ๋‹ค.
- ์•”ํ˜ธ๋ฌธ์„ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ํ‰๋ฌธ ์†์— ํŒจํ„ด์˜ ๋ฐ˜๋ณต์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜๋ฉฐ, ์ด ํŒจํ„ด์„ ํ†ตํ•ด ์•”ํ˜ธํ•ด๋…์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
=> ์ด ๋ชจ๋“œ๋Š” ์•ˆ์ „ํ•˜์ง€ ์•Š๋‹ค.

2. CBC(Cipher Block Chaining) Mode
- ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก์„ ๋งˆ์นœ Chain ์ฒ˜๋Ÿผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•.
- 1๋‹จ๊ณ„ ์•ž์˜ ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก์— ํ‰๋ฌธ ๋ธ”๋ก์„ XOR ํ•˜๊ณ ๋‚˜์„œ ์•”ํ˜ธํ™”๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
- ๋”ฐ๋ผ์„œ ์ƒ์„ฑ๋˜๋Š” ๊ฐ๊ฐ์˜ ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก์€ ํ˜„์žฌ ํ‰๋ฌธ ๋ธ”๋ก ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ทธ ์ด์ „์˜ ํ‰๋ฌธ ๋ธ”๋ก๋“ค์˜ ์˜ํ–ฅ๋„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.
๋”ฐ๋ผ์„œ ํ‰๋ฌธ ๋ธ”๋ก์„ ์•”ํ˜ธํ™”ํ•  ๋•Œ๋Š” <1๋‹จ๊ณ„ ์•ž์˜ ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก>์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ <1๋‹จ๊ณ„ ์•ž์˜ ์•”ํ˜ธ๋ฌธ ๋ธ”๋ก>์„ ๋Œ€์‹ ํ•  ๋น„ํŠธ์—ด์ธ ํ•œ ๊ฐœ์˜ ๋ธ”๋ก์„ ์ค€๋น„ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

์ด ๋น„ํŠธ์—ด์„ ์ดˆ๊ธฐํ™” ๋ฒกํ„ฐ IV(Initialization Vector)๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.
์ดˆ๊ธฐํ™” ๋ฒกํ„ฐ๋Š” ๋น„๋ฐ€ํ‚ค์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์†ก์‹ ์ž์™€ ์ˆ˜์‹ ์ž๊ฐ„์— ๋ฏธ๋ฆฌ ์•ฝ์†๋˜์–ด ์žˆ์–ด์•ผ ํ•˜์ง€๋งŒ ๊ณต๊ฐœ๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

โœ๏ธ ์ž์„ธํ•œ ์„ค๋ช… ์ „์— ์šฐ์„  ์ฝ”๋“œ๋ฅผ ๋ณด์ž

@Component
public class AES256 {

    private String keyDir;
    private String ivDir;

    private static String alg = "AES/CBC/PKCS5Padding";

    private byte[] iv;

    private byte[] key;

    public AES256(@Value("${fileDir.aeskey}") String keyDir,
                  @Value("${fileDir.iv}") String ivDir) throws Exception {
        this.ivDir = ivDir;
        this.keyDir = keyDir;
        this.key = getKey();
        this.iv = generateIv();
    }

    public String encrypt(String text) {
        byte[] encrypted = null;
        try{
            Cipher cipher = Cipher.getInstance(alg);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParamSpec);

            encrypted = cipher.doFinal(text.getBytes("UTF-8"));

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return Base64.getEncoder().encodeToString(encrypted);
    }

    public String decrypt(String cipherText) throws Exception {

        byte[] decrypted = null;

        try {
            Cipher cipher = Cipher.getInstance(alg);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParamSpec);

            byte[] decodedBytes = Base64.getDecoder().decode(cipherText);
            decrypted = cipher.doFinal(decodedBytes);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return new String(decrypted, "UTF-8");
    }

    private byte[] generateIv() throws Exception {
        File keyFile = new File(ivDir);
        FileInputStream fis = new FileInputStream(keyFile);
        byte[] keyBytes = new byte[(int) keyFile.length()];
        fis.read(keyBytes);
        fis.close();

        return keyBytes;
    }

    private byte[] getKey() throws Exception {
        File keyFile = new File(keyDir);
        FileInputStream fis = new FileInputStream(keyFile);
        byte[] keyBytes = new byte[(int) keyFile.length()];
        fis.read(keyBytes);
        fis.close();

        return keyBytes;
    }
}

โœ๏ธ ๋‹ค์‹œ ์„ค๋ช… ์žฌ๊ฒŒ

๋Œ€์ถฉ ํ๋ฆ„์€ ํŒŒ์•…ํ–ˆ์„ ๊ฒƒ์ด๋‹ค.

์•”ํ˜ธํ™”๋Š” plain text  -> plain bytes -> encrypt -> encrypted bytes -> encrypted Base64 Text
๋ณตํ˜ธํ™”๋Š” ๋‹น์—ฐํžˆ ๊ทธ ๋ฐ˜๋Œ€๋กœ ํ˜๋Ÿฌ๊ฐ€๊ฒŒ ๋œ๋‹ค.

์šฐ์„  ์œ„์—์„œ ์‚ฌ์šฉํ•œ Cipher, SecretKeySpec, IvParameterSpec๊ณผ ๊ฐ™์€ ํด๋ž˜์Šค๋“ค์€ Java์˜ ๊ธฐ๋ณธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•œ๋‹ค.

๋‚˜๋Š” Secret key์™€ iv๋ฅผ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜์˜€๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณด์ž.
Cipher ํด๋ž˜์Šค๋Š” ์•”ํ˜ธํ™”, ๋ณตํ˜ธํ™” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ Java Cryptography Extension๋ฅผ ๊ตฌ์„ฑํ•˜๋Š”๋ฐ ํ•ต์‹ฌ์ ์ธ ์š”์†Œ์ด๋‹ค.
CBC๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, PKCS5 Padding Scheme๋กœ ์ดˆ๊ธฐํ™”๋œ Cipher ์ธ์Šคํ„ด์Šค๋ฅผ ์–ป๊ฒŒ๋œ๋‹ค.

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

๊ทธ๋’ค์— SecretKeySpec ์ธ์Šคํ„ด์Šค๋ฅผ ๋น„๋ฐ€ํ‚ค๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•˜๊ณ , IvParameterSpec ์ธ์Šคํ„ด์Šค๋ฅผ iv ๊ฐ’์„ ํ†ตํ•ด ์ƒ์„ฑํ•œ๋‹ค.

โœ๏ธ Secret Key์™€ IV ์ƒ์„ฑ ๋ฐฉ๋ฒ•

package org.example;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.FileOutputStream;
import java.security.SecureRandom;
import java.util.Base64;

public class KeyGenerator {
    public static void main(String[] args) throws Exception {

        // AES 256-bit key ์ƒ์„ฑ
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(256);
        SecretKey secretKey = keyGen.generateKey();
        byte[] keyBytes = secretKey.getEncoded();
        System.out.println("Generated AES key (Base64 encoded): " + Base64.getEncoder().encodeToString(keyBytes));

        try (FileOutputStream fos = new FileOutputStream("/example/kiosek/key.txt")) {
            fos.write(keyBytes);
        }


        // iv ๊ฐ’ ์ƒ์„ฑ
        SecureRandom secureRandom = new SecureRandom();
        byte[] keyBytes = new byte[16];
        secureRandom.nextBytes(keyBytes);
        System.out.println("Generated iv key: " + Base64.getEncoder().encodeToString(keyBytes));

        try (FileOutputStream fos = new FileOutputStream("/example/kiosek/iv.txt")) {
            fos.write(keyBytes);
        }
    }
}