2009-07-24 21 views
145

Tôi có một ứng dụng sử dụng mã hóa AES 256 bit không được Java hỗ trợ trong hộp. Tôi biết để có được điều này để hoạt động chính xác tôi cài đặt JCE sức mạnh không giới hạn lọ trong thư mục bảo mật. Điều này là tốt cho tôi là nhà phát triển, tôi có thể cài đặt chúng.Làm thế nào để tránh cài đặt các tệp chính sách JCE "Sức mạnh không giới hạn" khi triển khai một ứng dụ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 nhiều khả năng sẽ không cài đặt các tệp chính sách này. Có 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 để ứng dụng của tôi chạy mà không ghi đè lên tệp trên máy người dùng cuối không? Một phần mềm của 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 khảo các tệp chính sách này từ bên trong một JAR?

+1

Hãy xem ở đây: http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#ExemptApps –

+0

Tham khảo [Làm thế nào để cài đặt Các tập tin chính sách về quyền lực pháp lý không hạn chế của Java] (http://opensourceforgeeks.blogspot.in/2014/09/how-to-install-java-cryptography.html) –

+10

Tôi nghi ngờ ý định của Sun/Oracle là khách hàng sẽ sử dụng mật mã ít an toàn hơn để NSA có thể truy cập vào kết nối. Tôi không nói đùa hay hoang tưởng, nhưng mật mã được coi là vũ khí và có [lệnh cấm xuất khẩu trên mã hóa chia sẻ] (https://en.wikipedia.org/wiki/Export_of_cryptography_from_the_United_States). – ArtB

Trả lời

0

Trong khi 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ải xuống tập lệnh shell Bash 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 dịch vụ web máy chủ và thay vì trình cài đặt chính thức, tôi vừa cung cấp tập lệnh để thiết lập ứng dụng trước khi người dùng có thể chạy ứng dụng đó. Bạn có thể làm cho ứng dụng không thể chạy cho đến khi 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 đè lên tệp * trên máy người dùng cuối" – erickson

+0

Tôi đã chỉnh sửa câu trả lời hoàn chỉnh vì câu trả lời ban đầu của tôi là sai. – djangofan

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ủ ứng dụng 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 JCE chỉ cần ở đó.

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

Điều đó khiến nó hoạt động.

8

Đối với thư viện mã hóa 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 độc ​​quyền, nhẹ để làm việc này.

+19

Họ là một nhà cung cấp mật mã tuyệt vời, nhưng vẫn yêu cầu tệp JCE sức mạnh không giới hạn để làm việc với các phím lớn. –

+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 sức mạnh không giới hạn. – laz

155

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

  • Cài đặt unlimited strength policy files. 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, nó nhanh chóng trở thành một rắc rối lớn (nếu không phải là rào cản) để 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. Có không có cách nào để phân phối 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 các điều khoản).
  • Bỏ qua API JCE và sử dụng thư viện mã hóa khác như Bouncy Castle. Cách tiếp cận này yêu cầu thêm một thư viện 1MB, 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 để nhân đôi chức năng bao gồm trong các thư viện 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 thực hiện một nhà cung cấp JCE, nhưng điều đó không hiệu quả vì các hạn chế về sức mạnh chính được áp dụng trước khi giao hàng để thực hiện.) Giải pháp này cũng sẽ không cho phép bạn sử dụng bộ mã hóa TLS (SSL) 256 bit, vì các thư viện TLS tiêu chuẩn gọi JCE nội bộ để xác định mọi hạn chế.

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

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ừ bộ khởi tạo tĩnh hoặc trước khi thực hiện bất kỳ hoạt động mã hóa nào.

Phần JceSecurity.isRestricted = false là tất cả những gì cần thiết để sử dụng thuật toán mã hóa 256 bit trực tiếp; tuy nhiên, nếu không có hai hoạt động khác, Cipher.getMaxAllowedKeyLength() sẽ vẫn tiếp tục báo cáo 128 và các bộ mã hóa 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 quá trình trên Java 9 và OpenJDK ở nơi không cần thiết. Là một hack xấu xí sau khi tất cả, nó có khả năng 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, bởi vì các lớp JCE riêng được obfuscated ở đó. Obfuscation không thay đổi từ phiên bản lên phiên bản mặc dù, vì vậy nó vẫn còn về mặt kỹ thuật có thể để hỗ trợ Java 6.

+23

Giải pháp phản chiếu có thể vi phạm [Thỏa thuận cấp phép Java] (http://www.oracle.com/technetwork/java/javase/terms/license/index.html): "F. GIỚI HẠN CÔNG NGHỆ JAVA. Bạn không thể .. Thay đổi hành vi của ... các lớp, các giao diện, hoặc các gói con dưới bất kỳ cách nào được xác định là 'java', 'javax', 'mặt trời', 'oracle' hoặc quy ước tương tự ... " –

+13

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

+1

"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" - Tôi không phải là chuyên gia, nhưng hiểu biết của tôi là hợp pháp để phân phối/gói một jre với chương trình của bạn, bao gồm các tệp chính sách JCE đã cung cấp cho bạn tuân thủ các điều khoản cấp phép. – peabody

21

Dưới đâ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) { 
    } 
} 
+2

Tùy chọn tuyệt vời để kiểm tra đơn vị. Cảm ơn! – cjungel

+0

Đâ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 đăng trên blog được đề 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 không? Tôi không chắc chắn xin vui lòng giúp tôi hiểu đó là tác động. – Dish

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 có sẵn, sử dụng và thông báo cho người dùng về những gì đang diễn ra. Một cái gì đó nói rằng ứng dụng của bạn đang rơi trở lại 128 bit phím do các tập tin chính sách không được cài đặt, ví dụ. 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.

12

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

Có vẻ như nó quay lại hoặc (a) sử dụng Lâu đài Bouncy, hoặc (b) cài đặt các tệp chính sách JCE.

+6

Bạn luôn có thể sử dụng nhiều phản ánh hơn http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection –

+0

Có, giải pháp @ M.Dudley sẽ vẫn hoạt động cho 'isRestricted' lĩnh vực, bởi vì nó sẽ chăm sóc của một sự bổ sung có thể có của một sửa đổi "cuối cùng". – MPelletier

+0

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 mật mã". Tóm lại: xóa "#" khỏi dòng "# crypto.policy = unlimited" trong "lib \ security \ java.security": http://www.oracle.com/technetwork/java/javase/8u151-relnotes- 3850493.html – hemisphire

55

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

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

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

  • Java 9: ​​Bất kỳ phát hành chính thức!
  • Java 8u161 hay muộn (Có tại)
  • Java 7u171 hay muộn (Chỉ có sẵn thông qua 'Hỗ trợ Oracle của tôi')
  • Java 6u181 hay muộn (Chỉ có sẵn thông qua 'Hỗ trợ Oracle của tôi')

Lưu ý rằng nếu vì một lý do lẻ các hành vi cũ là cần thiết trong Java 9, nó có thể được thiết lập sử dụng:

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

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

+3

Liên kết có liên quan: https://bugs.openjdk.java.net/browse/JDK-8170157 – ntoskrnl

+0

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

2

Dưới đây là một phiên bản cập nhật của ntoskrnl câu trả lời. Nó cũng có chức năng để loại bỏ công cụ sửa đổi cuối cùng như Arjan được đề cập trong phần 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")); 
} 
+0

Nó hoạt động độc đáo, nhưng dòng '((Bản đồ ) perms.get (defaultPolicy)). clear();' tạo ra lỗi trình biên dịch. Nhận xét dường như không ảnh hưởng đến chức năng của nó. Dòng này có cần thiết không? –

0

Đây là một phiên bản sửa đổi của mã @ ntoskrnl của tính năng isRestrictedCryptography kiểm tra bởi actual Cipher.getMaxAllowedKeyLength, slf4j khai thác gỗ 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 lại một cách chính xác mangling với sự phản ánh 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; 

// https://stackoverflow.com/questions/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 
    } 
} 
Các vấn đề liên quan