응용 프로그램을 배포 할 때“무제한 강도”JCE 정책 파일 설치를 피하는 방법은 무엇입니까?


169

Java에서 즉시 지원하지 않는 256 비트 AES 암호화를 사용하는 앱이 있습니다. 나는 이것이 올바르게 작동하도록 알고 보안 폴더에 JCE 무제한 강도 항아리를 설치합니다. 이것은 개발자 인 것이 좋습니다. 설치할 수 있습니다.

내 질문은이 앱이 배포되기 때문에 최종 사용자는 이러한 정책 파일을 설치하지 않았을 가능성이 높습니다. 최종 사용자가이를 다운로드하여 앱 기능을 만드는 것은 매력적인 해결책이 아닙니다.

최종 사용자 컴퓨터에서 파일을 덮어 쓰지 않고 앱을 실행하는 방법이 있습니까? 정책 파일을 설치하지 않고 처리 할 수있는 타사 소프트웨어? 또는 JAR 내에서 이러한 정책 파일을 참조하는 방법은 무엇입니까?




11
Sun / Oracle의 의도는 클라이언트가 안전하지 않은 암호를 사용하여 NSA가 연결을 스누핑 할 수 있다고 생각합니다. 농담이나 편집증이 아니지만 암호화는 무기로 취급되며 암호화 공유에 대한 수출 금지 가 있습니다 .
Sled

답변:


175

이 문제에 대해 일반적으로 인용되는 몇 가지 해결책이 있습니다. 불행히도 이들 중 어느 것도 완전히 만족스럽지 않습니다.

  • 무제한 강도 정책 파일을 설치하십시오 . 이것이 아마도 개발 워크 스테이션에 적합한 솔루션 일 수 있지만, 기술이 아닌 사용자가 모든 컴퓨터에 파일을 설치하게하는 것은 큰 번거 로움 (로드 블록이 아닌 경우)이됩니다. 프로그램과 함께 파일을 배포 할 방법없습니다 . JRE 디렉토리에 설치해야합니다 (권한으로 인해 읽기 전용 일 수도 있음).
  • JCE API를 건너 뛰고 Bouncy Castle 과 같은 다른 암호화 라이브러리를 사용하십시오 . 이 방법에는 추가 1MB 라이브러리가 필요하며 이는 응용 프로그램에 따라 상당한 부담이 될 수 있습니다. 또한 표준 라이브러리에 포함 된 기능을 복제하는 것은 바보 같은 느낌입니다. 분명히 API는 일반적인 JCE 인터페이스와 완전히 다릅니다. (BC는 JCE 공급자를 구현하지만 구현에 전달 하기 전에 주요 강도 제한이 적용 되기 때문에 도움이되지 않습니다 .)이 솔루션은 256 비트 TLS (SSL) 암호화 제품군을 사용할 수 없습니다 표준 TLS 라이브러리는 JCE를 내부적으로 호출하여 제한 사항을 판별합니다.

그러나 반성이 있습니다. 반사를 사용하여 할 수없는 것이 있습니까?

private static void removeCryptographyRestrictions() {
    if (!isRestrictedCryptography()) {
        logger.fine("Cryptography restrictions removal not needed");
        return;
    }
    try {
        /*
         * Do the following, but with reflection to bypass access checks:
         *
         * JceSecurity.isRestricted = false;
         * JceSecurity.defaultPolicy.perms.clear();
         * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
         */
        final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
        final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
        final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

        final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
        isRestrictedField.setAccessible(true);
        final Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
        isRestrictedField.set(null, false);

        final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
        defaultPolicyField.setAccessible(true);
        final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

        final Field perms = cryptoPermissions.getDeclaredField("perms");
        perms.setAccessible(true);
        ((Map<?, ?>) perms.get(defaultPolicy)).clear();

        final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        defaultPolicy.add((Permission) instance.get(null));

        logger.fine("Successfully removed cryptography restrictions");
    } catch (final Exception e) {
        logger.log(Level.WARNING, "Failed to remove cryptography restrictions", e);
    }
}

private static boolean isRestrictedCryptography() {
    // This matches Oracle Java 7 and 8, but not Java 9 or OpenJDK.
    final String name = System.getProperty("java.runtime.name");
    final String ver = System.getProperty("java.version");
    return name != null && name.equals("Java(TM) SE Runtime Environment")
            && ver != null && (ver.startsWith("1.7") || ver.startsWith("1.8"));
}

removeCryptographyRestrictions()암호화 작업을 수행하기 전에 정적 이니셜 라이저 등에서 호출하면 됩니다.

JceSecurity.isRestricted = false부분은 직접 256 비트 암호를 사용하는 데 필요한 모든 것입니다; 그러나 다른 두 가지 작업이 없으면 Cipher.getMaxAllowedKeyLength()계속 128을보고하며 256 비트 TLS 암호 제품군은 작동하지 않습니다.

이 코드는 Oracle Java 7 및 8에서 작동하며 필요하지 않은 Java 9 및 OpenJDK의 프로세스를 자동으로 건너 뜁니다. 결국 추악한 해킹이기 때문에 다른 공급 업체의 VM에서는 작동하지 않을 수 있습니다.

개인 JCE 클래스가 난독 처리되어 있기 때문에 Oracle Java 6에서도 작동하지 않습니다. 난독 화는 버전마다 바뀌지 않으므로 Java 6을 기술적으로 지원하는 것은 여전히 ​​가능합니다.


23
리플렉션 솔루션은 Java 라이센스 계약을 위반할 수 있습니다 . "F. Java 기술 제한 사항."java ", 'javax'로 식별되는 클래스, 인터페이스 또는 하위 패키지의 동작을 변경할 수 없습니다. , 'sun', 'oracle'또는 이와 유사한 규칙 ... "
M. Dudley

14
@ M.Dudley 될 수 있습니다. 이 코드가 포함 된 제품을 배송하기 전에 변호사에게 문의하십시오.
ntoskrnl

3
@peabody 프로그램에 100MB JRE를 포함시키는 것은 확실히 옵션입니다. 그러나 그렇지 않은 경우 파일 권한과 같은 여러 가지 이유로 프로그램에 정책 파일을 포함하더라도 사용자는 여전히 정책 파일을 수동으로 설치해야합니다. 내 경험상, 많은 사용자들이 그렇게 할 수 없습니다.
ntoskrnl

8
리플렉션 솔루션이 1.8.0_112에서 작동을 멈춘 것 같습니다. 1.8.0_111에서 작동하지만 112에서는 작동하지 않습니다.
John L

3
@ JohnL 나는 이것을 응용 프로그램에서 사용합니다. final8u111 에서 필드에 문제가 발생한 후이 답변에 따라 최종 필드를 변경할 수 있도록 수정했습니다 . 결과는로 선언하지 않았다는 점을 제외하고 ntoskrnl의 새 버전과 거의 modifiersField같습니다 final. 내 사용자 중 한 명이 8u112에서도 작동한다고보고했습니다.
Arjan

87

이제는 더 이상 Java 9 또는 Java 6, 7, 8의 최신 릴리스에 필요하지 않습니다 . :)

JDK-8170157 , 무제한 암호 정책은 기본적으로 활성화되어 있습니다.

JIRA 문제의 특정 버전 :

  • Java 9 (10, 11 등) : 공식 릴리스!
  • Java 8u161 이상 ( 현재 사용 가능 )
  • Java 7u171 이상 ( 'My Oracle Support'를 통해서만 사용 가능)
  • Java 6u181 이상 ( 'My Oracle Support'를 통해서만 사용 가능)

이상한 이유로 Java 9에서 이전 동작이 필요한 경우 다음을 사용하여 설정할 수 있습니다.

Security.setProperty("crypto.policy", "limited");

4
실제로이 정책은 기본값이므로 Java 9에서는 별도의 조치가 필요하지 않습니다!
ntoskrnl

2018/01/14 (최신 Oracle JDK는 8u151 / 152) 기준으로 Java 8에서는 기본적으로 여전히 활성화되어 있지 않습니다.이 답변이 처음 작성된 후 1 년이 넘었습니다 ... 그러나 java.com/en/jre 에 따르면 -jdk-cryptoroadmap.html 2018/01/16 GA에 예정되어 있습니다.
Alex

제 경우, 그리고이 사이트에서 A 마크를 받으려면 : ssllabs.com/ssltest ... 다음과 같이 설정해야합니다. Security.setProperty ( "crypto.policy", "unlimited"); 다음 ... 256 기반 알고리즘을 내 applications.properties의 세트 server.ssl.ciphers이 문서에 표시 -> weakdh.org/sysadmin.html
Artanis 제라툴

OpenJDK 8 설치와도 관련이 있습니다. 참조 : stackoverlow-Article : JCE 정책이 openjdk 8과 함께 제공됩니까?
leole

22

해결책은 다음과 같습니다. http://middlesphere-1.blogspot.ru/2014/06/this-code-allows-to-break-limit-if.html

//this code allows to break limit if client jdk/jre has no unlimited policy files for JCE.
//it should be run once. So this static section is always execute during the class loading process.
//this code is useful when working with Bouncycastle library.
static {
    try {
        Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
        field.setAccessible(true);
        field.set(null, java.lang.Boolean.FALSE);
    } catch (Exception ex) {
    }
}

이것은 "defaultPolicy"부분을 제외하고는 내 것과 같은 솔루션입니다. 블로그 게시물은 답변 후 날짜가 표시됩니다.
ntoskrnl

1
그러나 이것이 옳은 일입니까? 이 코드는 실시간으로 애플리케이션 보안에 도전 할 수 있습니까? 영향을 이해하도록 도와주세요.
요리

1
이것을 실행 한 후에이 오류가 발생합니다.java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
Andy

3
Java 8 빌드 111 부터는isRestricted 필드가 최종 단계가되기 때문에이 솔루션으로는 충분하지 않습니다 ( bugs.openjdk.java.net/browse/JDK-8149417 ). @ntoskrnl의 답변은 "최종"수정자를 포함하는 것을 처리합니다. Java 라이센스 계약에 대한 @ M.Dudley의 의견도 여전히 적용됩니다.
MPelletier 2009 년


13

JDK 8u102로, 반사에 더 이상 작동하지 않습니다 의존하지 솔루션을 게시 : 필드 이러한 솔루션 세트는 지금이다 final( https://bugs.openjdk.java.net/browse/JDK-8149417 ).

(a) Bouncy Castle을 사용하거나 (b) JCE 정책 파일을 설치 한 것으로 보입니다.


7
당신은 항상 더 많은 반사를 사용할 수 있습니다 stackoverflow.com/questions/3301635/…
Universal Electricity

예. @ M.Dudley의 솔루션은 isRestricted"최종"수정자를 추가 할 수 있기 때문에 여전히 현장에서 작동합니다 .
MPelletier 2009 년

1
새로운 릴리스 JDK 8u151에는 "암호화 정책을 제어하는 ​​새로운 보안 속성"이 있습니다. 결론 : "lib \ security \ java.security"의 "# crypto.policy = unlimited"줄에서 "#"을 제거하십시오. oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
hemisphire

8

대체 암호화 라이브러리에 대해서는 Bouncy Castle을 살펴보십시오 . AES와 많은 추가 기능이 있습니다. 자유주의적인 오픈 소스 라이브러리입니다. 그래도 작동하려면 가볍고 독점적 인 Bouncy Castle API를 사용해야합니다.


19
그들은 훌륭한 암호화 공급자이지만 큰 키로 작업하려면 무제한 강도의 JCE 파일이 필요합니다.
John Meagher

16
Bouncy Castle API를 직접 사용하는 경우 무제한 강도 파일이 필요하지 않습니다.
laz

4

당신은 방법을 사용할 수 있습니다

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

사용 가능한 키 길이를 테스트하려면이 키 길이를 사용하여 사용자에게 진행 상황을 알려주십시오. 예를 들어, 정책 파일이 설치되지 않아서 응용 프로그램이 128 비트 키로 다시 떨어 졌다는 내용이 있습니다. 보안에 민감한 사용자는 정책 파일을 설치하고 다른 사용자는 더 약한 키를 계속 사용합니다.


3

우리 애플리케이션에는 클라이언트 서버 아키텍처가 있었고 서버 수준의 데이터 만 해독 / 암호화 할 수있었습니다. 따라서 JCE 파일 만 필요합니다.

JNLP를 통해 클라이언트 시스템의 보안 jar를 업데이트해야하는 또 다른 문제점이있었습니다. JNLP를 통해 ${java.home}/lib/security/처음 실행시 라이브러리 와 JVM을 겹쳐 씁니다 .

그것은 효과가 있었다.


2

다음은 ntoskrnl 답변 의 업데이트 버전입니다 . 주석에 언급 된 Arjan 과 같은 최종 수정자를 제거하는 기능이 추가로 포함되어 있습니다 .

이 버전은 JRE 8u111 이상에서 작동합니다.

private static void removeCryptographyRestrictions() {
    if (!isRestrictedCryptography()) {
        return;
    }
    try {
        /*
         * Do the following, but with reflection to bypass access checks:
         * 
         * JceSecurity.isRestricted = false; JceSecurity.defaultPolicy.perms.clear();
         * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
         */
        final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
        final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
        final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

        Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
        isRestrictedField.setAccessible(true);
        setFinalStatic(isRestrictedField, true);
        isRestrictedField.set(null, false);

        final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
        defaultPolicyField.setAccessible(true);
        final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

        final Field perms = cryptoPermissions.getDeclaredField("perms");
        perms.setAccessible(true);
        ((Map<?, ?>) perms.get(defaultPolicy)).clear();

        final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        defaultPolicy.add((Permission) instance.get(null));
    }
    catch (final Exception e) {
        e.printStackTrace();
    }
}

static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }

private static boolean isRestrictedCryptography() {
    // This simply matches the Oracle JRE, but not OpenJDK.
    return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));
}

잘 작동하지만 라인 ((Map<?, ?>) perms.get(defaultPolicy)).clear();에 컴파일러 오류가 발생합니다. 주석 처리는 기능에 영향을 미치지 않는 것 같습니다. 이 줄이 필요합니까?
Andreas Unterweger

2

다음은 실제 , slf4j 로깅 및 애플리케이션 부트 스트랩에서 싱글 톤 초기화를 지원 하는 isRestrictedCryptography점검 기능이있는 @ntoskrnl 코드의 수정 된 버전입니다 .Cipher.getMaxAllowedKeyLength

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

@cranphin의 대답이 예측하는 것처럼 Java 8u162에서 기본적으로 무제한 정책을 사용할 수있게되면이 코드는 리플렉션으로 맹 글링을 올바르게 중지합니다.


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Map;

// /programming/1179672/how-to-avoid-installing-unlimited-strength-jce-policy-files-when-deploying-an
public class UnlimitedKeyStrengthJurisdictionPolicy {

    private static final Logger log = LoggerFactory.getLogger(UnlimitedKeyStrengthJurisdictionPolicy.class);

    private static boolean isRestrictedCryptography() throws NoSuchAlgorithmException {
        return Cipher.getMaxAllowedKeyLength("AES/ECB/NoPadding") <= 128;
    }

    private static void removeCryptographyRestrictions() {
        try {
            if (!isRestrictedCryptography()) {
                log.debug("Cryptography restrictions removal not needed");
                return;
            }
            /*
             * Do the following, but with reflection to bypass access checks:
             *
             * JceSecurity.isRestricted = false;
             * JceSecurity.defaultPolicy.perms.clear();
             * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
             */
            Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
            Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
            Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

            Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
            isRestrictedField.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
            isRestrictedField.set(null, false);

            Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
            defaultPolicyField.setAccessible(true);
            PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

            Field perms = cryptoPermissions.getDeclaredField("perms");
            perms.setAccessible(true);
            ((Map<?, ?>) perms.get(defaultPolicy)).clear();

            Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
            instance.setAccessible(true);
            defaultPolicy.add((Permission) instance.get(null));

            log.info("Successfully removed cryptography restrictions");
        } catch (Exception e) {
            log.warn("Failed to remove cryptography restrictions", e);
        }
    }

    static {
        removeCryptographyRestrictions();
    }

    public static void ensure() {
        // just force loading of this class
    }
}

-1

프로그램을 설치하는 동안 사용자에게 프롬프트하고 DOS 배치 스크립트 또는 Bash 쉘 스크립트를 다운로드하여 JCE를 적절한 시스템 위치에 복사하십시오.

서버 웹 서비스를 위해이 작업을 수행해야했으며 공식 설치 프로그램 대신 사용자가 앱을 실행하기 전에 앱을 설정하는 스크립트를 제공했습니다. 설정 스크립트를 실행할 때까지 앱을 실행할 수 없도록 만들 수 있습니다. 또한 JCE가 없다는 앱에 불만을 제기 한 다음 앱을 다운로드하고 다시 시작하도록 요청할 수 있습니까?


7
"내 응용 프로그램을 실행할 수 있도록 파일을 덮어 쓰지 않고 최종 사용자의 컴퓨터에"
에릭슨

초기 답변이 잘못되었으므로 답변을 완전히 편집했습니다.
djangofan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.