Java에서 (특히 256비트 이상)암호화 처리를 할 시 java.security.InvalidKeyException: Illegal key size 예외가 발생하는 경우가 있는데..
Java는 아래의 버전 이상에서만 unlimited 암호화를 지원한다.
- JDK 6u181 (개인은 6u45 버전까지 다운로드 가능)
- JDK 7u171 (개인은 7u80 버전까지 다운로드 가능)
- JDK 8u161
1004lucifer
주의
- JDK 6u45 리눅스 버전은 unlimited 로 설정이 되어있음.
- 따라서 JDK 6u45 사용 시 리눅스와 윈도우환경의 결과가 다르다.
(삽질 한참하다가 JDK소스 디컴파일해서 까보고 디버깅모드 돌려가며 겨우 알아냄..ㅠ)
위의 Java 버전보다 낮은경우에는 아래와 같이 암호화를 지원한다.
(언급되지 않은 알고리즘은 기본적으로 128비트까지 지원)
<JAVA_HOME>/jre/lib/security/local_policy.jar!/default_local.policy
1004lucifer
grant {permission javax.crypto.CryptoPermission "DES", 64;
permission javax.crypto.CryptoPermission "DESede", *;
permission javax.crypto.CryptoPermission "RC2", 128,
"javax.crypto.spec.RC2ParameterSpec", 128;
permission javax.crypto.CryptoPermission "RC4", 128;
permission javax.crypto.CryptoPermission "RC5", 128,
"javax.crypto.spec.RC5ParameterSpec", *, 12, *;
permission javax.crypto.CryptoPermission "RSA", *;
permission javax.crypto.CryptoPermission *, 128;
};
또한 SSL/TLS 프로토콜 이용시 사용하는 Cipher Suites 에서도
ECDSA, ECDH, ECDHE, ECDH_anon
위의 키교환방식에 대해서는 Java가 limited 상황에서는 사용할 수 없다.
JDK업데이트 없이 해결방법
1. JCE(Java Cryptography Extension)를 다운
- Java 1.2
- Java 1.4
- Java 5
- Java 6, 7, 8
- Java 9 부터는 디폴트 unlimit 정책
2. <JAVA_HOME>/jre/lib/security/ 디렉토리에
3. local_policy.jar, US_export_policy.jar 파일 덮어쓰기
PS.
JDK 8u151 이상 버전에서는 아래와 같이 두개의 디렉토리로 나뉘어져 있다.
- <JAVA_HOME>/jre/lib/security/policy/limited
- <JAVA_HOME>/jre/lib/security/policy/unlimited
JDK 8u151 이상에서는 JCE 다운로드 없이
<JAVA_HOME>/jre/lib/security/java.security 파일에
crypto.policy=unlimited 옵션 지정 시 바로 사용가능하다.
아래의 JDK의 JCE 소스 발췌를 통해 JDK 8u161 부터 unlimited 가 디폴트로 지정된것을 알 수 있다.
JDK 8u151 - jce.jar!/javax.crypto/JceSecurity.class
private static void setupJurisdictionPolicies() throws Exception { String str1 = System.getProperty("java.home"); String str2 = Security.getProperty("crypto.policy"); Path localPath = str2 == null ? null : Paths.get(str2, new String[0]); if ((localPath != null) && ((localPath.getNameCount() != 1) || (localPath.compareTo(localPath.getFileName()) != 0))) { throw new SecurityException("Invalid policy directory name format: " + str2); } if (localPath == null) { localPath = Paths.get(str1, new String[] { "lib", "security" }); } else { localPath = Paths.get(str1, new String[] { "lib", "security", "policy", str2 }); } if (debug != null) { debug.println("crypto policy directory: " + localPath); } File localFile1 = new File(localPath.toFile(), "US_export_policy.jar"); File localFile2 = new File(localPath.toFile(), "local_policy.jar"); if ((str2 == null) && ((!localFile1.exists()) || (!localFile2.exists()))) { localPath = Paths.get(str1, new String[] { "lib", "security", "policy", "limited" }); localFile1 = new File(localPath.toFile(), "US_export_policy.jar"); localFile2 = new File(localPath.toFile(), "local_policy.jar"); } URL localURL = ClassLoader.getSystemResource("javax/crypto/Cipher.class"); if ((localURL == null) || (!localFile1.exists()) || (!localFile2.exists())) { throw new SecurityException("Cannot locate policy or framework files!"); } CryptoPermissions localCryptoPermissions1 = new CryptoPermissions(); CryptoPermissions localCryptoPermissions2 = new CryptoPermissions(); loadPolicies(localFile1, localCryptoPermissions1, localCryptoPermissions2); CryptoPermissions localCryptoPermissions3 = new CryptoPermissions(); CryptoPermissions localCryptoPermissions4 = new CryptoPermissions(); loadPolicies(localFile2, localCryptoPermissions3, localCryptoPermissions4); if ((localCryptoPermissions1.isEmpty()) || (localCryptoPermissions3.isEmpty())) { throw new SecurityException("Missing mandatory jurisdiction policy files"); } defaultPolicy = localCryptoPermissions1.getMinimum(localCryptoPermissions3); if (localCryptoPermissions2.isEmpty()) { exemptPolicy = localCryptoPermissions4.isEmpty() ? null : localCryptoPermissions4; } else { exemptPolicy = localCryptoPermissions2.getMinimum(localCryptoPermissions4); } }
JDK 8u161 - jce.jar!/javax.crypto/JceSecurity.class
private static void setupJurisdictionPolicies() throws Exception { String str1 = System.getProperty("java.home"); String str2 = Security.getProperty("crypto.policy"); Path localPath = str2 == null ? null : Paths.get(str2, new String[0]); if ((localPath != null) && ((localPath.getNameCount() != 1) || (localPath.compareTo(localPath.getFileName()) != 0))) { throw new SecurityException("Invalid policy directory name format: " + str2); } if (localPath == null) { localPath = Paths.get(str1, new String[] { "lib", "security" }); } else { localPath = Paths.get(str1, new String[] { "lib", "security", "policy", str2 }); } if (debug != null) { debug.println("crypto policy directory: " + localPath); } File localFile1 = new File(localPath.toFile(), "US_export_policy.jar"); File localFile2 = new File(localPath.toFile(), "local_policy.jar"); if ((str2 == null) && ((!localFile1.exists()) || (!localFile2.exists()))) { localPath = Paths.get(str1, new String[] { "lib", "security", "policy", "unlimited" }); localFile1 = new File(localPath.toFile(), "US_export_policy.jar"); localFile2 = new File(localPath.toFile(), "local_policy.jar"); } URL localURL = ClassLoader.getSystemResource("javax/crypto/Cipher.class"); if ((localURL == null) || (!localFile1.exists()) || (!localFile2.exists())) { throw new SecurityException("Cannot locate policy or framework files!"); } CryptoPermissions localCryptoPermissions1 = new CryptoPermissions(); CryptoPermissions localCryptoPermissions2 = new CryptoPermissions(); loadPolicies(localFile1, localCryptoPermissions1, localCryptoPermissions2); CryptoPermissions localCryptoPermissions3 = new CryptoPermissions(); CryptoPermissions localCryptoPermissions4 = new CryptoPermissions(); loadPolicies(localFile2, localCryptoPermissions3, localCryptoPermissions4); if ((localCryptoPermissions1.isEmpty()) || (localCryptoPermissions3.isEmpty())) { throw new SecurityException("Missing mandatory jurisdiction policy files"); } defaultPolicy = localCryptoPermissions1.getMinimum(localCryptoPermissions3); if (localCryptoPermissions2.isEmpty()) { exemptPolicy = localCryptoPermissions4.isEmpty() ? null : localCryptoPermissions4; } else { exemptPolicy = localCryptoPermissions2.getMinimum(localCryptoPermissions4); } }
아래링크의 AES256 암호화 자바 샘플을 통해 디버깅모드로 실행하게되면 jce.jar 내부적으로 돌아가는 모습을 볼 수 있다.
- https://dukeom.wordpress.com/2013/01/08/aes256-%EC%95%94%ED%98%B8%ED%99%94-java-%EC%83%98%ED%94%8C/
(JDK 6은 클래스,변수명 난독화가 되어있어 디버깅모드로 한줄한줄 제대로 볼 수 없었음)
참고
- https://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#AppC
- https://hongsii.github.io/2018/04/05/aes256-java-security-invalidkeyexception-illegal-key-size-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95/
- https://dukeom.wordpress.com/2013/01/08/aes256-%EC%95%94%ED%98%B8%ED%99%94-java-%EC%83%98%ED%94%8C/
- https://rsec.kr/?p=455
안녕하세요. 문의사항이 있어 댓글을 남기게 되었습니다. 이 글에서 말하는 암호화 알고리즘이 서버 암호화 알고리즘을 말 하는건지 아니면 jdk 자체 알고리즘인지 궁금합니다. 서버 암호화 알고리즘을 변경(linux) 하려고 하거든요... 영향도 파악이 잘 안되서 혹시나....문의 드립니다.
답글삭제이 글에서 설명하는 암호화 알고리즘은 톰캣<=>브라우저 간에 SSL/TLS 통신할때 사용하는 알고리즘 이예요.
삭제Linux 서버 암호화 알고리즘과는 상관이 없습니다.