Bagaimana cara menghindari memasang file kebijakan JCE "Kekuatan Tidak Terbatas" ketika menggunakan aplikasi?

169

Saya memiliki aplikasi yang menggunakan enkripsi AES 256-bit yang tidak didukung oleh Java di luar kotak. Saya tahu untuk mendapatkan ini berfungsi dengan benar, saya menginstal stoples kekuatan tidak terbatas JCE di folder keamanan. Ini bagus untuk saya sebagai pengembang, saya dapat menginstalnya.

Pertanyaan saya adalah karena aplikasi ini akan didistribusikan, pengguna akhir kemungkinan besar tidak akan menginstal file kebijakan ini. Memiliki pengguna akhir mengunduh ini hanya untuk membuat fungsi aplikasi bukan solusi yang menarik.

Apakah ada cara untuk membuat aplikasi saya berjalan tanpa menimpa file pada mesin pengguna akhir? Perangkat lunak pihak ketiga yang dapat menanganinya tanpa menginstal file kebijakan? Atau cara hanya merujuk file kebijakan ini dari dalam JAR?

Duncan Jones
sumber
11
Saya menduga niat Sun / Oracle adalah bahwa klien akan menggunakan sandi yang kurang aman sehingga NSA dapat mengintip koneksi. Saya tidak bercanda atau paranoid, tetapi kriptografi diperlakukan sebagai senjata dan ada larangan ekspor untuk berbagi enkripsi .
Kereta luncur

Jawaban:

175

Ada beberapa solusi yang biasa dikutip untuk masalah ini. Sayangnya, keduanya tidak sepenuhnya memuaskan:

  • Instal file kebijakan kekuatan tidak terbatas . Meskipun ini mungkin solusi yang tepat untuk workstation pengembangan Anda, dengan cepat menjadi masalah besar (jika bukan penghalang jalan) untuk meminta pengguna non-teknis menginstal file di setiap komputer. Tidak ada cara untuk mendistribusikan file dengan program Anda; mereka harus diinstal di direktori JRE (yang bahkan mungkin hanya baca karena izin).
  • Lewati API JCE dan gunakan perpustakaan kriptografi lain seperti Bouncy Castle . Pendekatan ini membutuhkan perpustakaan 1MB tambahan, yang mungkin menjadi beban yang signifikan tergantung pada aplikasi. Rasanya konyol menggandakan fungsi yang termasuk dalam perpustakaan standar. Jelas, API juga sangat berbeda dari antarmuka JCE yang biasa. (BC memang mengimplementasikan penyedia JCE, tetapi itu tidak membantu karena pembatasan kekuatan utama diterapkan sebelum menyerahkan implementasi.) Solusi ini juga tidak akan membiarkan Anda menggunakan suar suites 256-bit TLS (SSL), karena pustaka TLS standar memanggil JCE secara internal untuk menentukan batasan apa pun.

Tapi kemudian ada refleksi. Adakah yang tidak bisa Anda lakukan menggunakan refleksi?

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

Cukup panggil removeCryptographyRestrictions()dari penginisialisasi statis atau semacamnya sebelum melakukan operasi kriptografis apa pun.

Bagian JceSecurity.isRestricted = falseinilah yang diperlukan untuk menggunakan sandi 256-bit secara langsung; Namun, tanpa dua operasi lainnya, Cipher.getMaxAllowedKeyLength()masih akan tetap melaporkan 128, dan 256-bit TLS cipher suites tidak akan berfungsi.

Kode ini berfungsi pada Oracle Java 7 dan 8, dan secara otomatis melewatkan proses pada Java 9 dan OpenJDK di tempat yang tidak diperlukan. Bagaimanapun, menjadi peretasan yang buruk, sepertinya tidak bekerja pada VM vendor lain.

Ini juga tidak berfungsi pada Oracle Java 6, karena kelas-kelas JCE pribadi dikaburkan di sana. Kebingungan tidak berubah dari versi ke versi, jadi masih memungkinkan secara teknis untuk mendukung Java 6.

ntoskrnl
sumber
23
Solusi refleksi dapat melanggar Perjanjian Lisensi Java : "F. PEMBATASAN TEKNOLOGI JAWA. Anda tidak boleh ... mengubah perilaku ... kelas, antarmuka, atau sub paket yang dengan cara apa pun diidentifikasi sebagai 'java', 'javax' , 'sun', 'oracle' atau konvensi serupa ... "
M. Dudley
14
@ M.Dudley Bisa jadi. Tanyakan kepada pengacara sebelum mengirim produk yang berisi kode ini jika itu menyangkut Anda.
ntoskrnl
3
@peabody Termasuk JRE 100MB dengan program Anda tentu merupakan opsi dalam beberapa kasus. Tetapi jika tidak, pengguna masih harus menginstal file kebijakan secara manual, bahkan jika Anda memasukkannya ke dalam program Anda (karena berbagai alasan seperti izin file). Dalam pengalaman saya, banyak pengguna yang tidak mampu melakukannya.
ntoskrnl
8
Sepertinya solusi refleksi berhenti bekerja di 1.8.0_112. Ini bekerja di 1.8.0_111, tetapi tidak 112.
John L
3
@ JohnL Saya menggunakan ini dalam aplikasi. Setelah mengalami masalah dengan finalbidang di 8u111, saya memodifikasinya sehingga dapat mengubah bidang terakhir, mengikuti jawaban ini . Hasilnya hampir sama dengan versi baru ntoskrnl, kecuali saya tidak menyatakannya modifiersFieldsebagai final. Salah satu pengguna saya melaporkan bahwa ia berfungsi di 8u112 juga.
Arjan
87

Ini sekarang tidak lagi diperlukan untuk Java 9 , juga untuk rilis Java 6, 7, atau 8. Terakhir! Akhirnya! :)

Per JDK-8170157 , kebijakan kriptografi tak terbatas sekarang diaktifkan secara default.

Versi spesifik dari masalah JIRA:

  • Java 9 (10, 11, dll.): Setiap rilis resmi!
  • Java 8u161 atau lebih baru (Tersedia sekarang )
  • Java 7u171 atau lebih baru (Hanya tersedia melalui 'Dukungan Oracle Saya')
  • Java 6u181 atau lebih baru (Hanya tersedia melalui 'Dukungan Oracle Saya')

Perhatikan bahwa jika karena alasan aneh perilaku lama diperlukan di Java 9, itu dapat diatur menggunakan:

Security.setProperty("crypto.policy", "limited");
cranphin
sumber
4
Bahkan, kebijakan ini adalah default, jadi tidak ada tindakan yang diperlukan di Java 9!
ntoskrnl
Pada 2018/01/14 (Oracle JDK terbaru adalah 8u151 / 152) ini masih belum diaktifkan secara default di Java 8, lebih dari setahun setelah jawaban ini ditulis ... Namun menurut java.com/en/jre -jdk-cryptoroadmap.html ini dimaksudkan untuk GA pada 2018/01/16
Alex
Dalam kasus saya, dan bagi saya untuk mendapatkan Tanda A di situs ini: ssllabs.com/ssltest ... Saya harus mengaturnya seperti ini: Security.setProperty ("crypto.policy", "unlimited"); lalu ... atur server.ssl.ciphers di application.properties saya dengan algoritma berbasis 256 yang ditunjukkan dalam artikel ini -> melemahdh.org/sysadmin.html
Artanis Zeratul
Juga relevan untuk OpenJDK 8-Instalasi. Lihat: stackoverlow-Article: Apakah kebijakan JCE dibundel dengan openjdk 8?
leole
22

Berikut ini solusinya: http://middlesphere-1.blogspot.ru/2014/06/ini-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) {
    }
}
mike
sumber
Ini adalah solusi yang sama dengan saya, kecuali tanpa bagian "defaultPolicy". Posting blog tertanggal setelah jawaban saya.
ntoskrnl
1
Tetapi apakah ini hal yang benar untuk dilakukan? Secara real time dapatkah kode ini menantang keamanan aplikasi? Saya tidak yakin tolong bantu saya memahami dampaknya.
Hidangan
1
Saya mendapatkan kesalahan ini setelah menjalankan ini:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
Andy
3
Pada Java 8 build 111, solusi ini tidak akan cukup, karena isRestrictedbidang telah menjadi final ( bugs.openjdk.java.net/browse/JDK-8149417 ). @ ntoskrnl menjawab mengurus kemungkinan penyertaan pengubah "final". Komentar @ M.Dudley tentang Perjanjian Lisensi Java masih berlaku juga.
MPelletier
13

Pada JDK 8u102, solusi yang diposting mengandalkan refleksi tidak akan lagi berfungsi: bidang yang ditetapkan solusi ini sekarang final( https://bugs.openjdk.java.net/browse/JDK-8149417 ).

Sepertinya itu kembali ke (a) menggunakan Bouncy Castle, atau (b) menginstal file kebijakan JCE.

Sam Roberton
sumber
7
Anda selalu dapat menggunakan lebih banyak refleksi stackoverflow.com/questions/3301635/…
Universal Electricity
Ya, solusi @ M.Dudley masih akan bekerja untuk isRestrictedbidang ini, karena menangani kemungkinan penambahan pengubah "final".
MPelletier
1
Rilis baru JDK 8u151 memiliki "Properti Keamanan Baru untuk mengontrol kebijakan kripto". Intinya: hapus "#" dari baris "# crypto.policy = tidak terbatas" di "lib \ security \ java.security": oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
hemisphire
8

Untuk pustaka kriptografi alternatif, lihat Bouncy Castle . Ini memiliki AES dan banyak fungsi tambahan. Ini perpustakaan open source liberal. Anda harus menggunakan API Castle Bouncy yang ringan dan eksklusif agar ini berfungsi.

Maarten Bodewes
sumber
19
Mereka adalah penyedia crypto yang hebat, tetapi masih membutuhkan kekuatan file JCE yang tidak terbatas untuk dapat bekerja dengan kunci besar.
John Meagher
16
Jika Anda menggunakan Bouncy Castle API secara langsung, Anda tidak memerlukan file kekuatan yang tidak terbatas.
laz
4

Anda bisa menggunakan metode

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

untuk menguji panjang kunci yang tersedia, gunakan itu dan beri tahu pengguna tentang apa yang sedang terjadi. Sesuatu yang menyatakan bahwa aplikasi Anda kembali ke kunci 128 bit karena file kebijakan tidak diinstal, misalnya. Pengguna yang sadar akan keamanan akan menginstal file kebijakan, yang lain akan terus menggunakan kunci yang lebih lemah.

Christian Schulte
sumber
3

Untuk aplikasi kami, kami memiliki arsitektur server klien dan kami hanya membolehkan mendekripsi / mengenkripsi data di tingkat server. Karenanya file JCE hanya diperlukan di sana.

Kami memiliki masalah lain di mana kami perlu memperbarui toples keamanan pada mesin klien, melalui JNLP, itu menimpa perpustakaan di ${java.home}/lib/security/dan JVM saat dijalankan pertama kali.

Itu membuatnya bekerja.

Mohamed Mansour
sumber
2

Inilah versi terbaru dari jawaban ntoskrnl . Ini juga berisi fungsi untuk menghapus pengubah akhir seperti Arjan yang disebutkan dalam komentar.

Versi ini berfungsi dengan JRE 8u111 atau lebih baru.

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"));
}
di-xoning
sumber
Ini berfungsi dengan baik, tetapi baris ((Map<?, ?>) perms.get(defaultPolicy)).clear();menghasilkan kesalahan kompiler. Mengomentari sepertinya tidak memengaruhi fungsinya. Apakah garis ini perlu?
Andreas Unterweger
2

Berikut ini adalah versi modifikasi dari kode @ ntoskrnl yang menampilkan isRestrictedCryptographycheck by aktualCipher.getMaxAllowedKeyLength , slf4j logging, dan dukungan inisialisasi singleton dari aplikasi bootstrap seperti ini:

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

Kode ini akan berhenti mangling dengan refleksi ketika kebijakan tak terbatas tersedia secara default di Java 8u162 seperti diprediksi oleh jawaban @ 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
    }
}
Vadzim
sumber
-1

Selama instalasi program Anda, hanya meminta pengguna dan memiliki skrip DOS Batch atau skrip Bash shell unduh dan salin JCE ke lokasi sistem yang tepat.

Saya dulu harus melakukan ini untuk layanan server web dan bukannya installer resmi, saya hanya menyediakan skrip untuk mengatur aplikasi sebelum pengguna bisa menjalankannya. Anda dapat membuat aplikasi tidak dapat dijalankan hingga mereka menjalankan skrip pengaturan. Anda juga bisa membuat aplikasi mengeluh bahwa JCE hilang dan kemudian meminta untuk mengunduh dan memulai ulang aplikasi?

Djangofan
sumber
7
"membuat aplikasi saya berjalan tanpa menimpa file pada mesin pengguna akhir"
erickson
Saya melakukan pengeditan lengkap jawaban saya karena jawaban awal saya salah.
djangofan