[JAVA] AES256 암호화 예제

[JAVA] AES256 암호화 예제

자바에서 AES256 으로 평문을 암호화하고, 암호화된 값을 평문으로 복호화하는 예제이다.

Secret Key 가 “0123456789abcdefghij0123456789ab”일 때 encryptAES 메서드를 사용하면 평문 “Hello World!” 가 아래와 같이 AES256 암호화 된다.

Secret Key : 0123456789abcdefghij0123456789ab

입력 : Hello World!
결과 : YanblGzXpM13KWrqVqhMYA==

(만약 Base64로 만들 때 Base64.encodeBase64URLSafeString 을 사용하면, 결과는 YanblGzXpM13KWrqVqhMYA)

여기서 Secret Key는 필자가 임의로 정한 것이다. 원하는 임의의 32자리 문자열을 사용하면 된다.

암호화할 때의 Secret Key와 복호화할 때의 Secret Key가 일치하기만 하면 된다.

아래 메서드에서 키의 길이는 16, 24, 32 만 지원한다.

키의 길이가 32 이면 AES-256 이라 부르고, 키의 길이가 24 이면 AES-192, 키의 길이가 16 이면 AES-128 이라 부른다.

참고로 Base64 는 org.apache.commons.codec.binary.Base64 패키지를 import 해야 한다.

import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class AES256Util {

        public static void main(String[] args) {
        AES256Util aesUtil = new AES256Util();
       
        String originText = “Hello World!”;
        System.out.println(“originText : ” + originText);
       
        String encText = aesUtil.encryptAES(“0123456789abcdefghij0123456789ab”, originText, false);
        System.out.println(“encText (encodeBase64) : ” + encText);
       
        encText = aesUtil.encryptAES(“0123456789abcdefghij0123456789ab”, originText, true);
        System.out.println(“encText (encodeBase64URLSafeString) : ” + encText);
       
        String decText = aesUtil.decryptAES(“0123456789abcdefghij0123456789ab”, encText);
        System.out.println(“decText : ” + decText);
    }
    
    
    public String encryptAES(String keyString, String plainText, boolean bUrlSafe) {
        String cipherText = “”;
        if ((keyString == null) || keyString.length() == 0 || (plainText == null) || plainText.length() == 0) {
            return cipherText;
        }
       

        // 키의 길이는 16, 24, 32 만 지원
        if ((keyString.length() != 16) && (keyString.length() != 24) && (keyString.length() != 32)) {
            return cipherText;
        }
       
        try {
            byte[] keyBytes = keyString.getBytes(“UTF-8”);
            byte[] plainTextBytes = plainText.getBytes(“UTF-8”);

            Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);
            int bsize = cipher.getBlockSize();
            IvParameterSpec ivspec = new IvParameterSpec(Arrays.copyOfRange(keyBytes, 0, bsize));
           
            SecretKeySpec secureKey = new SecretKeySpec(keyBytes, “AES”);
            cipher.init(Cipher.ENCRYPT_MODE, secureKey, ivspec);
            byte[] encrypted = cipher.doFinal(plainTextBytes);

            if (bUrlSafe) {
                cipherText = Base64.encodeBase64URLSafeString(encrypted);
            } else {
                cipherText = new String(Base64.encodeBase64(encrypted), “UTF-8”);
            }
           
        } catch (Exception e) {
            cipherText = “”;
            e.printStackTrace();
        }

        return cipherText;
    }
   
   
    public String decryptAES(String keyString, String cipherText) {
        String plainText = “”;
        if ((keyString == null) || keyString.length() == 0 || (cipherText == null) || cipherText.length() == 0) {
            return plainText;
        }
       
        if ((keyString.length() != 16) && (keyString.length() != 24) && (keyString.length() != 32)) {
            return plainText;
        }

        try {
            byte[] keyBytes = keyString.getBytes(“UTF-8”);
            byte[] cipherTextBytes = Base64.decodeBase64(cipherText.getBytes(“UTF-8”));

            Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);
            int bsize = cipher.getBlockSize();
            IvParameterSpec ivspec = new IvParameterSpec(Arrays.copyOfRange(keyBytes, 0, bsize));
           
            SecretKeySpec secureKey = new SecretKeySpec(keyBytes, “AES”);
            cipher.init(Cipher.DECRYPT_MODE, secureKey, ivspec);
            byte[] decrypted = cipher.doFinal(cipherTextBytes);

            plainText = new String(decrypted, “UTF-8”);
           
        } catch (Exception e) {
            plainText = “”;
            e.printStackTrace();
        }

        return plainText;
    }
}

실행결과

originText : Hello World!
encText (encodeBase64) : YanblGzXpM13KWrqVqhMYA==
encText (encodeBase64URLSafeString) : YanblGzXpM13KWrqVqhMYA
decText : Hello World!

문제해결

만약 AES256 암호화 메서드 실행 시 java.security.InvalidKeyException: Illegal key size 오류가 발생하는 경우

이유는 자바가 기본적으로 키 길이를 128bit까지 제한하기 때문이다.
제한을 풀기 위해서는 번들로 제공하는(따로 제공하는) JCE Unlimited Strength 정책 파일을 다운받아서 적용해야 한다.

* JCE Unlimited Strength 정책 파일 다운로드
Java6 : jce_policy-6.zip
http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html

Java7 : UnlimitedJCEPolicyJDK7.zip
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

Java8 : jce_policy-8.zip
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

* JCE Unlimited Strength 정책 파일의 적용
Java8 을 예로 들겠다.

오라클 사이트에서 jce_policy-8.zip 파일을 다운받아서 압축을 풀면 UnlimitedJCEPolicyJDK8 폴더가 나온다.
UnlimitedJCEPolicyJDK8 폴더 안에는 local_policy.jar, US_export_policy.jar, README.txt 이렇게 파일 3개가 있다.

해당 jar 파일 2개를 복사해서 C:\[jdk1.8경로]\jre\lib\security 폴더 안에 붙여넣기(덮어쓰기) 하면 된다.
ex) C:\Java\jdk1.8.0_112\jre\lib\security\local_policy.jar 덮어쓰기

및 C:\Java\jdk1.8.0_112\jre\lib\security\US_export_policy.jar 덮어쓰기
(당연히 파일 덮어쓰기 전 기존 파일은 백업해둘 것)

AES256 검증

아래 사이트에서 AES256 암호화 결과 및 복호화 결과를 검증해볼 수 있다.

검증 사이트 : https://www.devglan.com/online-tools/aes-encryption-decryption 

Select Mode : CBC
Key Size in Bits : 256 bit
IV : 0123456789abcdef
Secret Key : 0123456789abcdefghij0123456789ab
Output Text Format : base64

참고로 IV 값은 Secret Key 값의 절반(16자리)을 입력하면 된다.

참고사이트 : https://hongsii.github.io/2018/04/05/java-aes256-error/