-
File Encryption, Decryption 구현보안 2021. 5. 17. 00:10
개요
파일 암호화에는 여러가지 방법이 있습니다. 그 중 고급 암호화 표준인 AES를 사용한 파일 암호화를 자바로 구현해보려고 합니다. 구현에 앞서서 대칭 키 암호와 블록 암호에 대해서 학습 후 진행했습니다.
AES와 블록 암호
암호화 알고리즘에서 가장 널리 쓰이는 알고리즘 중 하나는 대칭 키 암호입니다. 대칭 키 암호화는 암호화(Encryption)와 복호화(Decryption)에 같은 키를 사용하는 알고리즘입니다. 특히, 공개 키 암호와 대비해서 성능이 뛰어나다는 장점이 있습니다.
대칭 키 암호는 암호화하는 단위에 따라 스트림 암호와 블록 암호로 구분됩니다. 스트림 암호는 평문을 비트/바이트로 계속 입력 받아서 암호화하는 방식이고, 블록 암호는 평문을 정해진 크기의 블록 단위로 나눠 이에 대응하는 암호화 블록을 만드는 방식입니다.
스트림 암호는 내부적으로 구현에 필요한 의사 난수 생성기가 구조적으로 한계가 있습니다. 그래서, 일반적으로는 블록 암호를 많이 사용하게 되는데, 그 중 블록암호의 대표적인 종류 중 하나가 AES입니다.
프로그램 구조
프로그램은 Input의 파일을 FileInputStream을 통해 읽어들이고, Encryption 후에 FileOutputStream으로 Output.txt파일을 만들어줍니다. 이후, 다시 파일을 읽어들여서 Decryption하고 결과값을 기존 PlainText와 동일한지 비교합니다.
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); IvParameterSpec iv = new IvParameterSpec(ivBytes); Cipher cipher = null; cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); cipher.init(Cipher.ENCRYPT_MODE, key, iv);
자바의 Cipher 클래스는 암호화와 관련한 여러 API를 제공합니다. 이 Cipher 클래스를 이용해서 프로그램을 구현했습니다. Cipher 클래스는 getInstance를 이용해 객체를 생성하는데, 인자로 사용할 암호화 알고리즘을 대입해줍니다.
byte[] fileBytes = new byte[BUF_SIZE]; FileInputStream fis = new FileInputStream("input"); byte[] output = new byte[cipher.getOutputSize(fileBytes.length)]; int processLen = 0; // read fileContent to Buffer int read = -1; while ((read = fis.read(fileBytes)) != -1) { // each loop, encryption content processLen = cipher.update(fileBytes, 0, fileBytes.length, output, 0); } // for padding process, execute doFinal() processLen = cipher.doFinal(output, processLen); // save the file on "output" FileOutputStream fos = new FileOutputStream("output"); fos.write(output); fis.close(); fos.close();
동일한 폴더에 있는 input 파일을 buffer size만큼 읽어들이면서 암호화를 진행합니다. 중요한 점은 doFinal() 메소드를 사용해 패딩 처리를 해줘야합니다.
cipher.init(Cipher.DECRYPT_MODE, key, iv); // initialize buffer's size, for padding size, add 16 bytes byte[] bytesToDecryption = new byte[BUF_SIZE]; FileInputStream fisToDecryption = new FileInputStream("output"); byte[] plainText = new byte[cipher.getOutputSize(bytesToDecryption.length)]; int readNum = -1; int decryptedLen = 0; // execute decryption while ((readNum = fisToDecryption.read(bytesToDecryption)) != -1) { decryptedLen = cipher.update(bytesToDecryption, 0, bytesToDecryption.length, plainText, 0); } decryptedLen = cipher.doFinal(plainText, decryptedLen); fisToDecryption.close();
그리고 다시 Cipher를 Decryption 할 수 있도록 초기화한 후에, Output에 쓰여진 암호문을 Decryption했습니다.