πŸ’Š Java & Kotlin & Spring/- Java & kotlin

[Java] Bouncy Castle 라이브러리λ₯Ό μ΄μš©ν•˜μ—¬ 파일 μ•”ν˜Έν™”, λ³΅ν˜Έν™” ν•˜κΈ° (feat.블둝 μ•”ν˜Έμ˜ SEED μ•Œκ³ λ¦¬μ¦˜)

Wonit 2021. 6. 19. 02:24

졜근 BoB 면접을 μ€€λΉ„ν•˜λ©΄μ„œ μ§€λ‚œ ν”„λ‘œμ νŠΈλ“€μ„ λ˜λŒμ•„λ³΄κ³  μžˆμ—ˆλ‹€.

 

λ‚΄ μ§€λ‚œ ν”„λ‘œμ νŠΈ 쀑에 ν•™κ³Ό μ—°κ΅¬μ‹€μ—μ„œ 학사 μ‘Έμ—… 논문에 μ°Έμ—¬ν•΄μ„œ 파일 μ•”λ³΅ν˜Έν™” λͺ¨λ“ˆμ„ κ΅¬ν˜„ν–ˆλ˜ κ²½ν—˜μ΄ μžˆλ‹€.

 

ν˜„μž¬ ν•΄λ‹Ή ν”„λ‘œκ·Έλž¨μ˜ μ†ŒμŠ€ μ½”λ“œλŠ” λ³΄μ•ˆμƒ κ³΅κ°œλ˜μ–΄ μžˆμ§€ μ•Šκ³  λ‚˜λ„ 2λ…„ μ „μ˜ ν”„λ‘œμ νŠΈλΌμ„œ 기얡이 잘 λ‚˜μ§€ μ•Šμ•˜λ‹€.

 

κ·Έλž˜μ„œ μ΅œλŒ€ν•œ κ·Έ 기얡을 더듬어 파일 μ•”λ³΅ν˜Έν™” λͺ¨λ“ˆμ„ λ‹€μ‹œ κ΅¬ν˜„ν•˜λ € ν•˜λŠ”λ°, 생각보닀 ν•œκΈ€λ‘œ 된 μžλ£Œκ°€ λ§Žμ§€ μ•Šμ•„ λ‹€λ₯Έ μ‚¬λžŒλ“€μ—κ²Œ κ³΅μœ ν•œλ‹€λ©΄ κ½€ 도움 될 것 κ°™μ•„μ„œ κ·Έ 과정을 ν•œ 번 ν¬μŠ€νŒ…ν•˜λ € ν•œλ‹€.

 


λͺ©μ°¨

  • Bouncy Castle
  • 블둝 μ•”ν˜Έ, SEED μ•Œκ³ λ¦¬μ¦˜μ΄λž€?
  • ν”„λ‘œμ νŠΈ 생성
    • ν”„λ‘œμ νŠΈ 생성
    • μ˜μ‘΄μ„± μΆ”κ°€
    • FileUtil.class κ΅¬ν˜„
    • BouncyModule.class κ΅¬ν˜„
    • Encryption.class κ΅¬ν˜„
  • ν…ŒμŠ€νŠΈ

Bouncy Castle

μš°μ„  λ‚˜λŠ” 파일 μ•”ν˜Έν™” λ³΅ν˜Έν™”λ₯Ό μœ„ν•΄ Bouncy Castle 라이브러리λ₯Ό μ΄μš©ν•  것이닀.

 

Bouncy Castle은 유λͺ…ν•œ μ•”ν˜Έν™” 라이브러리둜 Java, C# 버전에 따라 λ°°ν¬λœλ‹€.

 

λŒ€μΉ­ν‚€ μ•”ν˜Έ, 블둝 μ•”ν˜Έ, SEED μ•Œκ³ λ¦¬μ¦˜μ΄λž€?

 

μš°λ¦¬λŠ” Bouncy Castle 둜 μ•”ν˜Έν™”λ₯Ό μˆ˜ν–‰ν•  것인데, μ‹€μ§ˆμ μœΌλ‘œ μ•”ν˜Έν™”λ₯Ό μˆ˜ν–‰ν•  μ•Œκ³ λ¦¬μ¦˜μ€ λŒ€μΉ­ν‚€ μ•”ν˜Έ μ‹œμŠ€ν…œμ„ μ΄μš©ν•  것이닀.

 

λŒ€μΉ­ν‚€ μ•”ν˜ΈλŠ” μ•”ν˜Έν™” 킀와 λ³΅ν˜Έν™” ν‚€κ°€ λ™μΌν•œ μ•”ν˜Έ μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€.

 

λŒ€μΉ­ ν‚€ μ•”ν˜Έ μ‹œμŠ€ν…œμ—μ„œλŠ” 2κ°€μ§€ 방법이 λŒ€ν‘œμ μ΄λ‹€.

 

  1. 데이터λ₯Ό 블둝 λ‹¨μœ„λ‘œ μ•”ν˜Έν™” : 블둝 μ•”ν˜Έ
  2. 데이터λ₯Ό 슀트림 λ‹¨μœ„λ‘œ μ•”ν˜Έν™” : 슀트림 μ•”ν˜Έ

 

블둝 μ•”ν˜Έλž€?

λŒ€ν‘œμ μΈ 블둝 μ•”ν˜Έμ˜ κ΅¬μ‘°λŠ” Feistal κ³Ό SPN ꡬ쑰가 μ‘΄μž¬ν•˜λŠ”λ°, μš°λ¦¬λŠ” νŽ˜μ΄μŠ€νƒˆ ꡬ쑰λ₯Ό μ΄μš©ν•˜λ € ν•œλ‹€.

 

νŽ˜μ΄μŠ€νƒˆ κ΅¬μ‘°λŠ” λΌμš΄λ“œ ν•¨μˆ˜κ°€ 반볡적으둜 μ μš©λ˜λŠ” ꡬ쑰둜 λŒ€ν‘œμ μœΌλ‘œλŠ” DES와 κ΅­μ‚° μ œν’ˆμΈ SEEDκ°€ μ‘΄μž¬ν•œλ‹€.

 

참고둜 SPN κ΅¬μ‘°λŠ” κ°€μž₯ 널리 μ‚¬μš©λ˜λŠ” AESκ°€ μ‘΄μž¬ν•œλ‹€

 

μš°λΌλŠ” 블둝 μ•”ν˜Έμ˜ SEED μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•˜λ € ν•œλ‹€.

 

SEED μ•Œκ³ λ¦¬μ¦˜μ΄λž€?

 

SEED μ•Œκ³ λ¦¬μ¦˜μ€ 1999λ…„ 2μ›” ν•œκ΅­μΈν„°λ„·μ§„ν₯ 원과 κ΅­λ‚΄ μ•”ν˜Έμ „λ¬Έκ°€λ“€μ΄ 순수 κ΅­λ‚΄κΈ°μˆ λ‘œ κ°œλ°œν•œ 128λΉ„νŠΈ 블둝 μ•”ν˜Έ μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€.

 

1999λ…„ κ°œλ°œλ˜μ—ˆλŠ”λ° 2005λ…„ ꡭ제 ν‘œμ€€ν™” 기ꡬ인 ISO/IEC ꡭ제 λΈ”λ‘μ•”ν˜Έμ•Œκ³ λ¦¬μ¦˜ IETFν‘œμ€€μœΌλ‘œ μ œμ •λ˜μ—ˆλ‹€.

 

이제 μ–΄λŠ 정도 기본은 λ˜μ—ˆμœΌλ‹ˆ ν”„λ‘œμ νŠΈλ₯Ό μ‹€μ œλ‘œ λ§Œλ“€μ–΄λ³΄μž!

 

ν”„λ‘œμ νŠΈ 생성

 

ν”„λ‘œμ νŠΈλŠ” Gradle ν”„λ‘œμ νŠΈλ‘œ μƒμ„±ν•˜λ„λ‘ ν•˜μž!

 

 

그리고 Bouncy Castle 라이브러리 μ˜μ‘΄μ„±μ„ build.gradle 에 μΆ”κ°€ν•΄μ£Όμž!

 

dependencies {
    implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.68'
}

 

그리고 μ•„λž˜ 기술된 λ‚΄μš©λŒ€λ‘œ 잘 κ΅¬ν˜„ν•œλ‹€λ©΄ λ‹€μŒκ³Ό 같은 디렉토리 ꡬ쑰λ₯Ό κ°–κ²Œ 될 것이닀

 

 

FileUtil.java κ΅¬ν˜„

 

ν•΄λ‹Ή ν΄λž˜μŠ€λŠ” μ•”ν˜Έν™”ν•  νŒŒμΌμ„ 뢈러였고, μ•”ν˜Έν™”λœ νŒŒμΌμ„ μ €μž₯ν•  μœ ν‹Έ ν΄λž˜μŠ€μ΄λ‹€.

 

public class FileUtil {

    private FileInputStream fileInputStream;
    private FileOutputStream fileOutputStream;

    public byte[] getFileBytesFrom(String path) throws IOException {
        // 파일 객체 생성
        File file = new File(path);
        fileInputStream = new FileInputStream(file);

        byte[] fileBytes = new byte[(int)file.length()];

        fileInputStream.read(fileBytes);

        fileInputStream.close();
        return fileBytes;
    }

    public void saveFile(String path, byte[] fileData) throws IOException {
        fileOutputStream = new FileOutputStream(path);

        fileOutputStream.write(fileData);

        fileOutputStream.close();
    }
}

 

BouncyModule.java κ΅¬ν˜„

 

이제 μ‹€μ œλ‘œ BouncyCastle 둜 블둝 μ•”ν˜Έλ₯Ό μ΄μš©ν•΄μ„œ νŒŒμΌμ„ μ•”ν˜Έν™” ν•΄λ³΄μž

 

public class BouncyModule {

    public byte[] encrypt(String key, byte[] plainText) {
        byte[] keyBytes = key.getBytes();

        // 블둝 μ•”ν˜Έ 운용
        // 블둝보닀 데이터가 짧을 경우 νŒ¨λ”©μ„ μ‚¬μš©ν•¨
        // 블둝 μ•”ν˜Έ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œλŠ” SEED μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•¨
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new SEEDEngine());
        // μ΄ˆκΈ°ν™” 및 ν‚€ νŒŒλΌλ―Έν„° 생성 첫 번째 λ§€κ°œλ³€μˆ˜κ°€ true 라면 μ•”ν˜Έν™” λͺ¨λ“œ
        cipher.init(true, new KeyParameter(keyBytes));

        return getBytes(plainText, cipher);
    }

    public byte[] decrypt(String key, byte[] cipherText) {
        byte[] keyBytes = key.getBytes();

        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new SEEDEngine());
        cipher.init(false, new KeyParameter(keyBytes));

        return getBytes(cipherText, cipher);
    }

    private byte[] getBytes(byte[] targetData, BufferedBlockCipher cipher) {
        byte[] outputData = new byte[cipher.getOutputSize(targetData.length)];

        int tam = cipher.processBytes(targetData, 0, targetData.length, outputData, 0);

        try {
            cipher.doFinal(outputData, tam);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return outputData;
    }
}

 

λͺ¨λ“œλŠ” 블둝 μ•”ν˜Έ λͺ¨λ“œλ₯Ό μš΄μš©ν•œλ‹€.

 

λ§Œμ•½ 블둝보닀 λ°μ΄ν„°μ˜ 길이가 짧을 경우 νŒ¨λ”©μ„ μ‚¬μš©ν•˜κ³ , 블둝 μ•”ν˜Έ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ SEED μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•œλ‹€κ³  μ§€μ •ν•΄μ€€λ‹€.

 

EncryptUtil.java κ΅¬ν˜„

 

μœ„μ˜μ—μ„œ μƒμ„±ν•œ 파일 μœ ν‹Έκ³Ό bouncy castle λͺ¨λ“ˆμ„ μ΄μš©ν•΄μ„œ μ•”ν˜Έν™” μœ ν‹Έμ„ κ΅¬ν˜„ν•΄λ³΄μž

 

public class EncryptUtil {
    private static BouncyModule bouncyModule = new BouncyModule();
    private static FileUtil fileUtil = new FileUtil();

    public static void encryption(String key, String filePath) throws IOException {
        byte[] plainFileBytes = fileUtil.getFileBytesFrom(filePath);
        byte[] encryptedFileBytes = bouncyModule.encrypt(key, plainFileBytes);
        fileUtil.saveFile(filePath, encryptedFileBytes);
    }

    public static void decryption(String key, String filePath) throws IOException {
        byte[] encryptedFileBytes = fileUtil.getFileBytesFrom(filePath);
        byte[] decryptedFile = bouncyModule.decrypt(key, encryptedFileBytes);
        fileUtil.saveFile(filePath, decryptedFile);
    }
}

ν…ŒμŠ€νŠΈ

 

이제 κ΅¬ν˜„μ΄ λλ‚¬μœΌλ‹ˆ 파일 ν•˜λ‚˜λ₯Ό 작고 ν…ŒμŠ€νŠΈν•΄λ³΄μž

 

λ‚˜λŠ” 바탕화면에 μžˆλŠ” 학ꡐ 둜고 png νŒŒμΌμ„ μ•”ν˜Έν™” 해보도둝 ν•˜κ² λ‹€!

 

 

μ•”ν˜Έν™”λ₯Ό ν•˜κΈ° μ „μ—λŠ” 파일이 잘 μ—΄λ¦°λ‹€.

 

이제 main ν•¨μˆ˜μ—μ„œ μ•”ν˜Έν™”λ₯Ό μˆ˜ν–‰ν•˜μž

 

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

        String key = "$2a$10$mxLAM1fAEDPWkFz83IPDH.7yvyY6njmIMSk937jdH9UD3HhbqYPgO";
        String path = "/Users/my/Desktop/logo.png";

        EncryptUtil.encryption(key, path);
    }
}

 

그럼 λ‹€μŒκ³Ό 같이 νŒŒμΌμ„ μ—΄ 수 μ—†λ‹€κ³  λ‚˜μ˜¨λ‹€

 

 

그리고 λ³΅ν˜Έν™”λ₯Ό μˆ˜ν–‰ν•΄λ³΄μž

 

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

        // key 생성 (60 길이)
        String key = "$2a$10$mxLAM1fAEDPWkFz83IPDH.7yvyY6njmIMSk937jdH9UD3HhbqYPgO";
        String path = "/Users/jangwonik/Desktop/logo.png";

        // EncryptUtil.encryption(key, path);
        EncryptUtil.decryption(key, path);
    }
}

 

그럼 λ‹€μŒκ³Ό 같이 λ‹€μ‹œ νŒŒμΌμ„ μ—΄ 수 있게 λ˜μ—ˆλ‹€!