[Java] JDK 버전별 AES 256 암호화 적용 방법과 "Illegal key size" 에러 해결책
[Java] JDK 버전별 AES 256 암호화 적용 방법과 "Illegal key size" 에러 해결책
안녕하세요 가야태자 @talkit 입니다. Java관련된 글을 계속 적고 있는데, 해당글들에 이어서 그리고, 제가 이번 프로젝트를 를 진행하면서 겪었던 Java 1.7에서 AES256 암호화 시에 여러분은 오류가 발생하지 않았으면 하는 바램에 이글을 적습니다. 해당 글은 1.6, 1.7에서 AES 256 암호화시 겪게 되는 오류와 해결책, 그리고 JDK 각 버전별 예제를 담고 있습니다.
Java로 보안 관련 기능을 개발하다 보면 가장 흔하게 접하는 암호화 알고리즘이 바로 AES 256입니다. 하지만 분명 로컬(최신 JDK)에서는 잘 돌아가던 코드가 운영 서버(구버전 JDK)에만 올라가면 마법처럼 에러를 뿜어내는 경우가 있습니다.
오늘 그 원인과 JDK 버전별 차이점, 그리고 레거시 환경에서의 해결책까지 완벽하게 정리해 보겠습니다.
🚨 왜 구버전 Java에서는 AES 256이 실패할까?
AES 256 암호화를 적용하고 구버전 자바 환경에서 구동하면 높은 확률로 아래와 같은 치명적인 예외를 만나게 됩니다.
java.security.InvalidKeyException: Illegal key size
이 에러가 발생하는 이유는 과거 미국의 암호화 기술 수출 통제법 때문입니다. 미국은 강력한 암호화 기술이 해외로 유출되는 것을 막기 위해, 기본적으로 Java에 128비트(16바이트)를 초과하는 키를 사용할 수 없도록 제한을 걸어두었습니다. AES 256은 256비트(32바이트) 키를 사용하기 때문에 "키 사이즈가 불법(Illegal)"이라며 거부하는 것입니다.
📅 JDK 버전별 AES 256 지원 차이점
자바가 발전하면서 이 규제와 내부 정책도 많이 변했습니다. 내가 쓰는 JDK 버전이 어디에 속하는지 확인해 보세요.
| JDK 버전 | AES 256 지원 상태 | 특징 |
|---|---|---|
| JDK 1.6 / 1.7 | ❌ 기본 차단 | 기본 128비트 제한. AES 256 사용 시 수동 패치 필수. |
| JDK 1.8 | ⚠️ 버전별 상이 | - 8u151 미만: 수동 패치 필요 - 8u151 이상: 설정 파일로 활성화 가능 - 8u161 이상: 기본 활성화 |
| JDK 11 / 17 / 21 | ⭕ 기본 활성화 | 제약 없음. 성능 최적화 및 최신 암호화 모드(GCM) 권장. |
⚠️ Java 1.6 & 1.7 환경에서의 핵심 주의사항 및 해결책
제가 이번에 Java 1.7 프로젝트를 진행하며 깊게 겪었던 부분입니다. 구버전 환경에서 이 에러를 해결하려면 반드시 아래 사항을 숙지하고 조치해야 합니다.
1. 무제한 강도 정책(JCE) 파일 수동 패치 (올바른 해결책)
Oracle 공식 홈페이지에서 버전에 맞는 'Unlimited Strength Jurisdiction Policy Files'를 직접 다운로드받아 서버의 Java 경로에 덮어씌워야 합니다.
- Oracle 공식 다운로드 링크 검색 키워드:
Java Cryptography Extension (JCE) Unlimited Strength - 파일을 덮어쓸 경로:
${JAVA_HOME}/jre/lib/security/ - 교체할 파일:
local_policy.jar,US_export_policy.jar
💡 Tip: 만약 운영 환경이 Oracle JDK가 아닌 OpenJDK라면, 오픈소스 특성상 이 수출 규제 파일이 적용되지 않아 수동 패치 없이도 바로 AES 256이 작동할 수 있습니다. 서버의 Java 벤더사를 먼저 확인해 보세요!
2. 리플렉션(Reflection) 우회 꼼수 코드는 금물!
인터넷을 찾아보면 JCE 다운로드가 귀찮아서 Java 내부의 isRestricted 필드를 리플렉션 코드로 강제로 false로 바꾸는 팁이 공유되곤 합니다.
하지만 실무 운영 환경에서는 절대 사용하시면 안 됩니다. JVM 내부 보안 구조를 강제로 비트는 행위라, 자바 마이너 업데이트나 환경 변화에 따라 갑자기 애플리케이션이 뻗어버릴 수 있는 시한폭탄 같은 코드입니다. 반드시 위의 1번 방법(JCE 패치)으로 해결하셔야 합니다.
🔒 3. 가장 중요한 보안 규칙: 암호화 키 관리 (필독!)
코드 구현보다 더 중요한 것은 암호화 키를 안전하게 다루는 것입니다. 실무에 적용하실 때 다음 규칙을 반드시 지켜주세요.
- 예제 키 사용 금지: 암호화키는 여러분의 키를 직접 만들어서 쓰셔야 합니다. 제가 아래 예제 코드에서 사용한 키나, 다른 인터넷 블로그 등에서 샘플로 받은 키를 그대로 사용하시면 심각한 보안상 문제가 발생합니다. 예제 키는 누구나 알 수 있기 때문에 암호화의 의미가 완전히 사라집니다.
- 소스코드와 키 분리 보관: 암호화 키는 절대 소스코드 내에 하드코딩하면 안 됩니다. 해당 키는 소스와 완전히 분리되어 OS 환경 변수(Environment Variable), 외부 설정 파일(Properties, YAML), 혹은 AWS Secrets Manager나 HashiCorp Vault 같은 전문 키 관리 시스템(KMS)에 보관되어야 합니다.
- 외부 노출 절대 금지: 키가 담긴 설정 파일을 실수로 GitHub 같은 공용 저장소에 업로드하는 일이 없도록
.gitignore설정을 철저히 하셔야 합니다. 절대로 키를 외부에 공개하시면 안 됩니다.
💻 JDK 버전별 AES 256 구현 예제
1️⃣ JDK 1.6, 1.7, 1.8 (레거시 환경 - AES/CBC/PKCS5Padding)
구버전 환경에서 가장 범용적으로 쓰이는 CBC 모드 예제입니다. (※ 1.6, 1.7은 위에 설명한 JCE 패치가 완료된 상태여야 작동합니다.)
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64; // JDK 1.8 미만은 별도의 Base64 라이브러리(Apache Commons 등) 필요
public class Aes256Legacy {
// ⚠️ 주의: 아래 키와 IV는 예시일 뿐입니다. 실무에서는 소스코드와 분리하여 고유한 키를 사용하세요!
private static final String KEY = "abcdefghijklmnopqrstuvwxyz123456"; // 32바이트 키 (256비트)
private static final String IV = "1234567890123456"; // 16바이트 IV (CBC 모드 필수)
public static String encrypt(String text) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes("UTF-8"));
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
// JDK 1.8 기준 Base64Encoder 사용 (1.7 이하는 외부 라이브러리 활용)
return Base64.getEncoder().encodeToString(encrypted);
}
}
2️⃣ JDK 11, 17, 21 (최신 환경 - AES/GCM/NoPadding)
최신 자바 버전에서는 단순 CBC 모드보다 무결성 검증까지 함께 수행하여 보안성이 훨씬 강력한 GCM 모드 사용을 강력히 권장합니다. 별도의 JCE 설정 없이 즉시 실행됩니다.
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
public class Aes256Modern {
// ⚠️ 주의: 아래 키는 예시일 뿐입니다. 실무에서는 소스코드와 분리하여 고유한 키를 사용하세요!
private static final String KEY = "abcdefghijklmnopqrstuvwxyz123456"; // 32바이트
private static final int GCM_IV_LENGTH = 128; // IV bits
private static final int GCM_TAG_LENGTH = 16; // Tag bytes (128 bits)
public static String encrypt(String text) throws Exception {
// GCM 모드는 매번 새로운 IV 사용을 강력히 권장
byte[] iv = new byte[GCM_IV_LENGTH / 8];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
byte[] ciphertext = cipher.doFinal(text.getBytes("UTF-8"));
// IV와 암호문을 함께 저장/전송해야 복호화가 가능합니다.
byte[] cipherWithIv = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, cipherWithIv, 0, iv.length);
System.arraycopy(ciphertext, 0, cipherWithIv, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(cipherWithIv);
}
}
📝 마치며
오래된 시스템을 유지보수하거나 레거시 환경으로 마이그레이션할 때, 자바의 버전별 보안 정책 차이를 모르면 뜬금없는 에러에 며칠을 허비하게 됩니다.
특히 Java 1.6이나 1.7 환경에서 AES 256을 구현하실 때는 서버의 JCE 정책 파일 확인 단계를 거치셔서, 저처럼 삽질(?)하지 않고 한 번에 성공하시길 바라는 마음입니다. 아울러 아무리 코드가 완벽해도 암호화 키가 노출되면 무용지물이 되니 키 관리도 꼭 신경 써주세요!
궁금한 점이 있으시다면 언제든 댓글로 남겨주세요. 감사합니다!

헐…
0.00 SBD,
8.04 STEEM,
8.04 SP
[booming-kr-auto]
보팅 완료했습니다 🙌
왜 헐 인가요?