Làm cách nào để tránh cài đặt các tập tin chính sách của J Unlimited Sức mạnh không giới hạn của JCE khi triển khai một ứng dụng?


169

Tôi có một ứng dụng sử dụng mã hóa AES 256 bit không được Java hỗ trợ. Tôi biết để có được chức năng này một cách chính xác, tôi cài đặt các lọ cường độ không giới hạn JCE trong thư mục bảo mật. Điều này tốt cho tôi là nhà phát triển, tôi có thể cài đặt chúng.

Câu hỏi của tôi là vì ứng dụng này sẽ được phân phối, người dùng cuối rất có thể sẽ không cài đặt các tệp chính sách này. Yêu cầu người dùng cuối tải xuống những thứ này chỉ để làm cho chức năng ứng dụng không phải là một giải pháp hấp dẫn.

Có cách nào để làm cho ứng dụng của tôi chạy mà không ghi đè các tệp trên máy người dùng cuối không? Một phần mềm bên thứ ba có thể xử lý nó mà không cần cài đặt các tệp chính sách? Hoặc một cách để chỉ tham chiếu các tệp chính sách này từ trong JAR?




11
Tôi nghi ngờ ý định của Sun / Oracle là khách hàng sẽ sử dụng một mật mã kém an toàn hơn để NSA có thể rình mò kết nối. Tôi không nói đùa hay bị hoang tưởng, nhưng mật mã được coi là vũ khí và có lệnh cấm xuất khẩu để chia sẻ mã hóa .
Sled

Câu trả lời:


175

Có một vài giải pháp thường được trích dẫn cho vấn đề này. Thật không may, cả hai điều này đều không thỏa đáng:

  • Cài đặt các tập tin chính sách sức mạnh không giới hạn . Mặc dù đây có lẽ là giải pháp phù hợp cho máy trạm phát triển của bạn, nhưng nó nhanh chóng trở thành rắc rối lớn (nếu không phải là rào cản) khi người dùng không có kỹ thuật cài đặt các tệp trên mọi máy tính. Không có cách nào để phân phối các tệp với chương trình của bạn; chúng phải được cài đặt trong thư mục JRE (thậm chí có thể chỉ đọc do quyền).
  • Bỏ qua API JCE và sử dụng một thư viện mật mã khác như Bouncy Castle . Cách tiếp cận này đòi hỏi một thư viện thêm 1 MB, có thể là một gánh nặng đáng kể tùy thuộc vào ứng dụng. Nó cũng cảm thấy ngớ ngẩn khi sao chép chức năng có trong các thư viện tiêu chuẩn. Rõ ràng, API cũng hoàn toàn khác với giao diện JCE thông thường. (BC không triển khai nhà cung cấp JCE, nhưng điều đó không có ích vì các giới hạn cường độ chính được áp dụng trước khi bàn giao cho việc triển khai.) Giải pháp này cũng sẽ không cho phép bạn sử dụng bộ mật mã TLS (SSL) 256 bit, bởi vì các thư viện TLS tiêu chuẩn gọi JCE trong nội bộ để xác định bất kỳ hạn chế nào.

Nhưng sau đó có sự phản ánh. Có điều gì bạn không thể làm bằng cách sử dụng sự phản chiếu?

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"));
}

Chỉ cần gọi removeCryptographyRestrictions()từ trình khởi tạo tĩnh hoặc như vậy trước khi thực hiện bất kỳ hoạt động mã hóa nào.

Phần JceSecurity.isRestricted = falsenày là tất cả những gì cần thiết để sử dụng mật mã 256 bit trực tiếp; tuy nhiên, không có hai hoạt động khác, Cipher.getMaxAllowedKeyLength()vẫn sẽ tiếp tục báo cáo 128 và bộ mật mã TLS 256 bit sẽ không hoạt động.

Mã này hoạt động trên Oracle Java 7 và 8 và tự động bỏ qua quy trình trên Java 9 và OpenJDK khi không cần thiết. Rốt cuộc là một hack xấu xí, có khả năng nó không hoạt động trên máy ảo của các nhà cung cấp khác.

Nó cũng không hoạt động trên Oracle Java 6, vì các lớp JCE riêng bị che khuất ở đó. Việc obfuscation không thay đổi từ phiên bản này sang phiên bản khác, do đó về mặt kỹ thuật vẫn có thể hỗ trợ Java 6.


23
Giải pháp phản ánh có thể vi phạm Thỏa thuận cấp phép Java : "F. GIỚI HẠN CÔNG NGHỆ JAVA. Bạn không được ... thay đổi hành vi của ... các lớp, giao diện hoặc gói con theo bất kỳ cách nào được xác định là 'java', 'javax' , 'Mặt trời', 'tiên tri' hoặc quy ước tương tự ... "
M. Dudley

14
@ M.Dudley Có thể. Kiểm tra với luật sư trước khi vận chuyển một sản phẩm có chứa đoạn mã này nếu nó liên quan đến bạn.
ntoskrnl

3
@peabody Bao gồm JRE 100 MB với chương trình của bạn chắc chắn là một tùy chọn trong một số trường hợp. Nhưng nếu không, người dùng vẫn sẽ phải cài đặt các tệp chính sách theo cách thủ công, ngay cả khi bạn đưa chúng vào chương trình của mình (vì nhiều lý do như quyền truy cập tệp). Theo kinh nghiệm của tôi, nhiều người dùng không có khả năng đó.
ntoskrnl

8
Có vẻ như giải pháp phản chiếu chỉ dừng hoạt động trong 1.8.0_112. Nó hoạt động trong 1.8.0_111, nhưng không phải 112.
John L

3
@JohnL Tôi sử dụng điều này trong một ứng dụng. Sau khi gặp rắc rối với finaltrường trong 8u111, tôi đã sửa đổi nó để nó có thể thay đổi trường cuối cùng, theo câu trả lời này . Kết quả là giống như phiên bản mới của ntoskrnl, ngoại trừ việc tôi không tuyên bố modifiersFieldfinal. Một trong những người dùng của tôi báo cáo rằng nó cũng hoạt động trong 8u112.
Arjan

87

Điều này bây giờ không còn cần thiết cho Java 9 , cũng như cho bất kỳ bản phát hành gần đây nào của Java 6, 7 hoặc 8. Cuối cùng! :)

Theo JDK-8170157 , chính sách mật mã không giới hạn hiện được bật theo mặc định.

Các phiên bản cụ thể từ vấn đề JIRA:

  • Java 9 (10, 11, v.v.): Bất kỳ bản phát hành chính thức nào!
  • Java 8u161 hay muộn (Chỉ có tại )
  • Java 7u171 trở lên (Chỉ khả dụng thông qua 'Hỗ trợ Oracle của tôi')
  • Java 6u181 trở lên (Chỉ khả dụng thông qua 'Hỗ trợ Oracle của tôi')

Lưu ý rằng nếu vì một số lý do kỳ lạ, hành vi cũ là cần thiết trong Java 9, thì nó có thể được đặt bằng cách sử dụng:

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

4
Trong thực tế, chính sách này là mặc định, vì vậy không có hành động nào cần thiết trong Java 9!
ntoskrnl

Kể từ 2018/01/14 (Oracle JDK mới nhất là 8u151 / 152), điều này vẫn không được bật theo mặc định trên Java 8, hơn một năm sau khi câu trả lời này ban đầu được viết ... Tuy nhiên, theo java.com/en/jre -jdk-cryptoroadmap.html điều này được dự định là GA vào 2018/01/16
Alex

Trong trường hợp của tôi và để tôi nhận được Dấu A trong trang web này: ssllabs.com/ssltest ... Tôi phải đặt nó theo cách này: Security.setProperty ("crypto.policy", "không giới hạn"); sau đó ... bộ server.ssl.ciphers trong applications.properties tôi với 256 dựa trên các thuật toán đã nêu trong bài viết này -> weakdh.org/sysadmin.html
Artanis Zeratul

Cũng liên quan đến OpenJDK 8-Installer. Xem: stackoverlow-Article: Chính sách JCE có được đóng gói với openjdk 8 không?
leole

22

Đây là giải pháp: 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) {
    }
}

Đây là giải pháp tương tự như của tôi, ngoại trừ không có phần "defaultPolicy". Bài viết trên blog là ngày sau câu trả lời của tôi.
ntoskrnl

1
Nhưng điều này có đúng không? Trong thời gian thực mã này có thể thách thức bảo mật ứng dụng? Tôi không chắc chắn xin vui lòng giúp tôi hiểu tác động của nó.
Món ăn

1
Tôi gặp lỗi này sau khi chạy nó:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
Andy

3
Kể từ Java 8 build 111, giải pháp này sẽ không đủ, vì isRestrictedtrường đã trở thành cuối cùng ( bug.openjdk.java.net/browse/JDK-8149417 ). Câu trả lời của @ ntoskrnl quan tâm đến bất kỳ sự bổ sung nào có thể có của công cụ sửa đổi "cuối cùng". Nhận xét của @ M.Dudley về Thỏa thuận cấp phép Java vẫn được áp dụng.
MPelletier


13

Kể từ JDK 8u102, các giải pháp được đăng dựa trên sự phản chiếu sẽ không còn hoạt động nữa: trường mà các giải pháp này đặt bây giờ final( https://bugs.openjdk.java.net/browse/JDK-8149417 ).

Có vẻ như nó trở lại (a) bằng cách sử dụng Bouncy Castle hoặc (b) cài đặt các tệp chính sách JCE.


7
Bạn luôn có thể sử dụng nhiều hơn stackoverflow.com/questions/3301635/ Kiếm
Điện phổ quát

Có, giải pháp của @ M.Dudley vẫn sẽ hoạt động cho isRestrictedlĩnh vực này, bởi vì nó quan tâm đến việc bổ sung có thể có một công cụ sửa đổi "cuối cùng".
MPelletier

1
Bản phát hành mới JDK 8u151 có "Thuộc tính bảo mật mới để kiểm soát chính sách tiền điện tử". Bottom line: loại bỏ các "#" từ dòng "# crypto.policy = không giới hạn" trong "lib \ security \ java.security": oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
hemisphire

8

Đối với một thư viện mật mã thay thế, hãy xem Bouncy Castle . Nó có AES và rất nhiều chức năng bổ sung. Đó là một thư viện mã nguồn mở tự do. Bạn sẽ phải sử dụng API Bouncy Castle nhẹ, độc quyền để làm việc này.


19
Họ là một nhà cung cấp tiền điện tử tuyệt vời, nhưng vẫn yêu cầu tệp JCE có sức mạnh không giới hạn để hoạt động với các khóa lớn.
John Meagher

16
Nếu bạn sử dụng API Bouncy Castle trực tiếp, bạn không cần các tệp cường độ không giới hạn.
laz

4

Bạn có thể sử dụng phương pháp

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

để kiểm tra độ dài khóa khả dụng, sử dụng và thông báo cho người dùng về những gì đang diễn ra. Ví dụ, một cái gì đó cho biết ứng dụng của bạn đang rơi trở lại khóa 128 bit do các tệp chính sách không được cài đặt, chẳng hạn. Người dùng có ý thức bảo mật sẽ cài đặt các tệp chính sách, những người khác sẽ tiếp tục sử dụng các khóa yếu hơn.


3

Đối với ứng dụng của chúng tôi, chúng tôi có kiến ​​trúc máy chủ máy khách và chúng tôi chỉ cho phép giải mã / mã hóa dữ liệu ở cấp máy chủ. Do đó các tập tin JCE chỉ cần thiết ở đó.

Chúng tôi đã có một vấn đề khác là chúng tôi cần cập nhật một tệp bảo mật trên các máy khách, thông qua JNLP, nó ghi đè lên các thư viện trong${java.home}/lib/security/ và JVM trong lần chạy đầu tiên.

Điều đó làm cho nó hoạt động.


2

Đây là phiên bản cập nhật của câu trả lời ntoskrnl . Nó cũng chứa một chức năng để loại bỏ công cụ sửa đổi cuối cùng như Arjan đã đề cập trong các bình luận.

Phiên bản này hoạt động với JRE 8u111 hoặc mới hơn.

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"));
}

Nó hoạt động độc đáo, nhưng dòng ((Map<?, ?>) perms.get(defaultPolicy)).clear();mang lại một lỗi biên dịch. Bình luận dường như không ảnh hưởng đến chức năng của nó. Là dòng này cần thiết?
Andreas Unterweger

2

Đây là phiên bản sửa đổi của mã @ ntoskrnl có tính năng isRestrictedCryptographykiểm tra bằng cách ghi nhật ký slf4j thực tếCipher.getMaxAllowedKeyLength và hỗ trợ khởi tạo singleton từ ứng dụng bootstrap như thế này:

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

Mã này sẽ dừng chính xác việc xáo trộn với sự phản chiếu khi chính sách không giới hạn có sẵn theo mặc định trong Java 8u162 như dự đoán câu trả lời của @ cranphin.


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

Trong quá trình cài đặt chương trình của bạn, chỉ cần nhắc người dùng và có tập lệnh DOS Batch hoặc tập lệnh shell Bash tải xuống và sao chép JCE vào vị trí hệ thống thích hợp.

Tôi đã từng phải làm điều này cho một dịch vụ web máy chủ và thay vì một trình cài đặt chính thức, tôi chỉ cung cấp các tập lệnh để thiết lập ứng dụng trước khi người dùng có thể chạy nó. Bạn có thể làm cho ứng dụng không chạy được cho đến khi chúng chạy tập lệnh thiết lập. Bạn cũng có thể khiến ứng dụng phàn nàn rằng JCE bị thiếu và sau đó yêu cầu tải xuống và khởi động lại ứng dụng?


7
"làm cho ứng dụng của tôi chạy mà không ghi đè các tệp trên máy người dùng cuối"
erickson

Tôi đã chỉnh sửa hoàn toàn câu trả lời của mình vì câu trả lời ban đầu của tôi là sai.
djangofan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.